diff --git a/.config/1espt/PipelineAutobaseliningConfig.yml b/.config/1espt/PipelineAutobaseliningConfig.yml
new file mode 100644
index 000000000..409276a94
--- /dev/null
+++ b/.config/1espt/PipelineAutobaseliningConfig.yml
@@ -0,0 +1,46 @@
+## DO NOT MODIFY THIS FILE MANUALLY. This is part of auto-baselining from 1ES Pipeline Templates. Go to [https://aka.ms/1espt-autobaselining] for more details.
+
+pipelines:
+ 108463:
+ retail:
+ source:
+ credscan:
+ lastModifiedDate: 2024-03-04
+ eslint:
+ lastModifiedDate: 2024-03-04
+ psscriptanalyzer:
+ lastModifiedDate: 2024-03-04
+ armory:
+ lastModifiedDate: 2024-03-04
+ 108966:
+ retail:
+ source:
+ credscan:
+ lastModifiedDate: 2024-03-08
+ eslint:
+ lastModifiedDate: 2024-03-08
+ armory:
+ lastModifiedDate: 2024-03-08
+ binary:
+ credscan:
+ lastModifiedDate: 2024-03-08
+ binskim:
+ lastModifiedDate: 2024-03-08
+ 108223:
+ retail:
+ source:
+ credscan:
+ lastModifiedDate: 2024-03-11
+ eslint:
+ lastModifiedDate: 2024-03-11
+ psscriptanalyzer:
+ lastModifiedDate: 2024-03-11
+ armory:
+ lastModifiedDate: 2024-03-11
+ binary:
+ credscan:
+ lastModifiedDate: 2024-03-11
+ binskim:
+ lastModifiedDate: 2024-03-11
+ spotbugs:
+ lastModifiedDate: 2024-03-11
diff --git a/.config/guardian/.gdnbaselines b/.config/guardian/.gdnbaselines
new file mode 100644
index 000000000..c1a8a115f
--- /dev/null
+++ b/.config/guardian/.gdnbaselines
@@ -0,0 +1,16 @@
+{
+ "hydrated": false,
+ "properties": {
+ "helpUri": "https://eng.ms/docs/microsoft-security/security/azure-security/cloudai-security-fundamentals-engineering/security-integration/guardian-wiki/microsoft-guardian/general/baselines",
+ "hydrationStatus": "This file does not contain identifying data. It is safe to check into your repo. To hydrate this file with identifying data, run `guardian hydrate --help` and follow the guidance."
+ },
+ "version": "1.0.0",
+ "baselines": {
+ "default": {
+ "name": "default",
+ "createdDate": "2023-11-11 03:59:35Z",
+ "lastUpdatedDate": "2023-11-11 03:59:35Z"
+ }
+ },
+ "results": {}
+}
\ No newline at end of file
diff --git a/.config/tsaoptions.json b/.config/tsaoptions.json
new file mode 100644
index 000000000..4bb48b58b
--- /dev/null
+++ b/.config/tsaoptions.json
@@ -0,0 +1,3 @@
+{
+ "projectName": "os"
+}
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index fa49c69f2..000000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,25 +0,0 @@
----
-name: Bug report
-about: File a bug report
-title: ''
-labels: bug
-assignees: ''
-
----
-
-**Describe the bug**
-
-
-**To Reproduce**
-
-1. ...
-2. ...
-
-**Expected behavior**
-
-
-**Version Info**
-
-
-**Additional context**
-
diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml
new file mode 100644
index 000000000..a94b57637
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.yml
@@ -0,0 +1,48 @@
+name: Bug Report
+description: File a bug report to help improve CsWinRT
+title: 'Bug: '
+type: Bug
+body:
+
+ # Description
+ - type: textarea
+ attributes:
+ label: Description
+ description: Please enter a short, clear description of the bug.
+ validations:
+ required: true
+
+ # Step To Reproduce
+ - type: textarea
+ attributes:
+ label: Steps To Reproduce
+ description: Please provide any required setup and steps to reproduce the behavior.
+ placeholder: |
+ 1.
+ 2.
+ validations:
+ required: true
+
+ # Expected Behavior
+ - type: textarea
+ attributes:
+ label: Expected Behavior
+ description: Please provide a description of what you expected to happen.
+ validations:
+ required: true
+
+ # Version Info
+ - type: textarea
+ attributes:
+ label: Version Info
+ description: Specify version of CsWinRT NuGet, .NET SDK, Windows SDK, or other relevant info.
+ validations:
+ required: true
+
+ # Additional Context
+ - type: textarea
+ attributes:
+ label: Additional Context
+ description: 'Enter any other applicable info here'
+ validations:
+ required: false
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
deleted file mode 100644
index 09011a0b1..000000000
--- a/.github/ISSUE_TEMPLATE/feature_request.md
+++ /dev/null
@@ -1,34 +0,0 @@
----
-name: Feature request
-about: Suggest a new feature or idea
-title: ''
-labels: enhancement
-assignees: ''
-
----
-
-
-
-# Proposal: [your title here]
-
-
-## Summary
-
-
-## Rationale
-
-* {First reason for why we should consider this proposal}
-* {Second reason for why we should consider this proposal}
-* {etc}
-
-## Important Notes
-
-
-## Open Questions
-
diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml
new file mode 100644
index 000000000..576ae3a67
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.yml
@@ -0,0 +1,45 @@
+name: Feature request
+description: Suggest a new feature or an idea for CsWinRT.
+title: 'Proposal: '
+type: Feature
+body:
+
+ # Summary
+ - type: textarea
+ attributes:
+ label: Summary
+ description: Include 1-2 sentences summarizing your feature/proposal
+ validations:
+ required: true
+
+ # Rationale
+ - type: textarea
+ attributes:
+ label: Rationale
+ description: Describe why we should consider this proposal with a couple of reasons.
+ placeholder: |
+ - The first reason
+ - The second reason
+ - etc...
+ validations:
+ required: true
+
+ # Important Notes
+ - type: textarea
+ attributes:
+ label: Important Notes
+ description: Please include any other important details.
+ placeholder: |
+ - Usage examples
+ - Implementation details
+ - etc...
+ validations:
+ required: false
+
+ # Open Questions
+ - type: textarea
+ attributes:
+ label: Open Questions
+ description: Please list any open issues that you think still need to be addressed.
+ validations:
+ required: false
diff --git a/.gitignore b/.gitignore
index 8e8bd3515..c5a936e9b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,6 +4,7 @@
*_i.c
*_p.c
*.c
+!nuget/sources/*.c
*.winmd
*test*.xml
obj
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 89cedc610..27d3f00cc 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -6,17 +6,16 @@ Below is our guidance for how to build the repo, report issues, propose new feat
C#/WinRT currently requires the following packages, or newer, to build:
-- [Visual Studio 17.0](https://visualstudio.microsoft.com/downloads/)
-- [.NET 6.0 SDK](https://dotnet.microsoft.com/download/dotnet/6.0)
-- [.NET Core 3.1 SDK](https://dotnet.microsoft.com/download/dotnet-core/3.1)
-- [nuget.exe 5.8.0-preview.3](https://www.nuget.org/downloads)
-- Microsoft.WinUI 3.0.0-preview4.210210.4
+- [Visual Studio 17.0](https://visualstudio.microsoft.com/downloads/)
+- [.NET 8.0 SDK](https://dotnet.microsoft.com/download/dotnet/8.0)
+- [nuget.exe 5.9](https://www.nuget.org/downloads)
+- Microsoft.WindowsAppSDK.Foundation 1.8.250831001
The [`build.cmd`](src/build.cmd) script takes care of all related configuration steps and is the simplest way to get started building C#/WinRT. It installs prerequisites such as `nuget.exe` and the .NET 6 SDK, configures the environment to use .NET 6 (creating a `global.json` if necessary), builds the compiler, and builds and executes the unit tests. To build C#/WinRT, follow these steps:
- Open a Visual Studio Developer command prompt pointing at the repo.
- Run `src\build.cmd`.
-- To launch the project in Visual Studio, run `devenv cswinrt.sln` from the same command prompt. This will inherit the necessary environment.
+- To launch the project in Visual Studio, run `devenv src\cswinrt.sln` from the same command prompt. This will inherit the necessary environment.
**Note:** By default, the projects for various [Projections](src/Projections) only generate source files for Release configurations, where `cswinrt.exe` can execute in seconds. To generate sources for the [Projections](src/Projections) projects on Debug configurations, set the project property `GenerateTestProjection` to `true`. This configuration permits a faster inner loop in Visual Studio. In either case, existing projection sources under the "Generated Files" folder will still be compiled into the projection assembly.
diff --git a/README.md b/README.md
index 3636abd4f..a34d5c881 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-[](https://dev.azure.com/microsoft/Dart/_build/latest?definitionId=76961&branchName=master)
+[](https://dev.azure.com/microsoft/Dart/_build/latest?definitionId=112455&branchName=master)
# The C#/WinRT Language Projection
diff --git a/build/AzurePipelineTemplates/CsWinRT-Benchmarks-Steps.yml b/build/AzurePipelineTemplates/CsWinRT-Benchmarks-Steps.yml
index b6d3913d6..5197a41c1 100644
--- a/build/AzurePipelineTemplates/CsWinRT-Benchmarks-Steps.yml
+++ b/build/AzurePipelineTemplates/CsWinRT-Benchmarks-Steps.yml
@@ -10,28 +10,35 @@ steps:
script: get_testwinrt.cmd
workingDirectory: $(Build.SourcesDirectory)\src
-# Use .NET Core SDK 3.1
+# Use .NET Core runtme 3.1
- task: UseDotNet@2
- displayName: Use .NET Core SDK 3.1
+ displayName: Use .NET Core runtime 3.1
inputs:
+ packageType: runtime
version: 3.1.x
- installationPath: C:\Users\VssAdministrator\AppData\Local\Microsoft\dotnet\
- performMultiLevelLookup: true
+ installationPath: $(LocalAppData)\Microsoft\dotnet\
-# Install .NET 6 SDK
-- task: PowerShell@2
- displayName: Install .NET 6 SDK
+# Use .NET Core runtime 6.0
+- task: UseDotNet@2
+ displayName: Use .NET Core runtime 6.0
inputs:
- targetType: inline
- failOnStderr: true
- script: |
- Write-Host ##vso[task.setvariable variable=PATH;]${env:LocalAppData}\Microsoft\dotnet;${env:PATH};
-
- [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;
-
- dotnet new globaljson --sdk-version "$($env:NET6_SDK_VERSION)"
-
- &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version "$($env:NET6_SDK_VERSION)" -Architecture "x64" -AzureFeed "$($env:NET5_SDK_FEED)"
+ packageType: runtime
+ version: 6.x
+ installationPath: $(LocalAppData)\Microsoft\dotnet\
+
+- task: UseDotNet@2
+ displayName: Use .NET Core SDK 8.0
+ inputs:
+ packageType: sdk
+ version: 8.x
+ installationPath: $(LocalAppData)\Microsoft\dotnet\
+
+- task: UseDotNet@2
+ displayName: Use .NET Core SDK 9.0
+ inputs:
+ packageType: sdk
+ version: 9.x
+ installationPath: $(LocalAppData)\Microsoft\dotnet\
# Verify .NET SDK
- task: CmdLine@2
@@ -59,20 +66,3 @@ steps:
# Component Detection
- task: ComponentGovernanceComponentDetection@0
displayName: Component Detection
-
-# Stage Benchmark Results
-- task: CopyFiles@2
- displayName: Stage Benchmark Results
- condition: always()
- inputs:
- SourceFolder: $(Build.SourcesDirectory)\src\BenchmarkDotNet.Artifacts\results\
- Contents: '*'
- TargetFolder: $(Build.ArtifactStagingDirectory)\benchmarks
-
-# Publish Results
-- task: PublishBuildArtifacts@1
- displayName: Publish Results
- condition: always()
- inputs:
- PathtoPublish: $(Build.ArtifactStagingDirectory)\benchmarks
- ArtifactName: benchmarks
diff --git a/build/AzurePipelineTemplates/CsWinRT-Build-Steps.yml b/build/AzurePipelineTemplates/CsWinRT-Build-Steps.yml
index 0cb69be88..b0db10749 100644
--- a/build/AzurePipelineTemplates/CsWinRT-Build-Steps.yml
+++ b/build/AzurePipelineTemplates/CsWinRT-Build-Steps.yml
@@ -1,431 +1,373 @@
+parameters:
+ - name: "BuildConfiguration"
+ type: string
+ - name: "BuildPlatform"
+ type: string
+ - name: SetupForBuildOnly
+ type: boolean
+ default: false
+
steps:
-# Clone TestWinRT
-- task: CmdLine@2
- displayName: Clone TestWinRT
- enabled: false
- inputs:
- script: get_testwinrt.cmd
- workingDirectory: $(Build.SourcesDirectory)
-
-# Download procdump
-- task: PowerShell@2
- displayName: Download procdump
- enabled: false
- inputs:
- targetType: inline
- script: |
- mkdir $env:Agent_TempDirectory\procdump
-
- Invoke-WebRequest -Uri https://download.sysinternals.com/files/Procdump.zip -OutFile $env:Agent_TempDirectory\procdump\Procdump.zip
+ # Setup remote for source link to pick up
+ - task: PowerShell@2
+ condition: eq(variables['AdoMirrorBuild'], 'true')
+ inputs:
+ targetType: 'inline'
+ script: |
+ git remote add github "https://github.com/microsoft/cswinrt.git"
+ workingDirectory: $(Build.SourcesDirectory)
+
+ # Download procdump
+ - task: PowerShell@2
+ displayName: Download procdump
+ enabled: false
+ inputs:
+ targetType: inline
+ script: |
+ mkdir $env:Agent_TempDirectory\procdump
- Expand-Archive -Path $env:Agent_TempDirectory\procdump\Procdump.zip $env:Agent_TempDirectory\procdump\
-
- set PROCDUMP_PATH=$env:Agent_TempDirectory\procdump\
-
- Write-Host ##vso[task.setvariable variable=PATH;]${env:Agent_TempDirectory}\procdump;${env:PATH};
-
-# Use .NET Core SDK 3.1
-- task: UseDotNet@2
- displayName: Use .NET Core SDK 3.1
- inputs:
- version: 3.1.x
- installationPath: C:\Users\VssAdministrator\AppData\Local\Microsoft\dotnet\
- performMultiLevelLookup: true
-
-# Install .NET 6 SDK
-- task: PowerShell@2
- displayName: Install .NET 6 SDK
- inputs:
- targetType: inline
- failOnStderr: true
- script: |
- Write-Host ##vso[task.setvariable variable=PATH;]${env:LocalAppData}\Microsoft\dotnet;${env:PATH};
-
- [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;
-
- dotnet new globaljson --sdk-version "$($env:NET6_SDK_VERSION)"
-
- &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) -Version "$($env:NET6_SDK_VERSION)" -Architecture "x64" -AzureFeed "$($env:NET5_SDK_FEED)"
-
-# Verify .NET SDK
-- task: CmdLine@2
- displayName: Verify .NET SDK
- inputs:
- workingDirectory: $(Build.SourcesDirectory)
- script: |
- where dotnet
- dotnet --info
-
-# Parse Version
-- task: CmdLine@2
- displayName: Parse Version
- inputs:
- workingDirectory: $(Build.SourcesDirectory)
- script: |
- rem Parse the build-generated Build.BuildNumber into components that
- rem can be recombined for version resources, nuget packages, etc.
- @echo off
-
- rem Encode the build date/rev into a 16 bit value for resource versions
- if "$(PrereleaseVersion)"=="" (
- set RevisionBase=30000
- ) else (
- set RevisionBase=0
- )
-
- for /f "tokens=4,5 delims=." %%i in ("$(Build.BuildNumber)") do set BuildMetadata=%%i.%%j & set /a BuildRevision=%RevisionBase%+(((%%i/10000)-20)*366+((%%i)%%10000)/100*31+((%%i)%%100))*10+%%j
-
- set VersionNumber=$(MajorVersion).$(MinorVersion).$(PatchVersion).%BuildRevision%
-
- if "$(PrereleaseVersion)"=="" (
- set NugetVersion=$(MajorVersion).$(MinorVersion).$(PatchVersion)
- ) else (
- set NugetVersion=$(Build.BuildNumber)
- )
-
- rem Export generated version numbers back for subsequent tasks
- echo ##vso[task.setvariable variable=BuildMetadata;]%BuildMetadata%
- echo ##vso[task.setvariable variable=BuildRevision;]%BuildRevision%
- echo ##vso[task.setvariable variable=VersionNumber;]%VersionNumber%
- echo ##vso[task.setvariable variable=NugetVersion;]%NugetVersion%
-
-
-# Build Prerelease Targets
-- task: CmdLine@2
- displayName: Build Prerelease Targets
- inputs:
- workingDirectory: $(Build.SourcesDirectory)
- script: |
- if "$(PrereleaseVersion)"=="" goto :eof
-
- set prerelease_targets=nuget\Microsoft.Windows.CsWinRT.Prerelease.targets
- echo ^ > %prerelease_targets%
- echo ^> %prerelease_targets%
- echo Condition=" '$(NetCoreSdkVersion)' ^!= '$($env:NET5_SDK_VERSION)' and '$(Net5SdkVersion)' ^!= '$($env:NET5_SDK_VERSION)' "^> >> %prerelease_targets%
- echo ^ >> %prerelease_targets%
- echo ^ >> %prerelease_targets%
- echo ^ >> %prerelease_targets%
+ Invoke-WebRequest -Uri https://download.sysinternals.com/files/Procdump.zip -OutFile $env:Agent_TempDirectory\procdump\Procdump.zip
-
-# Build Tool
-- task: CmdLine@2
- displayName: Build Tool
- inputs:
- workingDirectory: $(Build.SourcesDirectory)\src
- script: |
- if "%VSCMD_VER%"=="" (
- pushd c:
- call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" >nul 2>&1
- popd
- )
-
- set cswinrt_echo=on
- set cswinrt_build_only=true
- set CIBuildReason=CI
- rem PDBs being copied for each project reference are causing out of disk space issues in the pipeline. Making use of AllowedReferenceRelatedFileExtensions to avoid them being copied.
- set cswinrt_build_params=/bl:$(Build.SourcesDirectory)\cswinrt.binlog /p:CleanIntermediateDirs=true /p:AllowedReferenceRelatedFileExtensions=".xml;.pri;.dll.config;.exe.config" /p:CIBuildReason=CI
- build.cmd $(BuildPlatform) $(BuildConfiguration) $(VersionNumber) $(Build.BuildNumber) $(WinRT.Runtime.AssemblyVersion)
-
-# Build Tool for Embedded Sample
-- task: CmdLine@2
- displayName: Build TestEmbedded Sample
- inputs:
- workingDirectory: $(Build.SourcesDirectory)\src\Samples\TestEmbedded
- script: |
- if "%VSCMD_VER%"=="" (
- pushd c:
- call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" >nul 2>&1
- popd
- )
-
- set echo_build=on
- set only_build=true
- rem PDBs being copied for each project reference are causing out of disk space issues in the pipeline. Making use of AllowedReferenceRelatedFileExtensions to avoid them being copied.
- set embed_build_params=/p:AllowedReferenceRelatedFileExtensions=".xml;.pri;.dll.config;.exe.config"
- build.cmd $(BuildPlatform) $(BuildConfiguration)
-
-
-# Mask BuildConfiguration
-- task: CmdLine@2
- displayName: Mask BuildConfiguration
- enabled: False
- inputs:
- script: |
- @echo off
-
- rem Although BuildConfiguration is PipelineRelease or PipelineDebug, the build outputs still go to Release or Debug
- rem change BuildConfiguration variable so staging succeeds
- rem the alternative would be to add configuration property for Pipeline* to every project in cswinrt.sln
+ Expand-Archive -Path $env:Agent_TempDirectory\procdump\Procdump.zip $env:Agent_TempDirectory\procdump\
+
+ set PROCDUMP_PATH=$env:Agent_TempDirectory\procdump\
+
+ Write-Host ##vso[task.setvariable variable=PATH;]${env:Agent_TempDirectory}\procdump;${env:PATH};
+
+ # Use .NET Core runtme 3.1
+ - task: UseDotNet@2
+ displayName: Use .NET Core runtime 3.1
+ inputs:
+ packageType: runtime
+ version: 3.1.x
+ installationPath: $(LocalAppData)\Microsoft\dotnet\
+
+ - task: UseDotNet@2
+ displayName: Use .NET Core runtime 3.1 x86
+ condition: eq(variables['BuildPlatform'], 'x86')
+ inputs:
+ packageType: runtime
+ version: 3.1.x
+ installationPath: $(LocalAppData)\Microsoft\dotnet\x86\
+ env:
+ PROCESSOR_ARCHITECTURE: x86
+
+ # Use .NET Core runtime 8.0
+ - task: UseDotNet@2
+ displayName: Use .NET Core runtime 8.0
+ inputs:
+ packageType: runtime
+ version: 8.x
+ installationPath: $(LocalAppData)\Microsoft\dotnet\
+
+ - task: UseDotNet@2
+ displayName: Use .NET Core runtime 8.0 x86
+ condition: eq(variables['BuildPlatform'], 'x86')
+ inputs:
+ packageType: runtime
+ version: 8.x
+ installationPath: $(LocalAppData)\Microsoft\dotnet\x86\
+ env:
+ PROCESSOR_ARCHITECTURE: x86
+
+# Install x86 first so that it isn't the first on PATH.
+ - task: UseDotNet@2
+ displayName: Use .NET Core SDK 9.0 x86
+ condition: eq(variables['BuildPlatform'], 'x86')
+ inputs:
+ packageType: sdk
+ version: 9.x
+ installationPath: $(LocalAppData)\Microsoft\dotnet\x86\
+ env:
+ PROCESSOR_ARCHITECTURE: x86
+
+ - task: UseDotNet@2
+ displayName: Use .NET Core SDK 9.0
+ inputs:
+ packageType: sdk
+ version: 9.x
+ installationPath: $(LocalAppData)\Microsoft\dotnet\
+
+# Set Dotnet path for x86. The normal one already gets set above.
+ - task: PowerShell@2
+ displayName: Set DotNet paths for x86
+ condition: eq(variables['BuildPlatform'], 'x86')
+ inputs:
+ targetType: inline
+ script: |
+ Write-Host "##vso[task.setvariable variable=DOTNET_ROOT(x86);]$env:DOTNET_ROOT\x86\"
+
+ # Verify .NET SDK
+ - task: CmdLine@2
+ displayName: Verify .NET SDK
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)
+ script: |
+ where dotnet
+ dotnet --info
+
+ # Parse Version
+ - task: CmdLine@2
+ displayName: Parse Version
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)
+ script: |
+ rem Parse the build-generated Build.BuildNumber into components that
+ rem can be recombined for version resources, nuget packages, etc.
+ @echo off
- if "$(BuildConfiguration)"=="PipelineRelease" (
- set NewBuildConfiguration=Release
- ) else if "$(BuildConfiguration)"=="PipelineDebug" (
- set NewBuildConfiguration=Debug
- )
-
- if "%NewBuildConfiguration%"!="" (
- echo ##vso[task.setvariable variable=BuildConfiguration;]%NewBuildConfiguration%
- )
-
-# Component Detection
-- task: ComponentGovernanceComponentDetection@0
- displayName: Component Detection
-
-# Stage BinLog
-- task: CopyFiles@2
- displayName: Stage BinLog
- condition: always()
- inputs:
- SourceFolder: $(Build.SourcesDirectory)
- Contents: cswinrt.binlog
- TargetFolder: $(Build.ArtifactStagingDirectory)\binlog
-
-# Publish BinLog
-- task: PublishBuildArtifacts@1
- displayName: Publish BinLog
- condition: always()
- inputs:
- PathtoPublish: $(Build.ArtifactStagingDirectory)\binlog
- ArtifactName: $(BuildConfiguration)_$(BuildPlatform)_binlog
-
-# Stage Windows projection
-- task: ArchiveFiles@2
- displayName: Stage Windows projection
- enabled: False
- inputs:
- rootFolderOrFile: $(Build.SourcesDirectory)\Projections\Windows\Generated Files
- includeRootFolder: false
- sevenZipCompression: 5
- archiveFile: $(Build.ArtifactStagingDirectory)\Windows\sources.zip
-
-# Publish Windows projection
-- task: PublishBuildArtifacts@1
- displayName: Publish Windows projection
- enabled: False
- inputs:
- PathtoPublish: $(Build.ArtifactStagingDirectory)\Windows
- ArtifactName: $(BuildConfiguration)_$(BuildPlatform)_Windows
-
-# Stage CsWinRT
-- task: CopyFiles@2
- displayName: Stage CsWinRT
- condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
- inputs:
- SourceFolder: $(Build.SourcesDirectory)\src\_build\$(BuildPlatform)\$(BuildConfiguration)\cswinrt\bin
- Contents: |
- cswinrt.exe
- cswinrt.pdb
- TargetFolder: $(Build.ArtifactStagingDirectory)\native
-
-# Stage WinRT.Interop.winmd
-- task: CopyFiles@2
- displayName: Stage WinRT.Interop.winmd
- condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
- continueOnError: True
- inputs:
- SourceFolder: $(Build.SourcesDirectory)\src\_build\$(BuildPlatform)\$(BuildConfiguration)\cswinrt\bin
- Contents: WinRT.Interop.winmd
- TargetFolder: $(Build.ArtifactStagingDirectory)\native
-
-# Stage WinRT.Host
-- task: CopyFiles@2
- displayName: Stage WinRT.Host
- continueOnError: True
- inputs:
- SourceFolder: $(Build.SourcesDirectory)\src\_build\$(BuildPlatform)\$(BuildConfiguration)\WinRT.Host\bin
- Contents: |
- WinRT.Host.dll
- WinRT.Host.pdb
- WinRT.Host.dll.mui
- TargetFolder: $(Build.ArtifactStagingDirectory)\native
-
-# Stage Unit Test
-- task: CopyFiles@2
- displayName: Stage Unit Test
- condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
- inputs:
- SourceFolder: $(Build.SourcesDirectory)\src\Tests\UnitTest\bin\$(BuildPlatform)\$(BuildConfiguration)\net5.0
- Contents: |
- unittest.dll
- unittest.pdb
- TargetFolder: $(Build.ArtifactStagingDirectory)\native
-
-# Publish Native
-- task: PublishBuildArtifacts@1
- displayName: Publish Native
- inputs:
- PathtoPublish: $(Build.ArtifactStagingDirectory)\native
- ArtifactName: $(BuildConfiguration)_$(BuildPlatform)
-
-# Stage NetStandard 2.0
-- task: CopyFiles@2
- displayName: Stage NetStandard 2.0
- condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
- inputs:
- SourceFolder: $(Build.SourcesDirectory)\src\WinRT.Runtime\bin\$(BuildConfiguration)\netstandard2.0
- Contents: |
- WinRT.Runtime.dll
- WinRT.Runtime.pdb
- TargetFolder: $(Build.ArtifactStagingDirectory)\release_netstandard2.0\
-
-# Stage Source Generator
-- task: CopyFiles@2
- displayName: Stage Source Generator
- condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
- inputs:
- SourceFolder: $(Build.SourcesDirectory)\src\Authoring\WinRT.SourceGenerator\bin\$(BuildConfiguration)\netstandard2.0
- Contents: |
- WinRT.SourceGenerator.dll
- WinRT.SourceGenerator.pdb
- TargetFolder: $(Build.ArtifactStagingDirectory)\release_netstandard2.0\
-
-# Publish NetStandard 2.0
-- task: PublishBuildArtifacts@1
- displayName: Publish NetStandard 2.0
- condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
- inputs:
- PathtoPublish: $(Build.ArtifactStagingDirectory)\release_netstandard2.0
- ArtifactName: netstandard2.0
-
-# Stage Net5.0
-- task: CopyFiles@2
- displayName: Stage Net5.0
- condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
- inputs:
- SourceFolder: $(Build.SourcesDirectory)\src\WinRT.Runtime\bin\$(BuildConfiguration)\net5.0
- Contents: |
- WinRT.Runtime.dll
- WinRT.Runtime.pdb
- TargetFolder: $(Build.ArtifactStagingDirectory)\release_net5.0
-
-# Stage Net6.0
-- task: CopyFiles@2
- displayName: Stage Net6.0
- condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
- inputs:
- SourceFolder: $(Build.SourcesDirectory)\src\WinRT.Runtime\bin\$(BuildConfiguration)\net6.0
- Contents: |
- WinRT.Runtime.dll
- WinRT.Runtime.pdb
- TargetFolder: $(Build.ArtifactStagingDirectory)\release_net6.0
-
-# Stage WinRT.Host.Shim
-- task: CopyFiles@2
- displayName: Stage WinRT.Host.Shim
- condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
- continueOnError: True
- inputs:
- SourceFolder: $(Build.SourcesDirectory)\src\Authoring\WinRT.Host.Shim\bin\$(BuildConfiguration)\net6.0
- Contents: |
- WinRT.Host.Shim.dll
- WinRT.Host.Shim.pdb
- TargetFolder: $(Build.ArtifactStagingDirectory)\release_net6.0
-
-# Stage IID Optimizer
-- task: CopyFiles@2
- displayName: Stage IID Optimizer
- condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
- continueOnError: True
- inputs:
- SourceFolder: $(Build.SourcesDirectory)\src\Perf\IIDOptimizer\bin\$(BuildConfiguration)\net6.0
- Contents: |
- IIDOptimizer.exe
- IIDOptimizer.dll
- IIDOptimizer.deps.json
- IIDOptimizer.runtimeconfig.json
- Mono.Cecil.dll
- Mono.Cecil.Mdb.dll
- Mono.Cecil.Pdb.dll
- Mono.Cecil.Rocks.dll
- System.CommandLine.dll
- cs/System.CommandLine.resources.dll
- de/System.CommandLine.resources.dll
- es/System.CommandLine.resources.dll
- fr/System.CommandLine.resources.dll
- it/System.CommandLine.resources.dll
- ja/System.CommandLine.resources.dll
- ko/System.CommandLine.resources.dll
- pl/System.CommandLine.resources.dll
- pt-BR/System.CommandLine.resources.dll
- ru/System.CommandLine.resources.dll
- tr/System.CommandLine.resources.dll
- zh-Hans/System.CommandLine.resources.dll
- zh-Hant/System.CommandLine.resources.dll
- TargetFolder: $(Build.ArtifactStagingDirectory)\release_net6.0\IIDOptimizer
-
-# Stage WinRT.Runtime ResX
-- task: CopyFiles@2
- displayName: Stage WinRT.Runtime ResX
- condition: succeeded()
- inputs:
- SourceFolder: $(Build.SourcesDirectory)\src\WinRT.Runtime
- Contents: |
- WinRTRuntimeErrorStrings.resx
- TargetFolder: $(Build.ArtifactStagingDirectory)\resx\WinRT.Runtime
-
-# Stage Authoring Diagnostics ResX
-- task: CopyFiles@2
- displayName: Stage Authoring Diagnostics ResX
- condition: succeeded()
- inputs:
- SourceFolder: $(Build.SourcesDirectory)\src\Authoring\WinRT.SourceGenerator
- Contents: |
- CsWinRTDiagnosticStrings.resx
- TargetFolder: $(Build.ArtifactStagingDirectory)\resx\WinRT.SourceGenerator
-
-# Stage CsWinMD
-- task: CopyFiles@2
- displayName: Stage CsWinMD
- condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
- inputs:
- SourceFolder: $(Build.SourcesDirectory)\src\Authoring\cswinmd\bin\$(BuildConfiguration)\net6.0
- Contents: |
- CsWinMD.dll
- CsWinMD.exe
- CsWinMD.pdb
- CsWinMD.deps.json
- CsWinMD.runtimeconfig.json
- Microsoft.CodeAnalysis.CSharp.dll
- Microsoft.CodeAnalysis.dll
- TargetFolder: $(Build.ArtifactStagingDirectory)\release_net6.0\CsWinMD
-
-# Publish ResX
-- task: PublishBuildArtifacts@1
- displayName: Publish ResX files
- condition: succeeded()
- inputs:
- PathtoPublish: $(Build.ArtifactStagingDirectory)\resx
- ArtifactName: ResX
-
-# Publish Net5.0
-- task: PublishBuildArtifacts@1
- displayName: Publish Net5.0
- condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
- inputs:
- PathtoPublish: $(Build.ArtifactStagingDirectory)\release_net5.0
- ArtifactName: net5.0
-
-# Publish Net6.0
-- task: PublishBuildArtifacts@1
- displayName: Publish Net6.0
- condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
- inputs:
- PathtoPublish: $(Build.ArtifactStagingDirectory)\release_net6.0
- ArtifactName: net6.0
-
-# Generate SBOM
-- task: AzureArtifacts.manifest-generator-task.manifest-generator-task.ManifestGeneratorTask@0
- displayName: 'SBOM Generator Task'
- condition: eq(variables['BuildConfiguration'],'Release')
- inputs:
- BuildDropPath: '$(Build.ArtifactStagingDirectory)'
- PackageName: 'Microsoft.Windows.CsWinRT'
- PackageVersion: '$(NugetVersion)'
-
-# Stage SBOM
-- task: PublishBuildArtifacts@1
- displayName: 'Publish SBOM to build artifacts'
- condition: eq(variables['BuildConfiguration'],'Release')
- inputs:
- PathtoPublish: '$(Build.ArtifactStagingDirectory)\_manifest'
- ArtifactName: SBOM_$(BuildPlatform)
+ rem Encode the build date/rev into a 16 bit value for resource versions
+ if "$(PrereleaseVersion)"=="" (
+ set RevisionBase=30000
+ ) else (
+ set RevisionBase=0
+ )
+
+ for /f "tokens=4,5 delims=." %%i in ("$(Build.BuildNumber)") do set BuildMetadata=%%i.%%j & set /a BuildRevision=%RevisionBase%+(((%%i/10000)-20)*366+((%%i)%%10000)/100*31+((%%i)%%100))*10+%%j
+
+ set VersionNumber=$(MajorVersion).$(MinorVersion).$(PatchVersion).%BuildRevision%
+
+ if "$(PrereleaseVersion)"=="" (
+ set NugetVersion=$(MajorVersion).$(MinorVersion).$(PatchVersion)
+ ) else (
+ set NugetVersion=$(Build.BuildNumber)
+ )
+
+ rem Export generated version numbers back for subsequent tasks
+ echo ##vso[task.setvariable variable=BuildMetadata;]%BuildMetadata%
+ echo ##vso[task.setvariable variable=BuildRevision;]%BuildRevision%
+ echo ##vso[task.setvariable variable=VersionNumber;]%VersionNumber%
+ echo ##vso[task.setvariable variable=NugetVersion;]%NugetVersion%
+
+ - task: NuGetAuthenticate@1
+
+ - task: NuGetCommand@2
+ displayName: NuGet restore cswinrt.sln
+ inputs:
+ command: 'restore'
+ restoreSolution: '$(Build.SourcesDirectory)\src\cswinrt.sln'
+ feedsToUse: config
+ nugetConfigPath: NuGet.config
+
+ - task: NuGetCommand@2
+ displayName: NuGet restore testwinrt.sln
+ inputs:
+ command: 'restore'
+ restoreSolution: '$(Build.SourcesDirectory)\src\testwinrt\test.sln'
+ feedsToUse: config
+ nugetConfigPath: NuGet.config
+
+ - ${{ if eq(parameters.SetupForBuildOnly, false) }}:
+
+# PDBs being copied for each project reference are causing out of disk space issues in the pipeline. Making use of AllowedReferenceRelatedFileExtensions to avoid them being copied.
+ - task: VSBuild@1
+ displayName: Build cswinrt.sln
+ inputs:
+ solution: $(Build.SourcesDirectory)\src\cswinrt.sln
+ msbuildArgs: /restore /p:CIBuildReason=CI,VersionNumber=$(VersionNumber),VersionString=$(Build.BuildNumber),AssemblyVersionNumber=$(WinRT.Runtime.AssemblyVersion),GenerateTestProjection=true,AllowedReferenceRelatedFileExtensions=".xml;.pri;.dll.config;.exe.config" /bl:$(Build.SourcesDirectory)\cswinrt.binlog
+ platform: $(BuildPlatform)
+ configuration: $(BuildConfiguration)
+
+ - task: NuGetCommand@2
+ displayName: NuGet restore TestEmbedded.sln
+ inputs:
+ command: 'restore'
+ restoreSolution: '$(Build.SourcesDirectory)\src\Samples\TestEmbedded\TestEmbedded.sln'
+ feedsToUse: config
+ nugetConfigPath: NuGet.config
+
+ - task: VSBuild@1
+ displayName: Build TestEmbedded.sln
+ inputs:
+ solution: $(Build.SourcesDirectory)\src\Samples\TestEmbedded\TestEmbedded.sln
+ msbuildArgs: /m /p:AllowedReferenceRelatedFileExtensions=".xml;.pri;.dll.config;.exe.config" /bl:$(Build.SourcesDirectory)\embeddedsample.binlog
+ platform: $(BuildPlatform)
+ configuration: $(BuildConfiguration)
+
+ # Stage BinLog
+ - task: CopyFiles@2
+ displayName: Stage BinLog
+ condition: failed()
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)
+ Contents: cswinrt.binlog
+ TargetFolder: $(StagingFolder)\binlog
+
+ # Stage Windows projection
+ - task: ArchiveFiles@2
+ displayName: Stage Windows projection
+ condition: and(succeeded(), eq(variables['PublishGeneratedFiles'], 'true'))
+ inputs:
+ rootFolderOrFile: $(Build.SourcesDirectory)\Projections\Windows\Generated Files
+ includeRootFolder: false
+ sevenZipCompression: 5
+ archiveFile: $(StagingFolder)\Windows\sources.zip
+
+ # Stage CsWinRT
+ - task: CopyFiles@2
+ displayName: Stage CsWinRT
+ condition: and(succeeded(), and(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release')))
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\_build\$(BuildPlatform)\$(BuildConfiguration)\cswinrt\bin
+ Contents: |
+ cswinrt.exe
+ cswinrt.pdb
+ TargetFolder: $(StagingFolder)\native
+
+ # Stage WinRT.Interop.winmd
+ - task: CopyFiles@2
+ displayName: Stage WinRT.Interop.winmd
+ condition: and(succeeded(), and(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release')))
+ continueOnError: True
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\_build\$(BuildPlatform)\$(BuildConfiguration)\cswinrt\bin
+ Contents: WinRT.Interop.winmd
+ TargetFolder: $(StagingFolder)\native
+
+ # Stage WinRT.Host
+ - task: CopyFiles@2
+ displayName: Stage WinRT.Host
+ condition: and(succeeded(), eq(variables['BuildConfiguration'], 'release'))
+ continueOnError: True
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\_build\$(BuildPlatform)\$(BuildConfiguration)\WinRT.Host\bin
+ Contents: |
+ WinRT.Host.dll
+ WinRT.Host.pdb
+ WinRT.Host.dll.mui
+ TargetFolder: $(StagingFolder)\native
+
+ # Stage NetStandard 2.0
+ - task: CopyFiles@2
+ displayName: Stage NetStandard 2.0
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\WinRT.Runtime\bin\$(BuildConfiguration)\netstandard2.0
+ Contents: |
+ WinRT.Runtime.dll
+ WinRT.Runtime.pdb
+ TargetFolder: $(StagingFolder)\netstandard2.0\
+
+ # Stage Source Generator (Roslyn 4.8)
+ - task: CopyFiles@2
+ displayName: Stage Source Generator
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\Authoring\WinRT.SourceGenerator.Roslyn4080\bin\$(BuildConfiguration)\netstandard2.0
+ Contents: |
+ WinRT.SourceGenerator.dll
+ WinRT.SourceGenerator.pdb
+ TargetFolder: $(StagingFolder)\netstandard2.0\roslyn4080\
+
+ # Stage Source Generator (Roslyn 4.12)
+ - task: CopyFiles@2
+ displayName: Stage Source Generator
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\Authoring\WinRT.SourceGenerator.Roslyn4120\bin\$(BuildConfiguration)\netstandard2.0
+ Contents: |
+ WinRT.SourceGenerator.dll
+ WinRT.SourceGenerator.pdb
+ TargetFolder: $(StagingFolder)\netstandard2.0\roslyn4120\
+
+ # Stage Net8.0
+ - task: CopyFiles@2
+ displayName: Stage Net8.0
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\WinRT.Runtime\bin\$(BuildConfiguration)\net8.0
+ Contents: |
+ WinRT.Runtime.dll
+ WinRT.Runtime.pdb
+ TargetFolder: $(StagingFolder)\net8.0
+
+ # Stage Net9.0
+ - task: CopyFiles@2
+ displayName: Stage Net9.0
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\WinRT.Runtime\bin\$(BuildConfiguration)\net9.0
+ Contents: |
+ WinRT.Runtime.dll
+ WinRT.Runtime.pdb
+ TargetFolder: $(StagingFolder)\net9.0
+
+ # Stage WinRT.Host.Shim
+ - task: CopyFiles@2
+ displayName: Stage WinRT.Host.Shim
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ continueOnError: True
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\Authoring\WinRT.Host.Shim\bin\$(BuildConfiguration)\net8.0
+ Contents: |
+ WinRT.Host.Shim.dll
+ WinRT.Host.Shim.pdb
+ TargetFolder: $(StagingFolder)\net8.0
+
+ # Stage IID Optimizer
+ - task: CopyFiles@2
+ displayName: Stage IID Optimizer
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ continueOnError: True
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\Perf\IIDOptimizer\bin\$(BuildConfiguration)\net8.0
+ Contents: |
+ IIDOptimizer.exe
+ IIDOptimizer.dll
+ IIDOptimizer.deps.json
+ IIDOptimizer.runtimeconfig.json
+ Mono.Cecil.dll
+ Mono.Cecil.Mdb.dll
+ Mono.Cecil.Pdb.dll
+ Mono.Cecil.Rocks.dll
+ System.CommandLine.dll
+ cs/System.CommandLine.resources.dll
+ de/System.CommandLine.resources.dll
+ es/System.CommandLine.resources.dll
+ fr/System.CommandLine.resources.dll
+ it/System.CommandLine.resources.dll
+ ja/System.CommandLine.resources.dll
+ ko/System.CommandLine.resources.dll
+ pl/System.CommandLine.resources.dll
+ pt-BR/System.CommandLine.resources.dll
+ ru/System.CommandLine.resources.dll
+ tr/System.CommandLine.resources.dll
+ zh-Hans/System.CommandLine.resources.dll
+ zh-Hant/System.CommandLine.resources.dll
+ TargetFolder: $(StagingFolder)\net8.0\IIDOptimizer
+
+ # Stage WinRT.Runtime ResX
+ - task: CopyFiles@2
+ displayName: Stage WinRT.Runtime ResX
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\WinRT.Runtime
+ Contents: |
+ WinRTRuntimeErrorStrings.resx
+ TargetFolder: $(StagingFolder)\resx\WinRT.Runtime
+
+ # Stage Authoring Diagnostics ResX
+ - task: CopyFiles@2
+ displayName: Stage Authoring Diagnostics ResX
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\Authoring\WinRT.SourceGenerator
+ Contents: |
+ CsWinRTDiagnosticStrings.resx
+ TargetFolder: $(StagingFolder)\resx\WinRT.SourceGenerator
+
+ # Stage CsWinMD
+ - task: CopyFiles@2
+ displayName: Stage CsWinMD
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\Authoring\cswinmd\bin\$(BuildConfiguration)\net8.0
+ Contents: |
+ CsWinMD.dll
+ CsWinMD.exe
+ CsWinMD.pdb
+ CsWinMD.deps.json
+ CsWinMD.runtimeconfig.json
+ Microsoft.CodeAnalysis.CSharp.dll
+ Microsoft.CodeAnalysis.dll
+ TargetFolder: $(StagingFolder)\net8.0\CsWinMD
diff --git a/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage-GitHub.yml b/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage-GitHub.yml
new file mode 100644
index 000000000..e63f8f5f3
--- /dev/null
+++ b/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage-GitHub.yml
@@ -0,0 +1,137 @@
+stages:
+- stage: BuildAndTest
+ displayName: Build/Test Stage
+ jobs:
+ - job: BuildAndTest
+ timeoutInMinutes: 270
+ # https://docs.microsoft.com/en-us/azure/devops/pipelines/process/phases?view=azure-devops&tabs=yaml#multi-job-configuration
+ strategy:
+ maxParallel: 10
+ matrix:
+ x64_Debug:
+ BuildPlatform: 'x64'
+ BuildConfiguration: 'debug'
+ x86_Debug:
+ BuildPlatform: 'x86'
+ BuildConfiguration: 'debug'
+ arm64_Debug:
+ BuildPlatform: 'arm64'
+ BuildConfiguration: 'debug'
+ x64_Release:
+ BuildPlatform: 'x64'
+ BuildConfiguration: 'release'
+ x86_Release:
+ BuildPlatform: 'x86'
+ BuildConfiguration: 'release'
+ arm64_Release:
+ BuildPlatform: 'arm64'
+ BuildConfiguration: 'release'
+ variables:
+ ob_outputDirectory: '$(Build.SourcesDirectory)\out'
+ ob_artifactSuffix: $(BuildConfiguration)_$(BuildPlatform)
+ StagingFolder: $(ob_outputDirectory)
+ steps:
+ - checkout: self
+ clean: true
+ persistCredentials: true
+ path: 's'
+ - checkout: TestWinRT
+ path: 's\src\TestWinRT'
+ displayName: "Checkout TestWinRT"
+
+# Build Steps
+ - template: CsWinRT-Build-Steps.yml@self
+ parameters:
+ BuildConfiguration: $(BuildConfiguration)
+ BuildPlatform: $(BuildPlatform)
+
+ - template: CsWinRT-Test-Steps.yml@self
+ parameters:
+ BuildConfiguration: $(BuildConfiguration)
+ BuildPlatform: $(BuildPlatform)
+
+ templateContext:
+ outputs:
+ - output: pipelineArtifact
+ displayName: 'Publish pipeline artifacts'
+ condition: or(failed(), eq(variables['BuildConfiguration'], 'release'))
+ targetPath: $(StagingFolder)
+ artifactName: drop_BuildAndTest_$(ob_artifactSuffix)
+
+ - job: Benchmarks
+ displayName: Run Benchmarks
+ condition: or(eq(variables['_RunBenchmarks'],'true'), eq(variables['Build.Reason'],'Schedule'))
+ dependsOn: []
+ timeoutInMinutes: 120
+ variables:
+ ob_outputDirectory: '$(Build.SourcesDirectory)\out'
+ StagingFolder: $(ob_outputDirectory)
+ steps:
+ - template: CsWinRT-Benchmarks-Steps.yml@self
+
+ templateContext:
+ outputs:
+ - output: pipelineArtifact
+ displayName: Publish Benchmarks Results
+ condition: always()
+ targetPath: $(Build.SourcesDirectory)\src\BenchmarkDotNet.Artifacts\results\
+ artifactName: benchmarks
+
+ # Seperate job to run certain tests
+ # that can't run in the other environment.
+ - job: Tests
+ displayName: Run Tests
+ dependsOn: []
+ timeoutInMinutes: 120
+ strategy:
+ matrix:
+ debug:
+ BuildConfiguration: 'debug'
+ release:
+ BuildConfiguration: 'release'
+ variables:
+ ob_outputDirectory: '$(Build.SourcesDirectory)\out'
+ StagingFolder: $(ob_outputDirectory)
+ BuildPlatform: 'x64'
+ steps:
+ - checkout: self
+ clean: true
+ persistCredentials: true
+ path: 's'
+ - checkout: TestWinRT
+ path: 's\src\TestWinRT'
+ displayName: "Checkout TestWinRT"
+
+# Build Steps
+ - template: CsWinRT-Build-Steps.yml@self
+ parameters:
+ BuildConfiguration: $(BuildConfiguration)
+ BuildPlatform: $(BuildPlatform)
+ SetupForBuildOnly: true
+
+ - task: MSBuild@1
+ displayName: Build Object Lifetime Tests
+ condition: succeeded()
+ inputs:
+ solution: $(Build.SourcesDirectory)\src\Tests\ObjectLifetimeTests\ObjectLifetimeTests.Lifted.csproj
+ msbuildArguments: /restore /p:CIBuildReason=CI,solutiondir=$(Build.SourcesDirectory)\src\,VersionNumber=$(VersionNumber),VersionString=$(Build.BuildNumber),AssemblyVersionNumber=$(WinRT.Runtime.AssemblyVersion),GenerateTestProjection=true,AllowedReferenceRelatedFileExtensions=".xml;.pri;.dll.config;.exe.config"
+ platform: $(BuildPlatform)
+ configuration: $(BuildConfiguration)
+
+# Run Object Lifetime Tests
+ - task: VSTest@3
+ displayName: Run Object Lifetime Tests
+ condition: succeeded()
+ inputs:
+ testAssemblyVer2: Tests\ObjectLifetimeTests\bin\$(BuildPlatform)\$(BuildConfiguration)\net8.0-windows10.0.19041.0\win-$(BuildPlatform)\ObjectLifetimeTests.Lifted.build.appxrecipe
+ searchFolder: $(Build.SourcesDirectory)\src
+
+# Run Source Generator Tests
+ - task: DotNetCoreCLI@2
+ displayName: Run Source Generator Tests
+ condition: succeeded()
+ inputs:
+ command: test
+ projects: 'src/Tests/SourceGeneratorTest/SourceGeneratorTest.csproj'
+ arguments: --diag $(StagingFolder)\unittest\test.log --logger trx;LogFilePath=UNITTEST-$(Build.BuildNumber).trx /nologo /m /p:configuration=$(BuildConfiguration);CIBuildReason=CI;solutiondir=$(Build.SourcesDirectory)\src\;VersionNumber=$(VersionNumber);VersionString=$(Build.BuildNumber);AssemblyVersionNumber=$(WinRT.Runtime.AssemblyVersion) -- RunConfiguration.TreatNoTestsAsError=true
+ testRunTitle: Unit Tests
\ No newline at end of file
diff --git a/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage-OneBranch.yml b/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage-OneBranch.yml
new file mode 100644
index 000000000..2cdfd8e83
--- /dev/null
+++ b/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage-OneBranch.yml
@@ -0,0 +1,193 @@
+jobs:
+ - job:
+ displayName: Build/Test Stage
+ pool:
+ type: windows
+
+ timeoutInMinutes: 270
+ # https://docs.microsoft.com/en-us/azure/devops/pipelines/process/phases?view=azure-devops&tabs=yaml#multi-job-configuration
+
+ strategy:
+ maxParallel: 10
+ matrix:
+ x64_Debug:
+ BuildPlatform: 'x64'
+ BuildConfiguration: 'debug'
+ x86_Debug:
+ BuildPlatform: 'x86'
+ BuildConfiguration: 'debug'
+ arm64_Debug:
+ BuildPlatform: 'arm64'
+ BuildConfiguration: 'debug'
+ x64_Release:
+ BuildPlatform: 'x64'
+ BuildConfiguration: 'release'
+ x86_Release:
+ BuildPlatform: 'x86'
+ BuildConfiguration: 'release'
+ arm64_Release:
+ BuildPlatform: 'arm64'
+ BuildConfiguration: 'release'
+
+ variables:
+ ob_outputDirectory: '$(Build.SourcesDirectory)\out'
+ ob_artifactSuffix: $(BuildConfiguration)_$(BuildPlatform)
+ StagingFolder: $(ob_outputDirectory)
+ ob_sdl_prefast_enabled: true
+ ob_sdl_prefast_runDuring: 'Build'
+ ob_sdl_checkCompliantCompilerWarnings: true
+ ob_symbolsPublishing_enabled: true
+ ob_symbolsPublishing_indexSources: true
+ ob_symbolsPublishing_searchPattern: '**/*.@(pdb|dll)'
+
+ steps:
+ - checkout: TestWinRT
+ path: 's\src\TestWinRT'
+ displayName: "Checkout TestWinRT"
+
+# Build Steps
+ - template: CsWinRT-Build-Steps.yml@self
+ parameters:
+ BuildConfiguration: $(BuildConfiguration)
+ BuildPlatform: $(BuildPlatform)
+
+ - template: CsWinRT-Test-Steps.yml@self
+ parameters:
+ BuildConfiguration: $(BuildConfiguration)
+ BuildPlatform: $(BuildPlatform)
+
+# Signing
+ - task: onebranch.pipeline.signing@1
+ displayName: '🔒 Onebranch Signing for cswinrt binaries'
+ condition: and(succeeded(), eq(variables['BuildConfiguration'], 'release'))
+ inputs:
+ command: sign
+ signing_profile: external_distribution
+ files_to_sign: |
+ native/cswinrt.exe;
+ netstandard2.0/WinRT.Runtime.dll;
+ netstandard2.0/WinRT.Host.Shim.dll;
+ netstandard2.0/roslyn4080/WinRT.SourceGenerator.dll;
+ netstandard2.0/roslyn4120/WinRT.SourceGenerator.dll;
+ net8.0/WinRT.Host.Shim.dll;
+ net8.0/WinRT.Runtime.dll;
+ net9.0/WinRT.Runtime.dll;
+ native/WinRT.Host.dll;
+ net8.0/IIDOptimizer/IIDOptimizer.exe;
+ net8.0/IIDOptimizer/IIDOptimizer.dll;
+ net8.0/CsWinMD/CsWinMD.exe;
+ net8.0/CsWinMD/CsWinMD.dll;
+ search_root: $(StagingFolder)
+
+ - task: onebranch.pipeline.signing@1
+ displayName: '🔒 Onebranch Signing for cswinrt binaries'
+ condition: and(succeeded(), eq(variables['BuildConfiguration'], 'release'))
+ inputs:
+ command: sign
+ cp_code: '400'
+ files_to_sign: |
+ native/WinRT.Interop.winmd;
+ native/WinRT.Host.dll.mui;
+ search_root: $(StagingFolder)
+
+# Signing 3rd Party
+ - task: onebranch.pipeline.signing@1
+ displayName: '🔒 Onebranch Signing for 3rd party binaries'
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildConfiguration'], 'release'))
+ inputs:
+ command: sign
+ signing_profile: 135020002
+ files_to_sign: |
+ net8.0/IIDOptimizer/Mono.Cecil.dll;
+ net8.0/IIDOptimizer/Mono.Cecil.Mdb.dll;
+ net8.0/IIDOptimizer/Mono.Cecil.Pdb.dll;
+ net8.0/IIDOptimizer/Mono.Cecil.Rocks.dll;
+ search_root: $(StagingFolder)
+
+ - job: Benchmarks
+ displayName: Run Benchmarks
+ condition: or(eq(variables['_RunBenchmarks'],'true'), eq(variables['Build.Reason'],'Schedule'))
+ dependsOn: []
+ pool:
+ type: windows
+ isCustom: true
+ name: 'Azure Pipelines'
+ vmImage: 'windows-2022'
+ timeoutInMinutes: 120
+ variables:
+ ob_outputDirectory: '$(Build.SourcesDirectory)\out'
+ StagingFolder: $(ob_outputDirectory)
+ steps:
+ - template: CsWinRT-Benchmarks-Steps.yml@self
+
+# Publish Benchmark Results
+ - task: PublishPipelineArtifact@1
+ displayName: Publish Benchmarks Results
+ condition: always()
+ inputs:
+ targetPath: $(Build.SourcesDirectory)\src\BenchmarkDotNet.Artifacts\results\
+ artifact: drop_BuildAndTest_Benchmarks
+
+ # Seperate job to run certain tests
+ # that can't run in the other environment.
+ - job: Tests
+ displayName: Run Tests
+ dependsOn: []
+ pool:
+ type: windows
+ isCustom: true
+ name: 'Azure Pipelines'
+ vmImage: 'windows-2022'
+ timeoutInMinutes: 120
+ strategy:
+ matrix:
+ debug:
+ BuildConfiguration: 'debug'
+ release:
+ BuildConfiguration: 'release'
+ variables:
+ ob_outputDirectory: '$(Build.SourcesDirectory)\out'
+ StagingFolder: $(ob_outputDirectory)
+ BuildPlatform: 'x64'
+ steps:
+ - checkout: self
+ clean: true
+ persistCredentials: true
+ path: 's'
+ - checkout: TestWinRT
+ path: 's\src\TestWinRT'
+ displayName: "Checkout TestWinRT"
+
+# Build Steps
+ - template: CsWinRT-Build-Steps.yml@self
+ parameters:
+ BuildConfiguration: $(BuildConfiguration)
+ BuildPlatform: $(BuildPlatform)
+ SetupForBuildOnly: true
+
+ - task: MSBuild@1
+ displayName: Build Object Lifetime Tests
+ condition: succeeded()
+ inputs:
+ solution: $(Build.SourcesDirectory)\src\Tests\ObjectLifetimeTests\ObjectLifetimeTests.Lifted.csproj
+ msbuildArguments: /restore /p:CIBuildReason=CI,solutiondir=$(Build.SourcesDirectory)\src\,VersionNumber=$(VersionNumber),VersionString=$(Build.BuildNumber),AssemblyVersionNumber=$(WinRT.Runtime.AssemblyVersion),GenerateTestProjection=true,AllowedReferenceRelatedFileExtensions=".xml;.pri;.dll.config;.exe.config"
+ platform: $(BuildPlatform)
+ configuration: $(BuildConfiguration)
+
+# Run Object Lifetime Tests
+ - task: VSTest@3
+ displayName: Run Object Lifetime Tests
+ condition: succeeded()
+ inputs:
+ testAssemblyVer2: Tests\ObjectLifetimeTests\bin\$(BuildPlatform)\$(BuildConfiguration)\net8.0-windows10.0.19041.0\win-$(BuildPlatform)\ObjectLifetimeTests.Lifted.build.appxrecipe
+ searchFolder: $(Build.SourcesDirectory)\src
+
+# Run Source Generator Tests
+ - task: DotNetCoreCLI@2
+ displayName: Run Source Generator Tests
+ condition: succeeded()
+ inputs:
+ command: test
+ projects: 'src/Tests/SourceGeneratorTest/SourceGeneratorTest.csproj'
+ arguments: --diag $(StagingFolder)\unittest\test.log --logger trx;LogFilePath=UNITTEST-$(Build.BuildNumber).trx /nologo /m /p:configuration=$(BuildConfiguration);CIBuildReason=CI;solutiondir=$(Build.SourcesDirectory)\src\;VersionNumber=$(VersionNumber);VersionString=$(Build.BuildNumber);AssemblyVersionNumber=$(WinRT.Runtime.AssemblyVersion) -- RunConfiguration.TreatNoTestsAsError=true
+ testRunTitle: Unit Tests
diff --git a/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage.yml b/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage.yml
deleted file mode 100644
index b293dd3b6..000000000
--- a/build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage.yml
+++ /dev/null
@@ -1,134 +0,0 @@
-stages:
-- stage: BuildAndTest
- displayName: Build/Test Stage
- jobs:
- - job: BuildAndTest
- pool:
- vmImage: windows-latest
- timeoutInMinutes: 180
- # https://docs.microsoft.com/en-us/azure/devops/pipelines/process/phases?view=azure-devops&tabs=yaml#multi-job-configuration
- strategy:
- maxParallel: 10
- matrix:
- x64_Debug:
- BuildPlatform: 'x64'
- BuildConfiguration: 'debug'
- x86_Debug:
- BuildPlatform: 'x86'
- BuildConfiguration: 'debug'
- arm64_Debug:
- BuildPlatform: 'arm64'
- BuildConfiguration: 'debug'
- x64_Release:
- BuildPlatform: 'x64'
- BuildConfiguration: 'release'
- x86_Release:
- BuildPlatform: 'x86'
- BuildConfiguration: 'release'
- arm64_Release:
- BuildPlatform: 'arm64'
- BuildConfiguration: 'release'
- steps:
- - checkout: self
- clean: true
- persistCredentials: true
-
-# Build Steps
- - template: CsWinRT-Build-Steps.yml
-
-# Set Dotnet paths
- - task: PowerShell@2
- displayName: Set DotNet paths
- inputs:
- targetType: inline
- script: |
- Write-Host "##vso[task.setvariable variable=DOTNET_ROOT;]C:\Users\VssAdministrator\AppData\Local\Microsoft\dotnet\"
- Write-Host "##vso[task.setvariable variable=DOTNET_ROOT(x86);]C:\Users\VssAdministrator\AppData\Local\Microsoft\dotnet\x86\"
-
-# Run Unit Tests
- - task: DotNetCoreCLI@2
- displayName: Run Unit Tests
- condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
- inputs:
- command: test
- projects: 'src/Tests/UnitTest/UnitTest.csproj'
- arguments: --diag $(Build.ArtifactStagingDirectory)\unittest\test.log --no-build --logger xunit;LogFilePath=UNITTEST-$(Build.BuildNumber).xml /nologo /m /p:platform=$(BuildPlatform);configuration=$(BuildConfiguration);CIBuildReason=CI -- RunConfiguration.TreatNoTestsAsError=true
- testRunTitle: Unit Tests
-
-# Run Embedded Unit Tests
- - task: DotNetCoreCLI@2
- displayName: Run Embedded Unit Tests
- condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
- inputs:
- command: test
- projects: 'src/Samples/TestEmbedded/UnitTestEmbedded/UnitTestEmbedded.csproj'
- arguments: --diag $(Build.ArtifactStagingDirectory)\unittestembedded\test.log --no-build --logger xunit;LogFilePath=EMBEDDEDUNITTEST-$(Build.BuildNumber).xml /nologo /m /p:platform=$(BuildPlatform);configuration=$(BuildConfiguration) -- RunConfiguration.TreatNoTestsAsError=true
- testRunTitle: Embedded Unit Tests
-
-# Run Object Lifetime Tests
- - task: VSTest@2
- displayName: Run Object Lifetime Tests
- condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
- inputs:
- testAssemblyVer2: Tests\ObjectLifetimeTests\bin\$(BuildPlatform)\$(BuildConfiguration)\net6.0-windows10.0.19041.0\win10-$(BuildPlatform)\ObjectLifetimeTests.Lifted.build.appxrecipe
- searchFolder: $(Build.SourcesDirectory)\src
-
-# Publish Test Log
- - task: PublishBuildArtifacts@1
- displayName: Publish Test Log
- condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
- inputs:
- PathtoPublish: $(Build.ArtifactStagingDirectory)\unittest
- ArtifactName: $(BuildConfiguration)_$(BuildPlatform)_UnitTest
-
-# Publish Embedded Test Log
- - task: PublishBuildArtifacts@1
- displayName: Publish Embedded Test Log
- condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
- inputs:
- PathtoPublish: $(Build.ArtifactStagingDirectory)\unittestembedded
- ArtifactName: $(BuildConfiguration)_$(BuildPlatform)_UnitTestEmbedded
-
-# Run Host Tests
- - task: CmdLine@2
- displayName: Run Host Tests
- condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
- continueOnError: True
- inputs:
- workingDirectory: $(Build.SourcesDirectory)\src
- script: |
- dir _build\$(BuildPlatform)\$(BuildConfiguration)\HostTest\bin
- _build\$(BuildPlatform)\$(BuildConfiguration)\HostTest\bin\HostTest.exe --gtest_output=xml:HOSTTEST-$(Build.BuildNumber).xml
- exit /b 0
-
-# Run Source Generator Tests
- - task: CmdLine@2
- displayName: Run Source Generator Tests
- condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
- continueOnError: True
- inputs:
- workingDirectory: $(Build.SourcesDirectory)\src
- script: |
- dir _build\$(BuildPlatform)\$(BuildConfiguration)\AuthoringConsumptionTest\bin
- _build\$(BuildPlatform)\$(BuildConfiguration)\AuthoringConsumptionTest\bin\AuthoringConsumptionTest.exe --gtest_output=xml:AUTHORINGTEST-$(Build.BuildNumber).xml
- exit /b 0
-
-# Run Functional Tests
- - task: CmdLine@2
- displayName: Run Functional Tests
- condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
- inputs:
- workingDirectory: $(Build.SourcesDirectory)\src
- script: |
- set cswinrt_label=functionaltest
- build.cmd $(BuildPlatform) $(BuildConfiguration) $(VersionNumber) $(Build.BuildNumber) $(WinRT.Runtime.AssemblyVersion)
-
- - job: Benchmarks
- displayName: Run Benchmarks
- condition: or(eq(variables['_RunBenchmarks'],'true'), eq(variables['Build.Reason'],'Schedule'))
- dependsOn: []
- pool:
- vmImage: windows-latest
- timeoutInMinutes: 120
- steps:
- - template: CsWinRT-Benchmarks-Steps.yml
diff --git a/build/AzurePipelineTemplates/CsWinRT-BuildOnly-Stage.yml b/build/AzurePipelineTemplates/CsWinRT-BuildOnly-Stage.yml
index 967e6ec8e..8e546331e 100644
--- a/build/AzurePipelineTemplates/CsWinRT-BuildOnly-Stage.yml
+++ b/build/AzurePipelineTemplates/CsWinRT-BuildOnly-Stage.yml
@@ -3,20 +3,34 @@ stages:
displayName: Build for Release|x64
jobs:
- job: Build
- pool:
- vmImage: windows-2022
- timeoutInMinutes: 90
- # https://docs.microsoft.com/en-us/azure/devops/pipelines/process/phases?view=azure-devops&tabs=yaml#multi-job-configuration
+ templateContext:
+ outputs:
+ - output: pipelineArtifact
+ displayName: 'Publish Native'
+ condition: succeeded()
+ targetPath: $(Build.ArtifactStagingDirectory)\native
+ artifactName: release_x64
+ - output: pipelineArtifact
+ displayName: 'Publish ResX files'
+ condition: succeeded()
+ targetPath: $(Build.ArtifactStagingDirectory)\resx
+ artifactName: ResX
+ timeoutInMinutes: 180
strategy:
maxParallel: 10
matrix:
x64_Release:
BuildPlatform: 'x64'
BuildConfiguration: 'release'
+ variables:
+ StagingFolder: $(Build.ArtifactStagingDirectory)
steps:
- checkout: self
clean: true
persistCredentials: true
# Build Steps
- - template: CsWinRT-Build-Steps.yml
\ No newline at end of file
+ - template: CsWinRT-Build-Steps.yml@self
+ parameters:
+ BuildConfiguration: $(BuildConfiguration)
+ BuildPlatform: $(BuildPlatform)
\ No newline at end of file
diff --git a/build/AzurePipelineTemplates/CsWinRT-FunctionalTest-Steps.yml b/build/AzurePipelineTemplates/CsWinRT-FunctionalTest-Steps.yml
new file mode 100644
index 000000000..b742f2695
--- /dev/null
+++ b/build/AzurePipelineTemplates/CsWinRT-FunctionalTest-Steps.yml
@@ -0,0 +1,93 @@
+parameters:
+ - name: "BuildConfiguration"
+ type: string
+ - name: "BuildPlatform"
+ type: string
+ - name: "FunctionalTests"
+ type: object
+ default:
+ - JsonValueFunctionCalls
+ - ClassActivation
+ - Structs
+ - Events
+ - DynamicInterfaceCasting
+ - Collections
+ - Async
+ - DerivedClassActivation
+ - DerivedClassAsBaseClass
+ - CCW
+
+steps:
+ - ${{ each functionalTest in parameters.FunctionalTests }}:
+
+ # Do restore separately to workaround issue where specifying TargetFramework causes nuget restore to propagate it to project references causing issues
+ - task: MSBuild@1
+ displayName: Restore ${{ functionalTest }}
+ condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), and(eq(variables['BuildConfiguration'], 'release'), eq(variables['BuildPlatform'], 'x64'))))
+ inputs:
+ solution: $(Build.SourcesDirectory)\src\Tests\FunctionalTests\${{ functionalTest }}\${{ functionalTest }}.csproj
+ msbuildArguments: /t:restore /p:CIBuildReason=CI,RuntimeIdentifier=win-$(BuildPlatform),solutiondir=$(Build.SourcesDirectory)\src\,VersionNumber=$(VersionNumber),VersionString=$(Build.BuildNumber),AssemblyVersionNumber=$(WinRT.Runtime.AssemblyVersion),GenerateTestProjection=true,AllowedReferenceRelatedFileExtensions=".xml;.pri;.dll.config;.exe.config"
+ platform: $(BuildPlatform)
+ configuration: $(BuildConfiguration)
+
+ - task: MSBuild@1
+ displayName: Publish ${{ functionalTest }} trimmed (net8.0)
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'))
+ inputs:
+ solution: $(Build.SourcesDirectory)\src\Tests\FunctionalTests\${{ functionalTest }}\${{ functionalTest }}.csproj
+ msbuildArguments: /t:publish /p:CIBuildReason=CI,RuntimeIdentifier=win-$(BuildPlatform),TargetFramework=net8.0,solutiondir=$(Build.SourcesDirectory)\src\,VersionNumber=$(VersionNumber),VersionString=$(Build.BuildNumber),AssemblyVersionNumber=$(WinRT.Runtime.AssemblyVersion),GenerateTestProjection=true,AllowedReferenceRelatedFileExtensions=".xml;.pri;.dll.config;.exe.config"
+ platform: $(BuildPlatform)
+ configuration: $(BuildConfiguration)
+
+ - task: CmdLine@2
+ displayName: Run ${{ functionalTest }} trimmed (net8.0)
+ condition: and(succeeded(), eq(variables['BuildPlatform'], 'x86'))
+ continueOnError: True
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)\src\Tests\FunctionalTests\${{ functionalTest }}\bin\$(BuildConfiguration)\net8.0\win-$(BuildPlatform)\publish
+ script: |
+ dir
+ echo Running ${{ functionalTest }}.exe
+ ${{ functionalTest }}.exe
+ if %Errorlevel% NEQ 100 (
+ exit /b !ErrorLevel!
+ )
+ exit /b 0
+
+ - task: CopyFiles@2
+ displayName: Copy ${{ functionalTest }} trimmed (net8.0)
+ condition: and(eq(variables['_PublishFunctionalTests'], 'true'), eq(variables['BuildPlatform'], 'x86'))
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\Tests\FunctionalTests\${{ functionalTest }}\bin\$(BuildConfiguration)\net8.0\win-$(BuildPlatform)\publish
+ TargetFolder: $(StagingFolder)\FunctionalTests\${{ functionalTest }}\
+
+ - task: MSBuild@1
+ displayName: Publish ${{ functionalTest }} for AOT (net9.0)
+ condition: and(succeeded(), and(eq(variables['BuildConfiguration'], 'release'), eq(variables['BuildPlatform'], 'x64')))
+ inputs:
+ solution: $(Build.SourcesDirectory)\src\Tests\FunctionalTests\${{ functionalTest }}\${{ functionalTest }}.csproj
+ msbuildArguments: /t:publish /p:CIBuildReason=CI,RuntimeIdentifier=win-$(BuildPlatform),TargetFramework=net9.0,solutiondir=$(Build.SourcesDirectory)\src\,VersionNumber=$(VersionNumber),VersionString=$(Build.BuildNumber),AssemblyVersionNumber=$(WinRT.Runtime.AssemblyVersion),GenerateTestProjection=true,AllowedReferenceRelatedFileExtensions=".xml;.pri;.dll.config;.exe.config"
+ platform: $(BuildPlatform)
+ configuration: $(BuildConfiguration)
+
+ - task: CmdLine@2
+ displayName: Run ${{ functionalTest }} for AOT (net9.0)
+ condition: and(succeeded(), and(eq(variables['BuildConfiguration'], 'release'), eq(variables['BuildPlatform'], 'x64')))
+ continueOnError: True
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)\src\Tests\FunctionalTests\${{ functionalTest }}\bin\$(BuildConfiguration)\net9.0\win-$(BuildPlatform)\publish
+ script: |
+ dir
+ echo Running ${{ functionalTest }}.exe
+ ${{ functionalTest }}.exe
+ if %Errorlevel% NEQ 100 (
+ exit /b !ErrorLevel!
+ )
+ exit /b 0
+
+ - task: CopyFiles@2
+ displayName: Copy ${{ functionalTest }} for AOT (net9.0)
+ condition: and(eq(variables['_PublishFunctionalTests'], 'true'), and(eq(variables['BuildConfiguration'], 'release'), eq(variables['BuildPlatform'], 'x64')))
+ inputs:
+ SourceFolder: $(Build.SourcesDirectory)\src\Tests\FunctionalTests\${{ functionalTest }}\bin\$(BuildConfiguration)\net9.0\win-$(BuildPlatform)\publish
+ TargetFolder: $(StagingFolder)\FunctionalTests\${{ functionalTest }}\
diff --git a/build/AzurePipelineTemplates/CsWinRT-LocalizationPipeline.yml b/build/AzurePipelineTemplates/CsWinRT-LocalizationPipeline.yml
index e8cd6794d..4b717aa18 100644
--- a/build/AzurePipelineTemplates/CsWinRT-LocalizationPipeline.yml
+++ b/build/AzurePipelineTemplates/CsWinRT-LocalizationPipeline.yml
@@ -1,12 +1,25 @@
name: $(MajorVersion).$(MinorVersion).$(PatchVersion)$(PrereleaseVersion).$(date:yyMMdd)$(rev:.r)
variables:
-- template: CsWinRT-Variables.yml
-- group: CsWinRT-TouchdownBuild-Secrets
+- template: CsWinRT-Variables.yml@self
- name: LocOutputDir
value: '$(Agent.TempDirectory)\Temp\LocOutput'
-stages:
-- template: CsWinRT-BuildOnly-Stage.yml
-
-- template: CsWinRT-LocalizeResources-Stage.yml
+resources:
+ repositories:
+ - repository: 1ESPipelineTemplates
+ type: git
+ name: 1ESPipelineTemplates/1ESPipelineTemplates
+ ref: refs/tags/release
+extends:
+ template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates
+ parameters:
+ pool:
+ name: Azure-Pipelines-1ESPT-ExDShared
+ image: windows-2022
+ os: windows
+ customBuildTags:
+ - ES365AIMigrationTooling
+ stages:
+ - template: CsWinRT-BuildOnly-Stage.yml@self
+ - template: CsWinRT-LocalizeResources-Stage.yml@self
diff --git a/build/AzurePipelineTemplates/CsWinRT-LocalizeResources-Stage.yml b/build/AzurePipelineTemplates/CsWinRT-LocalizeResources-Stage.yml
index cb7c0cd17..96b8566ea 100644
--- a/build/AzurePipelineTemplates/CsWinRT-LocalizeResources-Stage.yml
+++ b/build/AzurePipelineTemplates/CsWinRT-LocalizeResources-Stage.yml
@@ -3,8 +3,6 @@ stages:
displayName: Call TDBuild to Localize Resources
jobs:
- job: Localize
- pool:
- vmImage: windows-2022
timeoutInMinutes: 90
steps:
- checkout: self
@@ -17,31 +15,28 @@ stages:
git config --global user.name "TDBuild"
# Download x64
- - task: DownloadBuildArtifacts@0
+ - task: DownloadPipelineArtifact@2
displayName: Download x64
inputs:
artifactName: release_x64
itemPattern: ''
- downloadPath: $(Build.SourcesDirectory)
- extractTars: false
+ targetPath: $(Build.SourcesDirectory)\release_x64
# Download RESX
- - task: DownloadBuildArtifacts@0
+ - task: DownloadPipelineArtifact@2
displayName: Download ResX
inputs:
artifactName: ResX
itemPattern: ''
- downloadPath: $(Build.SourcesDirectory)
- extractTars: false
+ targetPath: $(Build.SourcesDirectory)\ResX
# Run TouchdownBuild
- - task: TouchdownBuildTask@1
+ - task: TouchdownBuildTask@4
inputs:
environment: 'PRODEXT'
- teamId: $(CsWinRTTDBuildTeamID)
- authType: 'OAuth'
- authId: $(CsWinRTTDBuildAuthID)
- authKey: $(CsWinRTTDBuildAuthKey)
+ teamId: 31600
+ authType: 'FederatedIdentity'
+ FederatedIdentityServiceConnection: 'CsWinrtTdbuild'
localizationTarget: true
isPreview: false
resourceFilePath: |
@@ -51,13 +46,17 @@ stages:
outputDirectoryRoot: $(LocOutputDir)
appendRelativeDir: true
cultureMappingType: 'None'
- gitAction: NONE
# Sign TDBuild Output
- - task: EsrpCodeSigning@1
+ - task: EsrpCodeSigning@5
displayName: ESRP CodeSigning
inputs:
- ConnectedServiceName: 81cc6790-027c-4ef3-928d-65e8b96a691a
+ ConnectedServiceName: $(SigningServiceName)
+ AppRegistrationClientId: $(SigningAppId)
+ AppRegistrationTenantId: $(SigningTenantId)
+ AuthAKVName: $(SigningAKVName)
+ AuthCertName: $(SigningAuthCertName)
+ AuthSignCertName: $(SigningSignCertName)
FolderPath: $(LocOutputDir)
Pattern: |
release_x64\de-DE\WinRT.Host.dll.mui
@@ -114,9 +113,10 @@ stages:
TargetFolder: $(Build.ArtifactStagingDirectory)\loc
# Publish TDBuild Output
- - task: PublishBuildArtifacts@1
- displayName: Publish TouchdownBuild Output
- condition: succeeded()
- inputs:
- PathtoPublish: $(Build.ArtifactStagingDirectory)\loc
- ArtifactName: LocalizedResources
\ No newline at end of file
+ templateContext:
+ outputs:
+ - output: pipelineArtifact
+ displayName: 'Publish TouchdownBuild Output'
+ condition: succeeded()
+ targetPath: $(Build.ArtifactStagingDirectory)\loc
+ artifactName: LocalizedResources
diff --git a/build/AzurePipelineTemplates/CsWinRT-OneBranchVariables.yml b/build/AzurePipelineTemplates/CsWinRT-OneBranchVariables.yml
new file mode 100644
index 000000000..f1d858cf7
--- /dev/null
+++ b/build/AzurePipelineTemplates/CsWinRT-OneBranchVariables.yml
@@ -0,0 +1,16 @@
+parameters:
+- name: 'debug'
+ displayName: 'Enable debug output'
+ type: boolean
+ default: false
+
+variables:
+ system.debug: ${{ parameters.debug }}
+ ENABLE_PRS_DELAYSIGN: 1
+ NUGET_XMLDOC_MODE: none
+
+ # Docker image which is used to build the project https://aka.ms/obpipelines/containers
+ WindowsContainerImage: 'onebranch.azurecr.io/windows/ltsc2022/vse2022:latest'
+
+ Codeql.Enabled: true # CodeQL once every 3 days on the default branch for all languages its applicable to in that pipeline.
+ GDN_USE_DOTNET: true
\ No newline at end of file
diff --git a/build/AzurePipelineTemplates/CsWinRT-PR-Pipeline-OneBranch.yml b/build/AzurePipelineTemplates/CsWinRT-PR-Pipeline-OneBranch.yml
new file mode 100644
index 000000000..7be4dc540
--- /dev/null
+++ b/build/AzurePipelineTemplates/CsWinRT-PR-Pipeline-OneBranch.yml
@@ -0,0 +1,68 @@
+parameters:
+- name: 'debug'
+ displayName: 'Enable debug output'
+ type: boolean
+ default: false
+
+variables:
+- template: CsWinRT-Variables.yml@self
+- template: CsWinRT-OneBranchVariables.yml
+ parameters:
+ debug: ${{ parameters.debug }}
+
+name: $(MajorVersion).$(MinorVersion).$(PatchVersion)$(PrereleaseVersion).$(date:yyMMdd)$(rev:.r)
+
+trigger: none
+
+pool:
+ type: windows
+
+resources:
+ repositories:
+ - repository: templates
+ type: git
+ name: OneBranch.Pipelines/GovernedTemplates
+ ref: refs/heads/main
+ - repository: TestWinRT
+ type: git
+ name: TestWinRT.mirror
+ ref: 'ea7f7d821d4139e6cc3293d28bca214dcef014ea'
+
+extends:
+ template: v2/Microsoft.NonOfficial.yml@templates
+ parameters:
+ platform:
+ name: 'windows_undocked'
+ product: 'build_tools'
+
+ featureFlags:
+ WindowsHostVersion:
+ Version: 2022
+
+ globalSdl:
+ isNativeCode: true
+ tsa:
+ enabled: false
+ useDynamicRouting: true
+ sbom:
+ enabled: true
+ prefast:
+ enabled: true
+ binskim:
+ scanOutputDirectoryOnly: true
+
+ nugetPublishing:
+ feeds:
+ - name: CsWinRT
+
+ customTags: ES365AIMigrationTooling
+
+ stages:
+ - stage: BuildAndTest
+ jobs:
+ - template: build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage-OneBranch.yml@self
+
+ - stage: Nuget
+ dependsOn: BuildAndTest
+ jobs:
+ - template: build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Stage-OneBranch.yml@self
\ No newline at end of file
diff --git a/build/AzurePipelineTemplates/CsWinRT-PR-Pipeline.yml b/build/AzurePipelineTemplates/CsWinRT-PR-Pipeline.yml
new file mode 100644
index 000000000..4489a47f2
--- /dev/null
+++ b/build/AzurePipelineTemplates/CsWinRT-PR-Pipeline.yml
@@ -0,0 +1,36 @@
+variables:
+- template: CsWinRT-Variables.yml@self
+
+name: $(MajorVersion).$(MinorVersion).$(PatchVersion)$(PrereleaseVersion).$(date:yyMMdd)$(rev:.r)
+
+resources:
+ repositories:
+ - repository: 1ESPipelineTemplates
+ type: git
+ name: 1ESPipelineTemplates/1ESPipelineTemplates
+ ref: refs/tags/release
+ - repository: TestWinRT
+ type: github
+ name: microsoft/TestWinRT
+ endpoint: microsoft
+ ref: 'aa4edcd52542cfe036473d008d3f66c8a220d59d'
+extends:
+ template: v1/1ES.Unofficial.PipelineTemplate.yml@1ESPipelineTemplates
+ parameters:
+ pool:
+ name: Azure-Pipelines-1ESPT-ExDShared
+ image: windows-2022
+ os: windows
+ customBuildTags:
+ - ES365AIMigrationTooling
+ sdl:
+ binskim:
+ scanOutputDirectoryOnly: true
+ sourceRepositoriesToScan:
+ exclude:
+ - repository: TestWinRT
+ featureFlags:
+ autoBaseline: false
+ stages:
+ - template: CsWinRT-BuildAndTest-Stage-GitHub.yml@self
+ - template: CsWinRT-PublishToNuGet-Stage-GitHub.yml@self
\ No newline at end of file
diff --git a/build/AzurePipelineTemplates/CsWinRT-Pipeline.yml b/build/AzurePipelineTemplates/CsWinRT-Pipeline.yml
index 4c7744619..e380b3cd6 100644
--- a/build/AzurePipelineTemplates/CsWinRT-Pipeline.yml
+++ b/build/AzurePipelineTemplates/CsWinRT-Pipeline.yml
@@ -1,13 +1,74 @@
-variables:
-- template: CsWinRT-Variables.yml
+parameters:
+- name: 'debug'
+ displayName: 'Enable debug output'
+ type: boolean
+ default: false
+
+variables:
+- template: CsWinRT-Variables.yml@self
+- template: CsWinRT-OneBranchVariables.yml
+ parameters:
+ debug: ${{ parameters.debug }}
name: $(MajorVersion).$(MinorVersion).$(PatchVersion)$(PrereleaseVersion).$(date:yyMMdd)$(rev:.r)
-stages:
-- template: CsWinRT-BuildAndTest-Stage.yml
+trigger: none
+
+pool:
+ type: windows
+
+resources:
+ repositories:
+ - repository: templates
+ type: git
+ name: OneBranch.Pipelines/GovernedTemplates
+ ref: refs/heads/main
+ - repository: TestWinRT
+ type: git
+ name: TestWinRT.mirror
+ ref: 'ea7f7d821d4139e6cc3293d28bca214dcef014ea'
+
+extends:
+ template: v2/Microsoft.Official.yml@templates
+ parameters:
+ platform:
+ name: 'windows_undocked'
+ product: 'build_tools'
+
+ featureFlags:
+ WindowsHostVersion:
+ Version: 2022
+
+ globalSdl:
+ isNativeCode: true
+ asyncSdl:
+ enabled: true
+ tsa:
+ enabled: true
+ useDynamicRouting: true
+ codeql:
+ compiled:
+ enabled: true
+ tsaEnabled: true
+ sbom:
+ enabled: true
+ prefast:
+ enabled: true
+ binskim:
+ scanOutputDirectoryOnly: true
+
+ nugetPublishing:
+ feeds:
+ - name: CsWinRT
-- template: CsWinRT-PublishToNuget-Stage.yml
+ customTags: ES365AIMigrationTooling
-- template: CsWinRT-PublishToMaestro-Stage.yml
+ stages:
+ - stage: BuildAndTest
+ jobs:
+ - template: build/AzurePipelineTemplates/CsWinRT-BuildAndTest-Stage-OneBranch.yml@self
-- template: CsWinRT-UpdateWinUIRepo-Stage.yml
+ - stage: Nuget
+ dependsOn: BuildAndTest
+ jobs:
+ - template: build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Stage-OneBranch.yml@self
\ No newline at end of file
diff --git a/build/AzurePipelineTemplates/CsWinRT-PublishToMaestro-Stage.yml b/build/AzurePipelineTemplates/CsWinRT-PublishToMaestro-Stage.yml
index a2f75008b..61b4bb52d 100644
--- a/build/AzurePipelineTemplates/CsWinRT-PublishToMaestro-Stage.yml
+++ b/build/AzurePipelineTemplates/CsWinRT-PublishToMaestro-Stage.yml
@@ -4,8 +4,6 @@ stages:
jobs:
- job: TriggerMaestroPublish
condition: eq(variables['_IsRelease'],'true')
- pool:
- vmImage: windows-latest
steps:
# Parse Versions needed for offical CsWinRT version of build
@@ -41,7 +39,7 @@ stages:
echo ##vso[task.setvariable variable=VersionNumber;]%VersionNumber%
echo ##vso[task.setvariable variable=NugetVersion;]%NugetVersion%
- - template: ..\..\eng\common\AzurePipelineTemplates\Maestro-PublishBuildToMaestro-Steps.yml
+ - template: ..\..\eng\common\AzurePipelineTemplates\Maestro-PublishBuildToMaestro-Steps.yml@self
parameters:
AssetNames: Microsoft.Windows.CsWinRT;CsWinRT.Dependency.DotNetCoreSdk;CsWinRT.Dependency.DotNetCoreRuntime;CsWinRT.Dependency.WindowsSdkVersionSuffix
- AssetVersions: $(NugetVersion);$(Net6.Sdk.Version);$(_DotNetRuntimeVersion);$(_WindowsSdkVersionSuffix)
\ No newline at end of file
+ AssetVersions: $(NugetVersion);$(Net8.Sdk.Version);$(_DotNetRuntimeVersion);$(_WindowsSdkVersionSuffix)
\ No newline at end of file
diff --git a/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Stage-GitHub.yml b/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Stage-GitHub.yml
new file mode 100644
index 000000000..554a36cd5
--- /dev/null
+++ b/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Stage-GitHub.yml
@@ -0,0 +1,23 @@
+stages:
+- stage: Publish
+ displayName: Publish To Artifacts
+ jobs:
+ - job: PublishToArtifacts
+ variables:
+ ob_outputDirectory: '$(Build.SourcesDirectory)\out'
+
+ steps:
+ - checkout: self
+ clean: True
+ persistCredentials: True
+
+ - template: CsWinRT-PublishToNuGet-Steps.yml@self
+ parameters:
+ IsGitHub: true
+
+ templateContext:
+ outputs:
+ - output: pipelineArtifact
+ displayName: 'Publish NuGet'
+ targetPath: $(ob_outputDirectory)\packages
+ artifactName: drop_Nuget
diff --git a/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Stage-OneBranch.yml b/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Stage-OneBranch.yml
new file mode 100644
index 000000000..0b854cc83
--- /dev/null
+++ b/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Stage-OneBranch.yml
@@ -0,0 +1,16 @@
+jobs:
+ - job: PublishTo_CsWinRT_InternalFeed
+ displayName: Publish To Internal Nuget Feed Stage
+ pool:
+ type: windows
+
+ variables:
+ ob_outputDirectory: '$(Build.SourcesDirectory)\out'
+ ob_nugetPublishing_enabled: true
+ # We only do packaging.
+ ob_sdl_prefast_enabled: false
+ ob_sdl_prefast_runDuring: 'Build'
+ ob_sdl_checkCompliantCompilerWarnings: true
+
+ steps:
+ - template: CsWinRT-PublishToNuGet-Steps.yml@self
\ No newline at end of file
diff --git a/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Steps.yml b/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Steps.yml
new file mode 100644
index 000000000..86a287452
--- /dev/null
+++ b/build/AzurePipelineTemplates/CsWinRT-PublishToNuGet-Steps.yml
@@ -0,0 +1,98 @@
+parameters:
+- name: 'IsGitHub'
+ displayName: 'GitHub Build'
+ type: boolean
+ default: false
+
+steps:
+ - task: NuGetAuthenticate@1
+
+ # Download x86
+ - task: DownloadPipelineArtifact@2
+ displayName: 'Download x86 '
+ inputs:
+ artifactName: drop_BuildAndTest_release_x86
+ itemPattern: ''
+ targetPath: $(Build.SourcesDirectory)\release_x86
+
+ # Download x64
+ - task: DownloadPipelineArtifact@2
+ displayName: Download x64
+ inputs:
+ artifactName: drop_BuildAndTest_release_x64
+ itemPattern: ''
+ targetPath: $(Build.SourcesDirectory)\release_x64
+
+ # Download arm64
+ - task: DownloadPipelineArtifact@2
+ displayName: Download arm64
+ inputs:
+ artifactName: drop_BuildAndTest_release_arm64
+ itemPattern: ''
+ targetPath: $(Build.SourcesDirectory)\release_arm64
+
+ # Parse Versions
+ - task: CmdLine@2
+ displayName: Parse Versions
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)
+ script: |
+ rem Parse the build-generated Build.BuildNumber into components that
+ rem can be recombined for version resources, nuget packages, etc.
+
+ @echo off
+
+ rem Encode the build date/rev into a 16 bit value for resource versions
+ if "$(PrereleaseVersion)"=="" (
+ set RevisionBase=30000
+ ) else (
+ set RevisionBase=0
+ )
+ for /f "tokens=4,5 delims=." %%i in ("$(Build.BuildNumber)") do set BuildMetadata=%%i.%%j & set /a BuildRevision=%RevisionBase%+(((%%i/10000)-20)*366+((%%i)%%10000)/100*31+((%%i)%%100))*10+%%j
+
+ set VersionNumber=$(MajorVersion).$(MinorVersion).$(PatchVersion).%BuildRevision%
+
+ if "$(PrereleaseVersion)"=="" (
+ set NugetVersion=$(MajorVersion).$(MinorVersion).$(PatchVersion)
+ ) else (
+ set NugetVersion=$(Build.BuildNumber)
+ )
+
+ rem Export generated version numbers back for subsequent tasks
+ echo ##vso[task.setvariable variable=BuildMetadata;]%BuildMetadata%
+ echo ##vso[task.setvariable variable=BuildRevision;]%BuildRevision%
+ echo ##vso[task.setvariable variable=VersionNumber;]%VersionNumber%
+ echo ##vso[task.setvariable variable=NugetVersion;]%NugetVersion%
+
+ # NuGet Pack
+ - task: NuGetCommand@2
+ displayName: NuGet pack
+ inputs:
+ command: pack
+ searchPatternPack: nuget/Microsoft.Windows.CsWinRT.nuspec
+ configurationToPack: Release
+ buildProperties: cswinrt_nuget_version=$(NugetVersion);cswinrt_exe=$(Build.SourcesDirectory)\release_x86\native\cswinrt.exe;interop_winmd=$(Build.SourcesDirectory)\release_x86\native\WinRT.Interop.winmd;netstandard2_runtime=$(Build.SourcesDirectory)\release_x86\netstandard2.0\WinRT.Runtime.dll;net8_runtime=$(Build.SourcesDirectory)\release_x86\net8.0\WinRT.Runtime.dll;net9_runtime=$(Build.SourcesDirectory)\release_x86\net9.0\WinRT.Runtime.dll;source_generator_roslyn4080=$(Build.SourcesDirectory)\release_x86\netstandard2.0\roslyn4080\WinRT.SourceGenerator.dll;source_generator_roslyn4120=$(Build.SourcesDirectory)\release_x86\netstandard2.0\roslyn4120\WinRT.SourceGenerator.dll;winrt_shim=$(Build.SourcesDirectory)\release_x86\net8.0\WinRT.Host.Shim.dll;winrt_host_x86=$(Build.SourcesDirectory)\release_x86\native\WinRT.Host.dll;winrt_host_x64=$(Build.SourcesDirectory)\release_x64\native\WinRT.Host.dll;winrt_host_arm64=$(Build.SourcesDirectory)\release_arm64\native\WinRT.Host.dll;winrt_host_resource_x86=$(Build.SourcesDirectory)\release_x86\native\WinRT.Host.dll.mui;winrt_host_resource_x64=$(Build.SourcesDirectory)\release_x64\native\WinRT.Host.dll.mui;winrt_host_resource_arm64=$(Build.SourcesDirectory)\release_arm64\native\WinRT.Host.dll.mui;guid_patch=$(Build.SourcesDirectory)\release_x86\net8.0\IIDOptimizer\*.*
+ packDestination: $(ob_outputDirectory)\packages
+
+ - task: NuGetCommand@2
+ displayName: NuGet pack
+ condition: eq(variables['_PublishCsWinMD'], 'true')
+ inputs:
+ command: pack
+ searchPatternPack: nuget/Microsoft.Windows.CsWinMD.nuspec
+ configurationToPack: Release
+ buildProperties: cswinmd_nuget_version=$(NugetVersion);cswinmd_outpath=$(Build.SourcesDirectory)\release_x86\net8.0\CsWinMD;source_generator_roslyn4080=$(Build.SourcesDirectory)\release_x86\netstandard2.0\roslyn4080\WinRT.SourceGenerator.dll
+ packDestination: $(ob_outputDirectory)\packages
+
+ - ${{ if eq(parameters.IsGitHub, false) }}:
+
+ # NuGet signing
+ - task: onebranch.pipeline.signing@1
+ displayName: '🔒 Onebranch Signing for NuGet'
+ inputs:
+ command: sign
+ cp_code: 'CP-401405'
+ files_to_sign: |
+ Microsoft.Windows.CsWinRT.*.nupkg;
+ Microsoft.Windows.CsWinMD.*.nupkg;
+ search_root: $(ob_outputDirectory)\packages
\ No newline at end of file
diff --git a/build/AzurePipelineTemplates/CsWinRT-PublishToNuget-Stage.yml b/build/AzurePipelineTemplates/CsWinRT-PublishToNuget-Stage.yml
deleted file mode 100644
index d79b9e2c7..000000000
--- a/build/AzurePipelineTemplates/CsWinRT-PublishToNuget-Stage.yml
+++ /dev/null
@@ -1,285 +0,0 @@
-stages:
-- stage: Publish
- displayName: Publish To Internal Nuget Feed Stage
- jobs:
- - job: PublishTo_CsWinRT_InternalFeed
- pool:
- vmImage: windows-latest
- steps:
- - checkout: self
- clean: True
- persistCredentials: True
-
-# Use NuGet 5.3
- - task: NuGetToolInstaller@1
- displayName: Use NuGet 5.3
- continueOnError: True
- inputs:
- versionSpec: 5.3
-
-# Component Detection
- - task: ComponentGovernanceComponentDetection@0
- displayName: Component Detection
-
-# Download x86
- - task: DownloadBuildArtifacts@0
- displayName: 'Download x86 '
- inputs:
- artifactName: release_x86
- itemPattern: ''
- downloadPath: $(Build.SourcesDirectory)
- extractTars: false
-
-# Download x64
- - task: DownloadBuildArtifacts@0
- displayName: Download x64
- inputs:
- artifactName: release_x64
- itemPattern: ''
- downloadPath: $(Build.SourcesDirectory)
- extractTars: false
-
-# Download arm64
- - task: DownloadBuildArtifacts@0
- displayName: Download arm64
- inputs:
- artifactName: release_arm64
- itemPattern: ''
- downloadPath: $(Build.SourcesDirectory)
- extractTars: false
-
-# Download NetStandard2.0
- - task: DownloadBuildArtifacts@0
- displayName: Download NetStandard 2.0
- inputs:
- artifactName: netstandard2.0
- itemPattern: ''
- downloadPath: $(Build.SourcesDirectory)
- extractTars: false
-
-# Download Net5.0
- - task: DownloadBuildArtifacts@0
- displayName: 'Download Net5.0 '
- inputs:
- artifactName: net5.0
- itemPattern: ''
- downloadPath: $(Build.SourcesDirectory)
- extractTars: false
-
-# Download Net6.0
- - task: DownloadBuildArtifacts@0
- displayName: 'Download Net6.0'
- inputs:
- artifactName: net6.0
- itemPattern: ''
- downloadPath: $(Build.SourcesDirectory)
- extractTars: false
-
-# Stage Binaries
- - task: CmdLine@2
- displayName: Stage Binaries
- inputs:
- workingDirectory: $(Build.SourcesDirectory)
- script: |
- copy release_x86\cswinrt.exe cswinrt.exe
- copy release_x86\WinRT.Interop.winmd WinRT.Interop.winmd
-
-# ESRP Codesigning
- - task: EsrpCodeSigning@1
- displayName: ESRP CodeSigning
- inputs:
- ConnectedServiceName: 81cc6790-027c-4ef3-928d-65e8b96a691a
- FolderPath: $(Build.SourcesDirectory)
- Pattern: |
- cswinrt.exe
- WinRT.Interop.winmd
- netstandard2.0\WinRT.Runtime.dll
- netstandard2.0\WinRT.Host.Shim.dll
- netstandard2.0\WinRT.SourceGenerator.dll
- net5.0\WinRT.Runtime.dll
- net6.0\WinRT.Host.Shim.dll
- net6.0\WinRT.Runtime.dll
- release_x64\WinRT.Host.dll
- release_x64\WinRT.Host.dll.mui
- release_x86\WinRT.Host.dll
- release_x86\WinRT.Host.dll.mui
- release_arm64\WinRT.Host.dll
- release_arm64\WinRT.Host.dll.mui
- net6.0\IIDOptimizer\IIDOptimizer.exe
- net6.0\IIDOptimizer\IIDOptimizer.dll
- net6.0\CsWinMD\CsWinMD.exe
- net6.0\CsWinMD\CsWinMD.dll
- UseMinimatch: true
- signConfigType: inlineSignParams
- inlineOperation: |
- [
- {
- "keyCode": "CP-230012",
- "operationSetCode": "SigntoolSign",
- "parameters": [
- {
- "parameterName": "OpusName",
- "parameterValue": "Microsoft"
- },
- {
- "parameterName": "OpusInfo",
- "parameterValue": "http://www.microsoft.com"
- },
- {
- "parameterName": "PageHash",
- "parameterValue": "/NPH"
- },
- {
- "parameterName": "FileDigest",
- "parameterValue": "/fd sha256"
- },
- {
- "parameterName": "TimeStamp",
- "parameterValue": "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
- }
- ],
- "toolName": "signtool.exe",
- "toolVersion": "6.2.9304.0"
- }
- ]
-
-# ESRP CodeSigning 3rd Party
- - task: EsrpCodeSigning@1
- displayName: ESRP CodeSigning 3rd party
- continueOnError: True
- inputs:
- ConnectedServiceName: 81cc6790-027c-4ef3-928d-65e8b96a691a
- FolderPath: $(Build.SourcesDirectory)
- Pattern: |
- net6.0\IIDOptimizer\Mono.Cecil.dll
- net6.0\IIDOptimizer\Mono.Cecil.Mdb.dll
- net6.0\IIDOptimizer\Mono.Cecil.Pdb.dll
- net6.0\IIDOptimizer\Mono.Cecil.Rocks.dll
- UseMinimatch: true
- signConfigType: inlineSignParams
- inlineOperation: |
- [
- {
- "KeyCode" : "CP-231522",
- "OperationCode" : "SigntoolSign",
- "Parameters" : {
- "OpusName" : "Microsoft",
- "OpusInfo" : "http://www.microsoft.com",
- "Append" : "/as",
- "FileDigest" : "/fd \"SHA256\"",
- "PageHash" : "/NPH",
- "TimeStamp" : "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256"
- },
- "ToolName" : "sign",
- "ToolVersion" : "1.0"
- },
- {
- "KeyCode" : "CP-231522",
- "OperationCode" : "SigntoolVerify",
- "Parameters" : {},
- "ToolName" : "sign",
- "ToolVersion" : "1.0"
- }
- ]
-
-# Parse Versions
- - task: CmdLine@2
- displayName: Parse Versions
- inputs:
- workingDirectory: $(Build.SourcesDirectory)
- script: |
- rem Parse the build-generated Build.BuildNumber into components that
- rem can be recombined for version resources, nuget packages, etc.
-
- @echo off
-
- rem Encode the build date/rev into a 16 bit value for resource versions
- if "$(PrereleaseVersion)"=="" (
- set RevisionBase=30000
- ) else (
- set RevisionBase=0
- )
- for /f "tokens=4,5 delims=." %%i in ("$(Build.BuildNumber)") do set BuildMetadata=%%i.%%j & set /a BuildRevision=%RevisionBase%+(((%%i/10000)-20)*366+((%%i)%%10000)/100*31+((%%i)%%100))*10+%%j
-
- set VersionNumber=$(MajorVersion).$(MinorVersion).$(PatchVersion).%BuildRevision%
-
- if "$(PrereleaseVersion)"=="" (
- set NugetVersion=$(MajorVersion).$(MinorVersion).$(PatchVersion)
- ) else (
- set NugetVersion=$(Build.BuildNumber)
- )
-
- rem Export generated version numbers back for subsequent tasks
- echo ##vso[task.setvariable variable=BuildMetadata;]%BuildMetadata%
- echo ##vso[task.setvariable variable=BuildRevision;]%BuildRevision%
- echo ##vso[task.setvariable variable=VersionNumber;]%VersionNumber%
- echo ##vso[task.setvariable variable=NugetVersion;]%NugetVersion%
-
-# NuGet Pack
- - task: NuGetCommand@2
- displayName: NuGet pack
- inputs:
- command: pack
- searchPatternPack: nuget/Microsoft.Windows.CsWinRT.nuspec
- configurationToPack: Release
- buildProperties: cswinrt_nuget_version=$(NugetVersion);cswinrt_exe=$(Build.SourcesDirectory)\cswinrt.exe;interop_winmd=$(Build.SourcesDirectory)\WinRT.Interop.winmd;netstandard2_runtime=$(Build.SourcesDirectory)\netstandard2.0\WinRT.Runtime.dll;net5_runtime=$(Build.SourcesDirectory)\net5.0\WinRT.Runtime.dll;net6_runtime=$(Build.SourcesDirectory)\net6.0\WinRT.Runtime.dll;source_generator=$(Build.SourcesDirectory)\netstandard2.0\WinRT.SourceGenerator.dll;winrt_shim=$(Build.SourcesDirectory)\net6.0\WinRT.Host.Shim.dll;winrt_host_x86=$(Build.SourcesDirectory)\release_x86\WinRT.Host.dll;winrt_host_x64=$(Build.SourcesDirectory)\release_x64\WinRT.Host.dll;winrt_host_arm64=$(Build.SourcesDirectory)\release_arm64\WinRT.Host.dll;winrt_host_resource_x86=$(Build.SourcesDirectory)\release_x86\WinRT.Host.dll.mui;winrt_host_resource_x64=$(Build.SourcesDirectory)\release_x64\WinRT.Host.dll.mui;winrt_host_resource_arm64=$(Build.SourcesDirectory)\release_arm64\WinRT.Host.dll.mui;guid_patch=$(Build.SourcesDirectory)\net6.0\IIDOptimizer\*.*
-
- - task: NuGetCommand@2
- displayName: NuGet pack
- condition: eq(variables['_PublishCsWinMD'], 'true')
- inputs:
- command: pack
- searchPatternPack: nuget/Microsoft.Windows.CsWinMD.nuspec
- configurationToPack: Release
- buildProperties: cswinmd_nuget_version=$(NugetVersion);cswinmd_outpath=$(Build.SourcesDirectory)\net6.0\CsWinMD;source_generator=$(Build.SourcesDirectory)\netstandard2.0\WinRT.SourceGenerator.dll
-
-# ESRP CodeSigning
- - task: EsrpCodeSigning@1
- displayName: ESRP CodeSigning
- inputs:
- ConnectedServiceName: 81cc6790-027c-4ef3-928d-65e8b96a691a
- FolderPath: $(Build.ArtifactStagingDirectory)
- Pattern: '*.nupkg'
- signConfigType: inlineSignParams
- inlineOperation: |
- [
- {
- "KeyCode" : "CP-401405",
- "OperationCode" : "NuGetSign",
- "Parameters" : {},
- "ToolName" : "sign",
- "ToolVersion" : "1.0"
- },
- {
- "KeyCode" : "CP-401405",
- "OperationCode" : "NuGetVerify",
- "Parameters" : {},
- "ToolName" : "sign",
- "ToolVersion" : "1.0"
- }
- ]
-
-# NuGet push
- - task: NuGetCommand@2
- displayName: NuGet push
- inputs:
- command: push
- searchPatternPush: $(Build.ArtifactStagingDirectory)/**/*.nupkg
- feedPublish: cfbb8a6b-97b7-4070-a6e8-a4081b046ae0
- externalEndpoint: 80b1372e-52e9-486d-934f-92d5590c2241
-
-# NuGet publish
- - task: PublishPipelineArtifact@1
- displayName: NuGet publish
- inputs:
- path: $(Build.ArtifactStagingDirectory)
- artifactName: Publish
-
-# Publish Symbols
- - task: PublishSymbols@2
- displayName: Publish Symbols
- inputs:
- SearchPattern: '**/*.pdb'
- IndexSources: false
- SymbolServerType: TeamServices
- SymbolsProduct: C#/WinRT
\ No newline at end of file
diff --git a/build/AzurePipelineTemplates/CsWinRT-Release.yml b/build/AzurePipelineTemplates/CsWinRT-Release.yml
new file mode 100644
index 000000000..cbb3d6ab2
--- /dev/null
+++ b/build/AzurePipelineTemplates/CsWinRT-Release.yml
@@ -0,0 +1,39 @@
+resources:
+ repositories:
+ - repository: templates
+ type: git
+ name: OneBranch.Pipelines/GovernedTemplates
+ ref: refs/heads/main
+ pipelines:
+ - pipeline: CsWinRT
+ source: CsWinRT Official (OneBranch)
+extends:
+ template: v2/OneBranch.Official.CrossPlat.yml@templates
+ parameters:
+ release:
+ category: NonAzure
+
+ stages:
+ - stage: 'Publish'
+ displayName: 'Publish to NuGet'
+ variables:
+ ob_release_environment: Production
+
+ jobs:
+ - job: ReleaseJob
+ templateContext:
+ inputs:
+ - input: pipelineArtifact
+ pipeline: CsWinRT
+ artifactName: drop_Nuget_PublishTo_CsWinRT_InternalFeed
+ pool:
+ type: release
+
+ steps:
+ - task: NuGetCommand@2
+ displayName: 'NuGet Push'
+ inputs:
+ command: push
+ packagesToPush: '$(Pipeline.Workspace)\packages\Microsoft.Windows.CsWinRT.*.nupkg'
+ nuGetFeedType: external
+ publishFeedCredentials: 'C#/WinRT NuGet 3'
\ No newline at end of file
diff --git a/build/AzurePipelineTemplates/CsWinRT-SyncMirror-Pipeline.yml b/build/AzurePipelineTemplates/CsWinRT-SyncMirror-Pipeline.yml
new file mode 100644
index 000000000..2f0095d00
--- /dev/null
+++ b/build/AzurePipelineTemplates/CsWinRT-SyncMirror-Pipeline.yml
@@ -0,0 +1,68 @@
+# Sync branches in a mirror repository to a base repo by running this pipeline
+# from the mirror repo, and supplying the base repo as a parameter
+name: $(BuildDefinitionName)_$(date:yyMMdd)$(rev:.r)
+
+parameters:
+ - name: "SourceToTargetBranches"
+ type: object
+ default:
+ master: master
+ - name: "SourceRepository"
+ type: string
+ default: "https://github.com/microsoft/cswinrt.git"
+
+resources:
+ repositories:
+ - repository: 1ESPipelineTemplates
+ type: git
+ name: 1ESPipelineTemplates/1ESPipelineTemplates
+ ref: refs/tags/release
+extends:
+ template: v1/1ES.Official.PipelineTemplate.yml@1ESPipelineTemplates
+ parameters:
+ pool:
+ name: Azure-Pipelines-1ESPT-ExDShared
+ image: windows-2022
+ os: windows
+ customBuildTags:
+ - ES365AIMigrationTooling
+ stages:
+ - stage: stage
+ jobs:
+ - job: SyncMirror
+ strategy:
+ matrix:
+ ${{ each branches in parameters.SourceToTargetBranches }}:
+ ${{ branches.key }}:
+ SourceBranch: ${{ branches.key }}
+ TargetBranch: ${{ branches.value }}
+ dependsOn: []
+ steps:
+ - checkout: self
+ persistCredentials: true
+
+ - task: PowerShell@2
+ inputs:
+ targetType: 'inline'
+ script: |
+ Write-Host "SourceBranch " + "$(SourceBranch)"
+ Write-Host "TargetBranch " + "$(TargetBranch)"
+
+ $repo = "${{ parameters.SourceRepository }}"
+ git remote add sourcerepo $repo
+ git remote
+
+ $target = "$(TargetBranch)"
+ git fetch origin $target
+ git checkout $target
+ git pull origin $target
+
+ $source = "$(SourceBranch)"
+ git fetch sourcerepo $source
+ git pull sourcerepo $source
+
+ - task: CmdLine@2
+ inputs:
+ script: |
+ git push
+
diff --git a/build/AzurePipelineTemplates/CsWinRT-Test-Steps.yml b/build/AzurePipelineTemplates/CsWinRT-Test-Steps.yml
new file mode 100644
index 000000000..fb60b711e
--- /dev/null
+++ b/build/AzurePipelineTemplates/CsWinRT-Test-Steps.yml
@@ -0,0 +1,101 @@
+parameters:
+ - name: "BuildConfiguration"
+ type: string
+ - name: "BuildPlatform"
+ type: string
+
+steps:
+# Enable GC stress
+ - task: PowerShell@2
+ displayName: Enable GC Stress
+ condition: and(succeeded(), eq(variables['_RunGCStress'], 'true'))
+ inputs:
+ targetType: inline
+ script: |
+ Write-Host "##vso[task.setvariable variable=DOTNET_GCStress;]0xC"
+
+# Run Unit Tests
+ - task: DotNetCoreCLI@2
+ displayName: Run Unit Tests
+ condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
+ inputs:
+ command: test
+ projects: 'src/Tests/UnitTest/UnitTest.csproj'
+ arguments: --diag $(Build.ArtifactStagingDirectory)\unittest\test.log --no-build --logger xunit;LogFilePath=UNITTEST-$(Build.BuildNumber).xml /nologo /m /p:platform=$(BuildPlatform);configuration=$(BuildConfiguration);CIBuildReason=CI -- RunConfiguration.TreatNoTestsAsError=true
+ testRunTitle: Unit Tests
+
+# Run Embedded Unit Tests
+ - task: DotNetCoreCLI@2
+ displayName: Run Embedded Unit Tests
+ condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
+ inputs:
+ command: test
+ projects: 'src/Samples/TestEmbedded/UnitTestEmbedded/UnitTestEmbedded.csproj'
+ arguments: --diag $(Build.ArtifactStagingDirectory)\unittestembedded\test.log --no-build --logger xunit;LogFilePath=EMBEDDEDUNITTEST-$(Build.BuildNumber).xml /nologo /m /p:platform=$(BuildPlatform);configuration=$(BuildConfiguration) -- RunConfiguration.TreatNoTestsAsError=true
+ testRunTitle: Embedded Unit Tests
+
+# Run Host Tests
+ - task: CmdLine@2
+ displayName: Run Host Tests
+ condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
+ continueOnError: True
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)\src
+ script: |
+ dir _build\$(BuildPlatform)\$(BuildConfiguration)\HostTest\bin
+ _build\$(BuildPlatform)\$(BuildConfiguration)\HostTest\bin\HostTest.exe --gtest_output=xml:HOSTTEST-$(Build.BuildNumber).xml
+
+# Run Source Generator Tests
+ - task: CmdLine@2
+ displayName: Run Source Generator Tests
+ condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
+ continueOnError: True
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)\src
+ script: |
+ dir _build\$(BuildPlatform)\$(BuildConfiguration)\AuthoringConsumptionTest\bin
+ _build\$(BuildPlatform)\$(BuildConfiguration)\AuthoringConsumptionTest\bin\AuthoringConsumptionTest.exe --gtest_output=xml:AUTHORINGTEST-$(Build.BuildNumber).xml
+
+# Run WUX Tests
+ - task: CmdLine@2
+ displayName: Run WUX Tests
+ condition: and(succeeded(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
+ continueOnError: True
+ inputs:
+ workingDirectory: $(Build.SourcesDirectory)\src
+ script: |
+ dir _build\$(BuildPlatform)\$(BuildConfiguration)\AuthoringWuxConsumptionTest\bin
+ _build\$(BuildPlatform)\$(BuildConfiguration)\AuthoringWuxConsumptionTest\bin\AuthoringWuxConsumptionTest.exe --gtest_output=xml:AUTHORINGWUXTEST-$(Build.BuildNumber).xml
+
+# Run Functional Tests
+ - template: CsWinRT-FunctionalTest-Steps.yml@self
+ parameters:
+ BuildConfiguration: $(BuildConfiguration)
+ BuildPlatform: $(BuildPlatform)
+
+# Disable GC stress
+ - task: PowerShell@2
+ displayName: Disable GC Stress
+ condition: and(succeeded(), eq(variables['_RunGCStress'], 'true'))
+ inputs:
+ targetType: inline
+ script: |
+ Write-Host "##vso[task.setvariable variable=DOTNET_GCStress;]0x0"
+
+ - task: CopyFiles@2
+ displayName: Publish Test Log
+ condition: and(failed(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
+ inputs:
+ SourceFolder: $(Build.ArtifactStagingDirectory)\unittest
+ Contents: |
+ test.log
+ TargetFolder: $(StagingFolder)\UnitTest
+
+ - task: CopyFiles@2
+ displayName: Publish Embedded Test Log
+ condition: and(failed(), or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')))
+ inputs:
+ SourceFolder: $(Build.ArtifactStagingDirectory)\unittestembedded
+ Contents: |
+ test.log
+ TargetFolder: $(StagingFolder)\UnitTestEmbedded
diff --git a/build/AzurePipelineTemplates/CsWinRT-UpdateWinUIRepo-Stage.yml b/build/AzurePipelineTemplates/CsWinRT-UpdateWinUIRepo-Stage.yml
index 622002efa..9c6303c9e 100644
--- a/build/AzurePipelineTemplates/CsWinRT-UpdateWinUIRepo-Stage.yml
+++ b/build/AzurePipelineTemplates/CsWinRT-UpdateWinUIRepo-Stage.yml
@@ -4,9 +4,6 @@ stages:
jobs:
- job: TriggerWinUIPipelines
condition: or(eq(variables['_IsRelease'],'true'), eq(variables['Build.Reason'],'Schedule'))
- pool:
- name: Azure Pipelines
- vmImage: windows-latest
steps:
- script: |
rem Parse the build-generated Build.BuildNumber into components that
diff --git a/build/AzurePipelineTemplates/CsWinRT-Variables.yml b/build/AzurePipelineTemplates/CsWinRT-Variables.yml
index f9371b4f9..2d969c6ca 100644
--- a/build/AzurePipelineTemplates/CsWinRT-Variables.yml
+++ b/build/AzurePipelineTemplates/CsWinRT-Variables.yml
@@ -1,18 +1,39 @@
variables:
- MajorVersion: 2
- MinorVersion: 0
- PatchVersion: 0
- WinRT.Runtime.AssemblyVersion: '2.0.0.0'
- Net5.SDK.Feed: 'https://dotnetcli.blob.core.windows.net/dotnet'
- Net5.SDK.Version: '5.0.408'
- Net6.SDK.Version: '6.0.301'
- NoSamples: 'false'
+- name: MajorVersion
+ value: 2
+- name: MinorVersion
+ value: 3
+- name: PatchVersion
+ value: 0
+- name: WinRT.Runtime.AssemblyVersion
+ value: '2.3.0.0'
+- name: Net5.SDK.Feed
+ value: 'https://dotnetcli.blob.core.windows.net/dotnet'
+- name: Net8.SDK.Version
+ value: '8.0.303'
+- name: Net9.SDK.Version
+ value: '9.0.201'
+
+- name: NoSamples
+ value: 'false'
# This 'coalesce' pattern allows the yml to define a default value for a variable but allows the value to be overridden at queue time.
# E.g. '_IsRelease' defaults to empty string, but if 'IsRelease' is set at queue time that value will be used.
- _IsRelease: $[coalesce(variables.IsRelease, '')]
- _RunBenchmarks: $[coalesce(variables.RunBenchmarks, 'false')]
- _DotNetRuntimeVersion: $[coalesce(variables.DotNetRuntimeVersion, '5.0.17')]
- _WindowsSdkVersionSuffix: $[coalesce(variables.WindowsSdkPackageVersionSuffix, '25')]
- _PublishCsWinMD: $[coalesce(variables.PublishCsWinMD, 'false')]
+- name: _IsRelease
+ value: $[coalesce(variables.IsRelease, '')]
+- name: _RunBenchmarks
+ value: $[coalesce(variables.RunBenchmarks, 'false')]
+- name: _RunGCStress
+ value: $[coalesce(variables.RunGCStress, 'false')]
+- name: _DotNetRuntimeVersion
+ value: $[coalesce(variables.DotNetRuntimeVersion, '8.0.303')]
+- name: _DotNet9RuntimeVersion
+ value: $[coalesce(variables.DotNet9RuntimeVersion, '9.0.201')]
+
+- name: _WindowsSdkVersionSuffix
+ value: $[coalesce(variables.WindowsSdkPackageVersionSuffix, '25')]
+- name: _PublishCsWinMD
+ value: $[coalesce(variables.PublishCsWinMD, 'false')]
+- name: _PublishFunctionalTests
+ value: $[coalesce(variables.PublishFunctionalTests, 'false')]
diff --git a/docs/aot-trimming.md b/docs/aot-trimming.md
new file mode 100644
index 000000000..d039ec224
--- /dev/null
+++ b/docs/aot-trimming.md
@@ -0,0 +1,59 @@
+# .NET trimming and AOT support in C#/WinRT
+
+## Overview
+
+As of CsWinRT **2.1.1** and Windows SDK projection 10.0..**38**, generated projections are AOT compatible and various trimming issues have been addressed as part of it. This Windows SDK projection version is included in the .NET SDK starting from the following versions: 6.0.134, 6.0.426, 8.0.109, 8.0.305 or 8.0.403. The `OptIn` mode of this support discussed in this document is added in CsWinRT **2.2.0** and is added for .NET 8 and later consumers.
+
+Projections can choose to mark their projects as [IsAotCompatible](https://learn.microsoft.com/dotnet/core/deploying/native-aot/?tabs=net8plus%2Cwindows#aot-compatibility-analyzers) to run the relevant .NET AOT analyzers on them. Similarly, C# libraries using C#/WinRT authoring support with the new version can also be AOT compatible and can be published for AOT using the [PublishAOT property and running publish](https://learn.microsoft.com/dotnet/core/deploying/native-aot/?tabs=net8plus%2Cwindows#publish-native-aot-using-the-cli).
+
+## Source generator modes
+
+CsWinRT makes use of a source generator as part of making generated projections and uses of it trimming and AOT compatible. The source generator is primarily used to generate vtables for classes defined by the project and for generic types used such that if they were passed across the WinRT ABI, the interfaces that they implement will be projected into WinRT. CsWinRT still has its existing reflection based approach which still works for non AOT and non trimming scenarios but also comes at a runtime cost. There are four different modes which the source generator can run in which is determined by `CsWinRTAotOptimizerEnabled`:
+
+| Mode | Meaning |
+| ----- | ------- |
+| OptIn | Only generates code for attributed types. |
+| Auto | Attempts to detect scenarios that implement interfaces that can be passed across the WinRT ABI boundary and generates code for them. |
+| True* | If running in a project that references WinUI / UWP, then runs in `Auto`. Otherwise runs in `OptIn`. |
+| False | Disables any generation of code and the analyzer. |
+
+In the default mode of `True`, the source generator looks at the type of project to determine whether it runs in `Auto` or `OptIn` mode. For WinUI / UWP projects, it runs in `Auto` mode given they frequently pass things across the WinRT boundary. This is to avoid having to mark every class with the attribute and to avoid listing every generic type used. Eventually the plan is to have the source generator in these scenarios to run as a build task. For projects which have explictly set `CsWinRTAotWarningLevel` to `2`, it also runs in `Auto` mode given the project had explicitly opted in to warnings for scenarios involving built-in interfaces and we want to avoid the changing the current behavior for such projects. These projects can manually still set to `OptIn` if that is what they desire. For all other projects, the source generator runs in `OptIn` mode where when it sees one of the generator attributes used, it will generate code for it to make it trimming and AOT compatible.
+
+The `OptIn` mode attributes still does work in `Auto` mode to allow to use in scenarios where the source generator didn't detect a certain scenario.
+
+## Consuming projected types and being trimming and AOT compatible
+
+If your app or library has non-WinRT classes that implement C#/WinRT projected interfaces, extend C#/WinRT projected classes, or implement WinRT mapped built-in .NET interfaces and are passed across the ABI to other WinRT functions, the class needs to be marked `partial` and if using `OptIn` mode also needs to be have the `WinRT.GeneratedWinRTExposedType` attribute. This enables the source generator distributed with C#/WinRT to add an attribute to the class with the WinRT vtable for it such that it can be looked up later in a way that is both trimming and AOT compatible.
+
+To help with this, there is a `code fixer` that will produce diagnostics when such types are not marked `partial`. This is helpful in `Auto` mode but will also produce diagnostics for types in `OptIn` mode that have the `WinRT.GeneratedWinRTExposedType` attribute but aren't marked `partial`. The severity and scope of the diagnostics are determined by `CsWinRTAotWarningLevel`:
+
+| Level | Meaning |
+| ----- | ------- |
+| 0 | Info diagnostics |
+| 1 | Warnings for types not marked partial that implement C#/WinRT projected interfaces. |
+| 2 | Warnings from level 1 plus warnings for types not marked partial that implement WinRT mapped built-in .NET interfaces. |
+
+In `Auto` mode, it is recommended to set the `CsWinRTAotWarningLevel` to 2 and go through all the warnings and mark such types `partial` or suppress the warning if those types are not passed across the WinRT ABI. By default, if you reference the CsWinRT package, the `CsWinRTAotWarningLevel` is set to 1. If you don't have a CsWinRT package reference, then the .NET default is to set `CsWinRTAotWarningLevel` to 1 if you have marked your binary as AOT compatible either by using `IsAotCompatible` or `PublishAot`. Even though it is only by default enabled as warnings for AOT scenarios, you should manually set it to 1 or 2 if you support trimming and address them.
+
+In `Auto` mode, the source generator also detects other scenarios such as boxing of arrays and instantiations of generic WinRT types for which it generates code that will allow the WinRT vtable for it to be looked up when passed across the WinRT ABI. In `OptIn` mode, for such types (boxed arrays, generic types) that are passed across the WinRT boundary, an assembly attribute `WinRT.GeneratedWinRTExposedExternalType`, can be placed in the project for each type to have the same code generated for it. This will also typically require `AllowUnsafeBlocks` to be enabled for your project as the generated code does make use of unsafe blocks.
+
+In general, this also means that if your app or library has a dependency on another library that uses or returns types falling into one of those above scenarios, that library needs to have also been ran with the updated CsWinRT version for your app or library to be AOT compatible. This is similar to the general .NET requirement where all your dependencies need to be AOT compatible for your app or library to be AOT compatible.
+
+## ICustomPropertyProvider type support on AOT (WinUI binding)
+
+Non source generated WinUI and UWP XAML binding scenarios (i.e not `x:Bind`) such as `DisplayMemberPath` make use of `ICustomPropertyProvider`. The implementation for this was reflection based and therby not AOT safe. CsWinRT provides an AOT safe implementation for this support. To make use of this, any classes which are provided as the source for such scenarios should be made `partial` and marked with the `WinRT.GeneratedBindableCustomProperty` attribute to make the CsWinRT source generator aware that this type is used in such scenarios and it should generate the AOT safe implementation for it. By default, with the empty constructor, the generated implementation will support all public properties. You are able to scope down the supported public properties by manually specfying the property names and indexer types in the constructor. For example: `[GeneratedBindableCustomProperty([nameof(Name), nameof(Value)], [typeof(int)])]`. To help determine the impacted scenarios, we added an exception that gets thrown when you rely on the non AOT safe version of this feature while `PublishAot` is set.
+
+## Known issues when publishing for AOT
+
+There are a couple issues related to our AOT support that are known with the current release which will be addressed in future updates or be fixed in .NET:
+
+1. Collection expressions can not be passed across the WinRT ABI boundary.
+
+2. In WinUI apps, you might run into a race condition which triggers a hang during .NET GC needing to restart the app. This is the result of an exception being thrown unexpectedly during GC due to an invalid handle. This is fixed as of .NET 9 RC2.
+
+## Known issues when publishing for JIT
+
+1. When publishing for ARM64 with ReadyToRun (R2R) enabled while targeting .NET 6, you may see a failed to optimize error. You can workaround this by moving your project to target .NET 8 or by using [PublishReadyToRunExclude](https://learn.microsoft.com/dotnet/core/deploying/ready-to-run#how-is-the-set-of-precompiled-assemblies-chosen) to exclude `WinRT.Runtime.dll` from R2R when building for ARM64.
+
+## Known issues in general
+
diff --git a/docs/authoring.md b/docs/authoring.md
index d37202e22..5509a3864 100644
--- a/docs/authoring.md
+++ b/docs/authoring.md
@@ -15,7 +15,7 @@ It is recommended to use .NET 6 and Visual Studio 2022 for C#/WinRT authoring sc
```xml
- net6.0-windows10.0.19041.0
+ net8.0-windows10.0.19041.0
```
@@ -66,9 +66,9 @@ To make your component available as a NuGet package, it is important to include
target="build\MyAuthoredComponent.targets" />
-
-
@@ -106,8 +106,11 @@ Consuming a C#/WinRT component from a C++/WinRT desktop application is supported
- **Package Reference**: In Visual Studio, right-click on the project in Solution Explorer and click **Manage NuGet packages** to search for and install the component package.
- **Project Reference**: In Visual Studio, right-click on the project in Solution Explorer and click **Add** -> **Reference**. Select the C#/WinRT component project under the **Projects** node.
+ - Note: The authoring project cannot target AnyCPU due to limitations between MSBuild and C++ Projects.
+
- Note: If your authored component is built with C#/WinRT version 1.3.3 or earlier, you also need a file reference to the `*.winmd` generated in the authored component's output directory. To add a file reference, right-click on the project in Solution Explorer and click **Add** -> **Reference**. Select the file from the **Browse** node.
+
#### Manifest Class Registration
Consuming a C#/WinRT component from a C++/WinRT desktop application requires adding manifest class registrations to the application.
diff --git a/docs/embedded.md b/docs/embedded.md
index b0c4f5409..0e72c6578 100644
--- a/docs/embedded.md
+++ b/docs/embedded.md
@@ -47,7 +47,7 @@ Here is an example project file for a library cross-targeting and embedding a C#
- net6.0-windows;net5.0-windows;netstandard2.0
+ net8.0-windows;net5.0-windows;netstandard2.0
x64;x86
diff --git a/docs/structure.md b/docs/structure.md
index d3325d7ad..195017a5d 100644
--- a/docs/structure.md
+++ b/docs/structure.md
@@ -25,7 +25,7 @@ Contains benchmarks written using BenchmarkDotNet to track the performance of sc
## [`src/cswinrt`](../src/cswinrt)
-Contains the sources and `cswinrt.vcxproj` project file for building the C#/WinRT compiler, **cswinrt.exe**. The projection's base library is contained in src/cswinrt/strings/WinRT.cs, which is processed by src/Strings.props to generate string literals contained in the compiler.
+Contains the sources and `cswinrt.vcxproj` project file for building the C#/WinRT compiler, **cswinrt.exe**.
The compiler uses the [WinMD NuGet package](http://aka.ms/winmd/nuget) for parsing [ECMA-335 metadata](http://www.ecma-international.org/publications/standards/Ecma-335.htm) files. The WinMD github repo includes a [winmd.natvis](https://github.com/microsoft/winmd/blob/master/vs/winmd.natvis) script for debugging metadata parsing. A symlink can be used to install the script:
> for /f "tokens=2*" %i in ('reg query "HKCU\Software\Microsoft\Windows\CurrentVersion\Explorer\User Shell Folders" /v Personal ^| findstr Personal') do @for /f "tokens=2" %k in ('"%ProgramFiles(x86)%\Microsoft Visual Studio\Installer\vswhere" -latest ^| findstr catalog_productLineVersion') do @echo %j\Visual Studio %k\Visualizers| for /f "delims=" %l in ('more') do @md "%l" 2>nul & mklink "%l\winmd.natvis" "c:\git\winmd\vs\winmd.natvis"
diff --git a/docs/version-history.md b/docs/version-history.md
index 2f899565c..d0c056199 100644
--- a/docs/version-history.md
+++ b/docs/version-history.md
@@ -18,7 +18,19 @@ Windows SDK package update version refers to the assembly file version for **Mic
| CsWinRT version* | Windows SDK
package update version | .NET SDK version(s) | Windows App SDK version(s) |
|-|-|-|-|
-| 1.6.3 | 25 | TBA | |
+| 2.1.1* | 38 (.NET 6)
41 (.NET 8)
39 (.NET 9) | 8.0.402
8.0.305
8.0.109
6.0.426
6.0.134 | 1.6.0 |
+| 2.0.8 | 34 | 8.0.400
8.0.304
8.0.108
6.0.425
6.0.133 | |
+| 2.0.7 | | | |
+| 2.0.6 | 33 | | |
+| 2.0.5 | | | |
+| 2.0.4 | 31 | 8.0.100
7.0.403
7.0.310
7.0.113
6.0.416
6.0.319
6.0.124 | |
+| 2.0.3 | 29 | 7.0.305
7.0.108
6.0.411
6.0.314
6.0.119 | 1.4.4 |
+| 2.0.2 | | | |
+| 2.0.1 | 28 | 7.0.101
6.0.404
6.0.307
6.0.112 | |
+| 2.0.0* | 27 | 6.0.401
6.0.304
6.0.109 | 1.2.0 |
+| 1.6.5 | | | |
+| 1.6.4 | 26 | 6.0.400
6.0.302
6.0.107 | |
+| 1.6.3 | 25 | 6.0.203
6.0.105
5.0.408
5.0.214 | |
| 1.6.1* | 24 | 6.0.202
6.0.104
5.0.407
5.0.213 | 1.1.0-preview2
0.8.7 |
| 1.5.0 | 23 | 6.0.201
6.0.103
5.0.406
5.0.212 | 1.1.0-preview1
|
| 1.5.0-prerelease.220124.4 | 23-preview| N/A | 0.8.7-preview1
diff --git a/nuget.config b/nuget.config
new file mode 100644
index 000000000..bfbb3e36b
--- /dev/null
+++ b/nuget.config
@@ -0,0 +1,7 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/nuget/Microsoft.Windows.CsWinMD.nuspec b/nuget/Microsoft.Windows.CsWinMD.nuspec
index 19ad426b2..b28502c55 100644
--- a/nuget/Microsoft.Windows.CsWinMD.nuspec
+++ b/nuget/Microsoft.Windows.CsWinMD.nuspec
@@ -27,7 +27,7 @@
-
+
diff --git a/nuget/Microsoft.Windows.CsWinRT.Authoring.targets b/nuget/Microsoft.Windows.CsWinRT.Authoring.targets
index 576405617..ca05e8036 100644
--- a/nuget/Microsoft.Windows.CsWinRT.Authoring.targets
+++ b/nuget/Microsoft.Windows.CsWinRT.Authoring.targets
@@ -7,12 +7,16 @@ Copyright (C) Microsoft Corporation. All rights reserved.
true
-
- true
+
+ true
- WinRT.Host.runtimeconfig.json
+ WinRT.Host.runtimeconfig.json
true
$(GetCopyToOutputDirectoryItemsDependsOn);CsWinRTAuthoring_AddManagedDependencies
+ $(CsWinRTEnableLogging)
+
+
+ false
@@ -27,10 +31,12 @@ Copyright (C) Microsoft Corporation. All rights reserved.
+
+
@@ -40,7 +46,7 @@ Copyright (C) Microsoft Corporation. All rights reserved.
<_CsWinRTRuntimeCopyLocalItemsFromNuGetPackage Include="@(RuntimeCopyLocalItems->HasMetadata('NuGetPackageVersion'))" />
<_CsWinRTDetectedPackages Include="%(_CsWinRTRuntimeCopyLocalItemsFromNuGetPackage.NuGetPackageId)\%(_CsWinRTRuntimeCopyLocalItemsFromNuGetPackage.NuGetPackageVersion)" Condition="@(_CsWinRTRuntimeCopyLocalItemsFromNuGetPackage->Count()) > 0" />
<_CsWinRTDetectedDistinctPackages Include="@(_CsWinRTDetectedPackages->Distinct())" />
-
+
@@ -51,14 +57,16 @@ Copyright (C) Microsoft Corporation. All rights reserved.
$(CsWinRTAuthoringInputs) @(CsWinRTAuthoringDistinctWinMDs->'"%(FullPath)"', ' ')
-
-
+
-
-
+
+
-
+
WinRT.Host.Shim.dll
PreserveNewest
@@ -77,17 +85,23 @@ Copyright (C) Microsoft Corporation. All rights reserved.
-
+
-
+
+ Condition="'%(ReferenceCopyLocalPaths.AssetType)' == 'runtime' or '%(ReferenceCopyLocalPaths.CopyLocal)' == 'true'">
%(ReferenceCopyLocalPaths.DestinationSubDirectory)%(ReferenceCopyLocalPaths.Filename)%(ReferenceCopyLocalPaths.Extension)
PreserveNewest
@@ -101,7 +115,7 @@ Copyright (C) Microsoft Corporation. All rights reserved.
AfterTargets="GenerateBuildRuntimeConfigurationFiles"
BeforeTargets="GetCopyToOutputDirectoryItems;_GetCopyToOutputDirectoryItemsFromThisProject">
-
+
$([System.IO.Path]::GetFileName($(ProjectRuntimeConfigFilePath)))
@@ -136,30 +150,23 @@ Copyright (C) Microsoft Corporation. All rights reserved.
-
-
- %(FullPath)
- PreserveNewest
-
-
-
-
- WinRT.Host.dll
+
+ WinRT.Host.dll
+ $(AssemblyName).dll
true
- %(CsWinRTComponent_ManagedImplementation.TargetPath)
+ $(TargetDir)$(AssemblyName).dll
winmd
true
true
true
-
@@ -233,10 +240,10 @@ Copyright (C) Microsoft Corporation. All rights reserved.
- $([System.String]::Copy('$(Flat_Peeked_Depends1)').Replace('CsWinRT','$(AssemblyName)'))
- $([System.String]::Copy('$(Flat_Peeked_Depends2)').Replace('CsWinRT','$(AssemblyName)'))
- $([System.String]::Copy('$(Flat_Peeked_Target1)').Replace('CsWinRT','$(AssemblyName)'))
- $([System.String]::Copy('$(Flat_Peeked_Target2)').Replace('CsWinRT','$(AssemblyName)'))
+ $([System.String]::Copy('$(Flat_Peeked_Depends1)').Replace('CsWinRT','$(AssemblyName)').Replace(".","_"))
+ $([System.String]::Copy('$(Flat_Peeked_Depends2)').Replace('CsWinRT','$(AssemblyName)').Replace(".","_"))
+ $([System.String]::Copy('$(Flat_Peeked_Target1)').Replace('CsWinRT','$(AssemblyName)').Replace(".","_"))
+ $([System.String]::Copy('$(Flat_Peeked_Target2)').Replace('CsWinRT','$(AssemblyName)').Replace(".","_"))
@@ -250,10 +257,38 @@ Copyright (C) Microsoft Corporation. All rights reserved.
-
+
-
+
+
+ true
+ lib\$(TargetFramework)\winmd
+
+
+
+
+ true
+ lib\$(TargetFramework)\winmd
+
+
+
+
+ true
+ buildTransitive\$(AssemblyName).targets;build\$(AssemblyName).targets
+
+
+
+
+
+
+
true
lib\$(TargetFramework)
@@ -281,24 +316,6 @@ Copyright (C) Microsoft Corporation. All rights reserved.
lib\$(TargetFramework)
-
-
- true
- lib\$(TargetFramework)\winmd
-
-
-
-
- true
- lib\$(TargetFramework)\winmd
-
-
-
-
- true
- buildTransitive\$(AssemblyName).targets;build\$(AssemblyName).targets
-
-
true
diff --git a/nuget/Microsoft.Windows.CsWinRT.BeforeMicrosoftNetSdk.targets b/nuget/Microsoft.Windows.CsWinRT.BeforeMicrosoftNetSdk.targets
new file mode 100644
index 000000000..fa3f7ee3c
--- /dev/null
+++ b/nuget/Microsoft.Windows.CsWinRT.BeforeMicrosoftNetSdk.targets
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+ false
+
+
+
+
+ Shared
+ true
+
+
+ <_CsWinRTEnableStubExeGeneration>true
+
+
+
diff --git a/nuget/Microsoft.Windows.CsWinRT.Embedded.targets b/nuget/Microsoft.Windows.CsWinRT.Embedded.targets
index ff38fa69c..dc61b1898 100644
--- a/nuget/Microsoft.Windows.CsWinRT.Embedded.targets
+++ b/nuget/Microsoft.Windows.CsWinRT.Embedded.targets
@@ -16,7 +16,7 @@ Copyright (C) Microsoft Corporation. All rights reserved.
-
+
@@ -68,8 +68,8 @@ Copyright (C) Microsoft Corporation. All rights reserved.
-
-
+
+
diff --git a/nuget/Microsoft.Windows.CsWinRT.nuspec b/nuget/Microsoft.Windows.CsWinRT.nuspec
index 48cb6f4ee..970304040 100644
--- a/nuget/Microsoft.Windows.CsWinRT.nuspec
+++ b/nuget/Microsoft.Windows.CsWinRT.nuspec
@@ -14,7 +14,8 @@
LICENSE
https://github.com/microsoft/cswinrt
-
+
+
@@ -22,6 +23,7 @@
+
@@ -30,12 +32,15 @@
-
-
+
+
+
+
-
+
+
+
+
-
+
-
+
-
+
-
+
+
+
+
+
diff --git a/nuget/Microsoft.Windows.CsWinRT.props b/nuget/Microsoft.Windows.CsWinRT.props
index e58f26d52..8d743f146 100644
--- a/nuget/Microsoft.Windows.CsWinRT.props
+++ b/nuget/Microsoft.Windows.CsWinRT.props
@@ -8,6 +8,9 @@ Copyright (C) Microsoft Corporation. All rights reserved.
$([MSBuild]::NormalizeDirectory('$(MSBuildThisFileDirectory)', '..'))
$(CsWinRTPath)cswinrt.exe
+
+
+ $(BeforeMicrosoftNETSdkTargets);$(MSBuildThisFileDirectory)Microsoft.Windows.CsWinRT.BeforeMicrosoftNetSdk.targets
diff --git a/nuget/Microsoft.Windows.CsWinRT.targets b/nuget/Microsoft.Windows.CsWinRT.targets
index 1e7f531f3..7e5b43acd 100644
--- a/nuget/Microsoft.Windows.CsWinRT.targets
+++ b/nuget/Microsoft.Windows.CsWinRT.targets
@@ -13,30 +13,147 @@ Copyright (C) Microsoft Corporation. All rights reserved.
false
false
true
+ true
true
- CsWinRTIncludeProjection;CsWinRTRemoveWinMDReferences;$(CoreCompileDependsOn)
- false
+
+
+ ResolveAssemblyReferences;CsWinRTIncludeProjection;CsWinRTRemoveWinMDReferences;$(CoreCompileDependsOn)
+ false
+ true
+
+
+ true
+ false
+
+
+ false
+ false
+
+
+ net5.0
+ net6.0
+ net7.0
+ net8.0
+ netstandard2.0
+ $(WindowsSDKVersion.TrimEnd('\'))
+ $(TargetPlatformVersion)
+
+
+ true
+ false
+
+
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+ false
+ true
+
+
+ $(CSWinRTIncludes)
+ $(CsWinRTIncludesWithFixup.Trim())
+ $([System.Text.RegularExpressions.Regex]::Replace($(CsWinRTIncludesWithFixup), '[\r\n]', ''))
+ true
+
+
+ false
+ true
+
+
+ 0
+ 1
+
+
+
- $(CsWinRTPath)lib\net6.0
+ $(CsWinRTPath)lib\net8.0
$(CsWinRTPath)runtimes\**\native
-
-
-
+
+
+
@@ -57,17 +174,30 @@ Copyright (C) Microsoft Corporation. All rights reserved.
-
+
+
+
+
+ <_CsWinRT_CopyLocalImplementationDlls
+ Include="@(CsWinRTInputs->'%(RootDir)%(Directory)%(DestinationSubDirectory)%(Implementation)')"
+ Condition="'%(CsWinRTInputs.Implementation)'!=''" />
+
+
- $(GeneratedFilesDir)
- $([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)', 'Generated Files'))
+ $(GeneratedFilesDir)\CsWinRT\
+ $([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)', 'Generated Files', 'CsWinRT'))
@@ -79,9 +209,9 @@ Copyright (C) Microsoft Corporation. All rights reserved.
-
+
$(CsWinRTGeneratedFilesDir)cswinrt.rsp
$(CsWinRTGeneratedFilesDir)cswinrt_internal.rsp
@@ -90,8 +220,6 @@ Copyright (C) Microsoft Corporation. All rights reserved.
-->
"$(CsWinRTExe)" %40"$(CsWinRTResponseFile)"
"$(CsWinRTExe)" %40"$(CsWinRTResponseFilePrivateProjection)"
- $(WindowsSDKVersion.TrimEnd('\'))
- $(TargetPlatformVersion)
-input $(CsWinRTWindowsMetadata)
@@ -99,16 +227,16 @@ Copyright (C) Microsoft Corporation. All rights reserved.
-
+
-
+
Windows;Microsoft
Windows;Microsoft
-
-
+
+
@@ -118,7 +246,7 @@ Copyright (C) Microsoft Corporation. All rights reserved.
-
+
@(CsWinRTExcludePrivateItems->'-exclude %(Identity)', '
')
@@ -145,9 +273,8 @@ Copyright (C) Microsoft Corporation. All rights reserved.
-internal
-embedded
-public_enums
- net5.0
- net6.0
- netstandard2.0
+ -public_exclusiveto
+ -idic_exclusiveto
$(CsWinRTCommandVerbosity)
@@ -159,6 +286,8 @@ $(CsWinRTFilters)
$(CsWinRTIncludeWinRTInterop)
$(CsWinRTEmbeddedProjection)
$(CsWinRTEmbeddedEnums)
+$(CsWinRTPublicExclusiveTo)
+$(CsWinRTDynamicallyInterfaceCastableExclusiveTo)
@@ -182,24 +311,35 @@ $(CsWinRTInternalProjection)
false
true
+ 0
+ 0
+
+
+
+
-
-
+
+
+
+
-
+
+
+
-
-
+
+
-
+
+
@@ -210,8 +350,300 @@ $(CsWinRTInternalProjection)
+
+
+
+
+
+ <_StubExeFolderName>StubExe
+ <_StubExeGeneratedFilesDir Condition="'$(_StubExeGeneratedFilesDir)' == '' AND '$(GeneratedFilesDir)' != ''">$(GeneratedFilesDir)\$(_StubExeFolderName)\
+ <_StubExeGeneratedFilesDir Condition="'$(_StubExeGeneratedFilesDir)' == ''">$([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)', '$(IntermediateOutputPath)', 'Generated Files', '$(_StubExeFolderName)'))
+
+
+ <_StubExeNativeLibraryPath>$([System.IO.Path]::Combine($(MSBuildProjectDirectory), $(NativeOutputPath), $(AssemblyName).lib))
+
+
+ <_StubExeSourceFileName>$(AssemblyName).c
+ <_StubExeSourceFilePath>$([System.IO.Path]::Combine($(_StubExeGeneratedFilesDir), $(_StubExeSourceFileName)))
+
+
+ <_StubExeOriginalSourceFilePath>$([System.IO.Path]::Combine($(MSBuildThisFileDirectory), 'sources', 'StubExe.c'))
+
+
+ <_StubExeBinaryFileName>$(AssemblyName).exe
+ <_StubExeBinaryOutputFilePath>$([System.IO.Path]::Combine($(_StubExeGeneratedFilesDir), $(_StubExeBinaryFileName)))
+ <_StubExeBinaryDestinationFilePath>$([System.IO.Path]::Combine($(MSBuildProjectDirectory), $(NativeOutputPath), $(_StubExeBinaryFileName)))
+
+
+ <_StubExePlatform Condition="'$(_StubExePlatform)' == '' AND '$(Platform)' != '' AND '$(Platform)' != 'AnyCPU'">$(Platform)
+ <_StubExePlatform Condition="'$(_StubExePlatform)' == '' AND '$(PlatformTarget)' != ''">$(PlatformTarget)
+
+ <_StubExeWin32ManifestPath Condition="'$(Win32Manifest)' != ''">$([System.IO.Path]::Combine($(MSBuildProjectDirectory), $(Win32Manifest)))
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ClExeFilePath Condition="'$(_ClExeFilePath)' == '' AND '$(CsWinRTUseEnvironmentalTools)' == 'true'">cl
+ <_ClExeFilePath Condition="'$(_ClExeFilePath)' == '' AND '$(_CppToolsDirectory)' != ''">"$([System.IO.Path]::Combine($(_CppToolsDirectory), 'cl.exe'))"
+
+
+
+
+
+
+
+
+
+ <_MsvcCurrentVersionInstallDirectory>$([System.IO.Path]::GetFullPath($([System.IO.Path]::Combine($(_CppToolsDirectory), '..', '..', '..'))))
+ <_MsvcIncludePath>$([System.IO.Path]::Combine($(_MsvcCurrentVersionInstallDirectory), 'include'))
+ <_MsvcLibPath>$([System.IO.Path]::Combine($(_MsvcCurrentVersionInstallDirectory), 'lib', '$(_StubExePlatform)'))
+ <_MsvcLibWildcardPath>$([System.IO.Path]::Combine($(_MsvcLibPath), '*.lib'))
+
+
+ <_WindowsSdk10InstallDirectory Condition="'$(_WindowsSdk10InstallDirectory)' == '' AND '$(WindowsSdkPath)' != ''">$(WindowsSdkPath)
+ <_WindowsSdk10InstallDirectory Condition="'$(_WindowsSdk10InstallDirectory)' == ''">C:\Program Files (x86)\Windows Kits\10
+ <_WindowsSdkIncludePath Condition="'$(TargetPlatformVersion)' != ''">$([System.IO.Path]::Combine($(_WindowsSdk10InstallDirectory), 'Include', '$(TargetPlatformVersion)'))
+ <_WindowsSdk-ucrt-IncludePath>$([System.IO.Path]::Combine($(_WindowsSdkIncludePath), 'ucrt'))
+ <_WindowsSdk-um-IncludePath>$([System.IO.Path]::Combine($(_WindowsSdkIncludePath), 'um'))
+
+
+ <_WindowsSdkLibPath Condition="'$(TargetPlatformVersion)' != ''">$([System.IO.Path]::Combine($(_WindowsSdk10InstallDirectory), 'Lib', '$(TargetPlatformVersion)'))
+ <_WindowsSdk-ucrt-LibPath>$([System.IO.Path]::Combine($(_WindowsSdkLibPath), 'ucrt', '$(_StubExePlatform)'))
+ <_WindowsSdk-um-LibPath>$([System.IO.Path]::Combine($(_WindowsSdkLibPath), 'um', '$(_StubExePlatform)'))
+ <_WindowsSdk-ucrt-LibWildcardPath>$([System.IO.Path]::Combine($(_WindowsSdk-ucrt-LibPath), '*.lib'))
+ <_WindowsSdk-um-LibWildcardPath>$([System.IO.Path]::Combine($(_WindowsSdk-um-LibPath), '*.lib'))
+
+
+
+
+
+
+
+
+
+
+
+
+ <_StubExeMsvcArgs Condition="'$(CsWinRTUseEnvironmentalTools)' != 'true'" Include='"$(_MsvcLibWildcardPath)"' />
+ <_StubExeMsvcArgs Condition="'$(CsWinRTUseEnvironmentalTools)' != 'true'" Include='"$(_WindowsSdk-ucrt-LibWildcardPath)"' />
+ <_StubExeMsvcArgs Condition="'$(CsWinRTUseEnvironmentalTools)' != 'true'" Include='"$(_WindowsSdk-um-LibWildcardPath)"' />
+
+
+ <_StubExeMsvcArgs Include="/nologo" />
+
+
+ <_StubExeMsvcArgs Condition="'$(CsWinRTUseEnvironmentalTools)' != 'true'" Include='/I "$(_MsvcIncludePath)"' />
+ <_StubExeMsvcArgs Condition="'$(CsWinRTUseEnvironmentalTools)' != 'true'" Include='/I "$(_WindowsSdk-ucrt-IncludePath)"' />
+ <_StubExeMsvcArgs Condition="'$(CsWinRTUseEnvironmentalTools)' != 'true'" Include='/I "$(_WindowsSdk-um-IncludePath)"' />
+
+
+ <_StubExeMsvcArgs Condition="'$(Configuration)' == 'Debug'" Include="/MTd" />
+ <_StubExeMsvcArgs Condition="'$(Configuration)' != 'Debug'" Include="/MT" />
+
+
+ <_StubExeMsvcArgs Include="/O2" />
+
+
+ <_StubExeMsvcArgs Include="/link" />
+
+
+ <_StubExeMsvcArgs Include="/NOLOGO" />
+
+
+ <_StubExeMsvcArgs Include="/MANIFEST:NO" Condition="'$(_StubExeWin32ManifestPath)'==''" />
+ <_StubExeMsvcArgs Include="/MANIFEST:EMBED /MANIFESTINPUT:$(_StubExeWin32ManifestPath)" Condition="'$(_StubExeWin32ManifestPath)'!=''" />
+
+
+ <_StubExeMsvcArgs Condition="'$(Configuration)' == 'Debug'" Include="/NODEFAULTLIB:libucrtd.lib" />
+ <_StubExeMsvcArgs Condition="'$(Configuration)' != 'Debug'" Include="/NODEFAULTLIB:libucrt.lib" />
+ <_StubExeMsvcArgs Condition="'$(Configuration)' == 'Debug'" Include="/DEFAULTLIB:ucrtd.lib" />
+ <_StubExeMsvcArgs Condition="'$(Configuration)' != 'Debug'" Include="/DEFAULTLIB:ucrt.lib" />
+
+
+ <_StubExeMsvcArgs Include="/INCREMENTAL:NO" />
+
+
+ <_StubExeMsvcArgs Include="/OPT:ICF" />
+ <_StubExeMsvcArgs Include="/OPT:REF" />
+
+
+ <_StubExeMsvcArgs Condition="'$(UseUwpTools)' == 'true'" Include="/APPCONTAINER" />
+
+
+ <_StubExeMsvcArgs Condition="'$(OutputType)' == 'WinExe'" Include="/SUBSYSTEM:WINDOWS" />
+ <_StubExeMsvcArgs Condition="'$(OutputType)' != 'WinExe'" Include="/SUBSYSTEM:CONSOLE" />
+
+
+ <_StubExeMsvcArgs Include="/ENTRY:wmainCRTStartup" />
+
+
+ <_StubExeMsvcArgs Condition="'$(ControlFlowGuard)' == 'Guard'" Include="/guard:cf" />
+
+
+ <_StubExeMsvcArgs Condition="'$(CETCompat)' != 'false' and '$(_StubExePlatform)' == 'x64'" Include="/CETCOMPAT" />
+ <_StubExeMsvcArgs Condition="'$(CETCompat)' == 'false' and '$(_StubExePlatform)' == 'x64'" Include="/CETCOMPAT:NO" />
+
+
+ <_StubExeMsvcArgs Condition="'$(CETCompat)' != 'false' and '$(_StubExePlatform)' == 'x64' and '$(ControlFlowGuard)' == 'Guard'" Include="/guard:ehcont"/>
+
+
+
+
+ <_StubExeMsvcArgsText>@(_StubExeMsvcArgs, ' ')
+
+
+
+
+
+
+
+ <_StubExeAdditionalPath>$(PATH);$(WindowsSDKBuildToolsBinVersionedArchFolder)
+ <_StubExeEnvVars>PATH=$(_StubExeAdditionalPath.Replace(';','%3B'))
+
+
+
+
+
+
+
+
+
+
+
+
+ $(_StubExeBinaryFileName)
+ PreserveNewest
+
+
+
+
+
+
+ true
+ false
+ true
+ true
+ true
+ true
+ true
+ false
+ false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/nuget/readme.md b/nuget/readme.md
index bdaa5b3c5..cc027eca6 100644
--- a/nuget/readme.md
+++ b/nuget/readme.md
@@ -42,8 +42,17 @@ C#/WinRT behavior can be customized with these project properties:
| CsWinRTEnableLogging | true \| *false | Generates a log.txt file to help with diagnosing issues with generating the metadata file and sources for a C#/WinRT authoring component |
| CsWinRTWindowsMetadata | \ \| "local" \| "sdk" \| *$(WindowsSDKVersion) | Specifies the source for Windows metadata |
| CsWinRTGenerateProjection | *true \| false | Indicates whether to generate and compile projection sources (true), or only to compile them (false) |
+| CsWinRTPrivateProjection | true \| *false | Indicates if a projection based on `CsWinRTIncludesPrivate` and related 'private' properties should be generated as `internal` |
| CsWinRTGeneratedFilesDir | *"$(IntermediateOutputPath)\Generated Files" | Specifies the location for generated project source files |
| CsWinRTIIDOptimizerOptOut | true \| *false | Determines whether to run the IIDOptimizer on the projection assembly |
+| CsWinRTRcwFactoryFallbackGeneratorForceOptIn | true \| *false | Forces the RCW factory fallback generator to be enabled (it only runs on .exe projects by default) |
+| CsWinRTRcwFactoryFallbackGeneratorForceOptOut | true \| *false | Forces the RCW factory fallback generator to be disabled (overrides "ForceOptIn" as well) |
+| CsWinRTMergeReferencedActivationFactories | true \| *false | Makes the native `DllGetActivationFactory` exported function for AOT scenarios also forward the activation call to all referenced WinRT components, allowing them to all be merged into a single executable or shared library |
+| CsWinRTEnableManifestFreeActivation | *true \| false | Enables the manifest-free WinRT activation path as fallback when `RoGetActivationFactory` fails to resolve a WinRT type |
+| CsWinRTManifestFreeActivationReportOriginalException | true \| *false | If 'CsWinRTEnableManifestFreeActivation' is set and activating a type fails, ensures the original exception is thrown, rather than 'NotSupportedException' |
+| CsWinRTUseEnvironmentalTools | *true \| false | Makes the invocation of MSVC to produce a stub .exe (for Native AOT scenarios) rely on environmental tools, rather than looking up the MSVC install path. This is enabled by default when running in a Visual Studio Developer Command Prompt (or PowerShell), and false otherwise. |
+| CsWinRTAotOptimizerEnabled | Auto \| OptIn \| *true \| false | Enables the C#/WinRT AOT optimizer. WinRT types can opt-in to AOT optimization by adding the `[GeneratedWinRTExposedType]` attribute. More details can be found [here](https://github.com/microsoft/CsWinRT/blob/master/docs/aot-trimming.md#source-generator-modes) |
+| CsWinRTAotWarningLevel | 0 \| *1 \| 2 \| 3 | Specifies the warning level used by the code fixer for trimming and AOT compat (0 - no warnings, 1 - warnings for scenarios involving non built-in types, 2 - warnings for all scenarios, 3 - for more extensive warnings for trim/AOT, including noisy ones) |
\*Default value
**If CsWinRTFilters is not defined, the following effective value is used:
@@ -57,6 +66,21 @@ C#/WinRT behavior can be customized with these project properties:
* -output $(CsWinRTGeneratedFilesDir)
* $(CsWinRTFilters)
+## Advanced size saving options
+
+C#/WinRT supports several feature switches to opt-out of some features available by default, like projection of built-in .NET types. These features are often required by applications, but might be unnecessary in scenarios such as minimal WinRT components authoring. In those cases, it's possible to use these properties to opt-out of any of these features, which can provide binary size savings and performance improvements.
+
+| Property | Value(s) | Description |
+|-|-|-|
+| CsWinRTEnableDynamicObjectsSupport | \*true \| false | Enables support for marshalling WinRT types to dynamic objects (ie. `ExpandoObject`). Setting this to **false** allows all of `System.Linq.Expressions` to be trimmed out. |
+| CsWinRTUseExceptionResourceKeys | true \| \*false | Uses minimal, non-localized exception messages to save binary size. Setting this to **true** allows stripping all of the localization .resw files that are bundled as embedded resources into C#/WinRT. |
+| CsWinRTEnableDefaultCustomTypeMappings | \*true \| false | Enables all available custom type mappings of built-in .NET types by default (eg. `INotifyPropertyChanged`). Setting this to **false** allows trimming all code requires to support those features. If you only want to opt-in into a specific subset of built-in type mappings, you can enable just the ones you need via the `WinRT.Projections.Register*Mapping()` methods. Note that some type mappings will also rely on other configurable features in order to work. For instance, the type mappings for nullable primitive types will not work correctly if the `CsWinRTEnableIReferenceSupport` feature switch is set to **false**. |
+| CsWinRTEnableICustomPropertyProviderSupport | \*true \| false | Enables marshalling of `ICustomPropertyProvider` objects. Setting this to **false** allows trimming all supporting code for this interface, and will make it not available on marshalled CCW types. |
+| CsWinRTEnableIReferenceSupport | \*true \| false | Enables support and marshalling of `IReference`, `IReferenceArray` and `IPropertyValue` objects. Setting this to **false** allows trimming all supporting code for all three interfaces, and will make all of them not available on marshalled CCW types. |
+| CsWinRTEnableIDynamicInterfaceCastableSupport | \*true \| false | Enables support for the [`IDynamicInterfaceCastable`](https://devblogs.microsoft.com/dotnet/improvements-in-native-code-interop-in-net-5-0/#idynamicinterfacecastable) infrastructure for RCW types. Setting this to **false** allows trimming of all related code, and will disallow casting RCW types to interface types they don't directly implement in metadata. |
+| CsWinRTLoadComponentsInDefaultALC | true \| \*false | Setting this to **true** configures `WinRT.Host.dll` to load CsWinRT components in the default [`Assembly Load Context (ALC)`](https://learn.microsoft.com/dotnet/core/dependency-loading/understanding-assemblyloadcontext). Note you should ensure components and dependencies loaded are compatible and will not conflict in any way to avoid potential issues. By default, when set to **false**, they load in their own ALC. |
+\*Default value
+
### Windows Metadata
Windows Metadata is required for all C#/WinRT projections, and can be supplied by:
diff --git a/nuget/sources/StubExe.c b/nuget/sources/StubExe.c
new file mode 100644
index 000000000..e02bc6c36
--- /dev/null
+++ b/nuget/sources/StubExe.c
@@ -0,0 +1,13 @@
+#include
+
+// Declare the import for '__managed__Main', which is a special export produced by the
+// Native AOT runtime to allow jumping into the real 'Main' of an application. This is
+// exported automatically when the 'CustomNativeMain' property is set to 'true'.
+__declspec(dllimport)
+int __stdcall __managed__Main(int argc, wchar_t** argv);
+
+// Our entry point is simply a direct jump into the entry point from Native AOT
+int wmain(int argc, wchar_t** argv)
+{
+ return __managed__Main(argc, argv);
+}
\ No newline at end of file
diff --git a/src/Authoring/Directory.Build.props b/src/Authoring/Directory.Build.props
index 1b421ed0d..0be7ce8a3 100644
--- a/src/Authoring/Directory.Build.props
+++ b/src/Authoring/Directory.Build.props
@@ -2,4 +2,9 @@
+
+ true
+ True
+
+
diff --git a/src/Authoring/WinRT.Host.Shim/Module.cs b/src/Authoring/WinRT.Host.Shim/Module.cs
index 1e2a4039a..a896b1c37 100644
--- a/src/Authoring/WinRT.Host.Shim/Module.cs
+++ b/src/Authoring/WinRT.Host.Shim/Module.cs
@@ -1,122 +1,182 @@
-// TODO: consider embedding this as a resource into WinRT.Host.dll,
-// to simplify deployment
-
-using System;
+// TODO: consider embedding this as a resource into WinRT.Host.dll,
+// to simplify deployment
+
+using System;
using System.Collections.Concurrent;
-using System.Reflection;
-
-#if NET
-using System.Runtime.Loader;
-[assembly: global::System.Runtime.Versioning.SupportedOSPlatform("Windows")]
-#endif
-
-namespace WinRT.Host
-{
- public static class Shim
- {
- private const int S_OK = 0;
- private const int E_NOINTERFACE = unchecked((int)0x80004002);
- private const int REGDB_E_READREGDB = unchecked((int)0x80040150);
- private const int REGDB_E_CLASSNOTREG = unchecked((int)0x80040154);
-
- public unsafe delegate int GetActivationFactoryDelegate(IntPtr hstrTargetAssembly, IntPtr hstrRuntimeClassId, IntPtr* activationFactory);
-
- public static unsafe int GetActivationFactory(IntPtr hstrTargetAssembly, IntPtr hstrRuntimeClassId, IntPtr* activationFactory)
- {
- *activationFactory = IntPtr.Zero;
-
- var targetAssembly = MarshalString.FromAbi(hstrTargetAssembly);
- var runtimeClassId = MarshalString.FromAbi(hstrRuntimeClassId);
-
- try
- {
- var assembly = ActivationLoader.LoadAssembly(targetAssembly);
- var type = assembly.GetType("WinRT.Module");
- if (type == null)
- {
- return REGDB_E_CLASSNOTREG;
- }
- var GetActivationFactory = type.GetMethod("GetActivationFactory");
- if (GetActivationFactory == null)
- {
- return REGDB_E_READREGDB;
- }
- IntPtr factory = (IntPtr)GetActivationFactory.Invoke(null, new object[] { runtimeClassId });
- if (factory == IntPtr.Zero)
- {
- return E_NOINTERFACE;
- }
- *activationFactory = factory;
- return S_OK;
- }
- catch (Exception e)
- {
- global::WinRT.ExceptionHelpers.SetErrorInfo(e);
- return global::WinRT.ExceptionHelpers.GetHRForException(e);
- }
- }
-
-#if !NET
- private static class ActivationLoader
- {
- public static Assembly LoadAssembly(string targetAssembly) => Assembly.LoadFrom(targetAssembly);
- }
-#else
- private class ActivationLoader : AssemblyLoadContext
+using System.Collections.Generic;
+using System.Reflection;
+
+#if NET
+using System.Runtime.Loader;
+using System.Threading;
+[assembly: global::System.Runtime.Versioning.SupportedOSPlatform("Windows")]
+#endif
+
+namespace WinRT.Host
+{
+ public static class Shim
+ {
+ private const int S_OK = 0;
+ private const int E_NOINTERFACE = unchecked((int)0x80004002);
+ private const int REGDB_E_READREGDB = unchecked((int)0x80040150);
+ private const int CLASS_E_CLASSNOTAVAILABLE = unchecked((int)(0x80040111));
+
+ public unsafe delegate int GetActivationFactoryDelegate(IntPtr hstrTargetAssembly, IntPtr hstrRuntimeClassId, IntPtr* activationFactory);
+
+#if NET
+ private const string UseLoadComponentsInDefaultALCPropertyName = "CSWINRT_LOAD_COMPONENTS_IN_DEFAULT_ALC";
+ private readonly static bool _IsLoadInDefaultContext = IsLoadInDefaultContext();
+
+ private static HashSet _InitializedResolversInDefaultContext = null;
+
+ public static Assembly LoadInDefaultContext(string targetAssembly)
{
- private static readonly ConcurrentDictionary ALCMapping = new ConcurrentDictionary(StringComparer.Ordinal);
- private AssemblyDependencyResolver _resolver;
-
- public static Assembly LoadAssembly(string targetAssembly)
- {
- return ALCMapping.GetOrAdd(targetAssembly, (_) => new ActivationLoader(targetAssembly))
- .LoadFromAssemblyPath(targetAssembly);
- }
-
- private ActivationLoader(string path)
- {
- _resolver = new AssemblyDependencyResolver(path);
- AssemblyLoadContext.Default.Resolving += (AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName) =>
- {
- // Consolidate all WinRT.Runtime loads to the default ALC, or failing that, the first shim ALC
- if (string.CompareOrdinal(assemblyName.Name, "WinRT.Runtime") == 0)
- {
- string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
- if (assemblyPath != null)
- {
- return LoadFromAssemblyPath(assemblyPath);
- }
- }
- return null;
- };
- }
-
- protected override Assembly Load(AssemblyName assemblyName)
- {
- if (string.CompareOrdinal(assemblyName.Name, "WinRT.Runtime") != 0)
- {
- string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
- if (assemblyPath != null)
- {
- return LoadFromAssemblyPath(assemblyPath);
- }
- }
-
- return null;
- }
-
- protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
- {
- string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
- if (libraryPath != null)
- {
- return LoadUnmanagedDllFromPath(libraryPath);
- }
-
- return IntPtr.Zero;
- }
- }
-#endif
- }
-}
-
+ if (_InitializedResolversInDefaultContext == null)
+ {
+ Interlocked.CompareExchange(ref _InitializedResolversInDefaultContext, new HashSet(StringComparer.OrdinalIgnoreCase), null);
+ }
+
+ lock (_InitializedResolversInDefaultContext)
+ {
+ if (!_InitializedResolversInDefaultContext.Contains(targetAssembly))
+ {
+ var resolver = new AssemblyDependencyResolver(targetAssembly);
+ AssemblyLoadContext.Default.Resolving += (AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName) =>
+ {
+ string assemblyPath = resolver.ResolveAssemblyToPath(assemblyName);
+ if (assemblyPath != null)
+ {
+ return assemblyLoadContext.LoadFromAssemblyPath(assemblyPath);
+ }
+ return null;
+ };
+
+ _InitializedResolversInDefaultContext.Add(targetAssembly);
+ }
+ }
+
+ return AssemblyLoadContext.Default.LoadFromAssemblyPath(targetAssembly);
+ }
+
+ public static bool IsLoadInDefaultContext()
+ {
+ if (AppContext.TryGetSwitch(UseLoadComponentsInDefaultALCPropertyName, out bool isEnabled))
+ {
+ return isEnabled;
+ }
+
+ return false;
+ }
+#endif
+
+ public static unsafe int GetActivationFactory(IntPtr hstrTargetAssembly, IntPtr hstrRuntimeClassId, IntPtr* activationFactory)
+ {
+ *activationFactory = IntPtr.Zero;
+
+ var targetAssembly = MarshalString.FromAbi(hstrTargetAssembly);
+ var runtimeClassId = MarshalString.FromAbi(hstrRuntimeClassId);
+
+ try
+ {
+#if NET
+ Assembly assembly;
+ if (_IsLoadInDefaultContext)
+ {
+ assembly = LoadInDefaultContext(targetAssembly);
+ }
+ else
+ {
+ assembly = ActivationLoader.LoadAssembly(targetAssembly);
+ }
+#else
+ var assembly = ActivationLoader.LoadAssembly(targetAssembly);
+#endif
+ var type = assembly.GetType("WinRT.Module");
+ if (type == null)
+ {
+ return REGDB_E_READREGDB;
+ }
+ var GetActivationFactory = type.GetMethod("GetActivationFactory", new Type[] { typeof(string) });
+ if (GetActivationFactory == null)
+ {
+ return REGDB_E_READREGDB;
+ }
+ IntPtr factory = (IntPtr)GetActivationFactory.Invoke(null, new object[] { runtimeClassId });
+ if (factory == IntPtr.Zero)
+ {
+ return CLASS_E_CLASSNOTAVAILABLE;
+ }
+ *activationFactory = factory;
+ return S_OK;
+ }
+ catch (Exception e)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(e);
+ return global::WinRT.ExceptionHelpers.GetHRForException(e);
+ }
+ }
+
+#if !NET
+ private static class ActivationLoader
+ {
+ public static Assembly LoadAssembly(string targetAssembly) => Assembly.LoadFrom(targetAssembly);
+ }
+#else
+ private class ActivationLoader : AssemblyLoadContext
+ {
+ private static readonly ConcurrentDictionary ALCMapping = new ConcurrentDictionary(StringComparer.Ordinal);
+ private AssemblyDependencyResolver _resolver;
+
+ public static Assembly LoadAssembly(string targetAssembly)
+ {
+ return ALCMapping.GetOrAdd(targetAssembly, (_) => new ActivationLoader(targetAssembly))
+ .LoadFromAssemblyPath(targetAssembly);
+ }
+
+ private ActivationLoader(string path)
+ {
+ _resolver = new AssemblyDependencyResolver(path);
+ AssemblyLoadContext.Default.Resolving += (AssemblyLoadContext assemblyLoadContext, AssemblyName assemblyName) =>
+ {
+ // Consolidate all WinRT.Runtime loads to the default ALC, or failing that, the first shim ALC
+ if (string.CompareOrdinal(assemblyName.Name, "WinRT.Runtime") == 0)
+ {
+ string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
+ if (assemblyPath != null)
+ {
+ return LoadFromAssemblyPath(assemblyPath);
+ }
+ }
+ return null;
+ };
+ }
+
+ protected override Assembly Load(AssemblyName assemblyName)
+ {
+ if (string.CompareOrdinal(assemblyName.Name, "WinRT.Runtime") != 0)
+ {
+ string assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
+ if (assemblyPath != null)
+ {
+ return LoadFromAssemblyPath(assemblyPath);
+ }
+ }
+
+ return null;
+ }
+
+ protected override IntPtr LoadUnmanagedDll(string unmanagedDllName)
+ {
+ string libraryPath = _resolver.ResolveUnmanagedDllToPath(unmanagedDllName);
+ if (libraryPath != null)
+ {
+ return LoadUnmanagedDllFromPath(libraryPath);
+ }
+
+ return IntPtr.Zero;
+ }
+ }
+#endif
+ }
+}
+
diff --git a/src/Authoring/WinRT.Host.Shim/WinRT.Host.Shim.csproj b/src/Authoring/WinRT.Host.Shim/WinRT.Host.Shim.csproj
index fb24df655..25fb7c1c1 100644
--- a/src/Authoring/WinRT.Host.Shim/WinRT.Host.Shim.csproj
+++ b/src/Authoring/WinRT.Host.Shim/WinRT.Host.Shim.csproj
@@ -1,7 +1,7 @@
- netstandard2.0;net6.0
+ netstandard2.0;net8.0
diff --git a/src/Authoring/WinRT.Host/NetHostDir.csproj b/src/Authoring/WinRT.Host/NetHostDir.csproj
index 341d68e52..9b566979e 100644
--- a/src/Authoring/WinRT.Host/NetHostDir.csproj
+++ b/src/Authoring/WinRT.Host/NetHostDir.csproj
@@ -1,7 +1,7 @@
- net6.0
+ net8.0
diff --git a/src/Authoring/WinRT.Host/WinRT.Host.vcxproj b/src/Authoring/WinRT.Host/WinRT.Host.vcxproj
index 2dd3c14d0..af07a3e52 100644
--- a/src/Authoring/WinRT.Host/WinRT.Host.vcxproj
+++ b/src/Authoring/WinRT.Host/WinRT.Host.vcxproj
@@ -1,6 +1,6 @@
-
+
Debug
@@ -39,7 +39,7 @@
v143
v142
Unicode
-
+
true
@@ -285,14 +285,14 @@
-
+
This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.
-
-
+
+
\ No newline at end of file
diff --git a/src/Authoring/WinRT.Host/packages.config b/src/Authoring/WinRT.Host/packages.config
index 81f107b8b..f229371b3 100644
--- a/src/Authoring/WinRT.Host/packages.config
+++ b/src/Authoring/WinRT.Host/packages.config
@@ -1,4 +1,4 @@
-
-
-
+
+
+
\ No newline at end of file
diff --git a/src/Authoring/WinRT.SourceGenerator.Roslyn4080/WinRT.SourceGenerator.Roslyn4080.csproj b/src/Authoring/WinRT.SourceGenerator.Roslyn4080/WinRT.SourceGenerator.Roslyn4080.csproj
new file mode 100644
index 000000000..d0b0785bb
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator.Roslyn4080/WinRT.SourceGenerator.Roslyn4080.csproj
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/Authoring/WinRT.SourceGenerator.Roslyn4120/WinRT.SourceGenerator.Roslyn4120.csproj b/src/Authoring/WinRT.SourceGenerator.Roslyn4120/WinRT.SourceGenerator.Roslyn4120.csproj
new file mode 100644
index 000000000..d0b0785bb
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator.Roslyn4120/WinRT.SourceGenerator.Roslyn4120.csproj
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Shipped.md b/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Shipped.md
index 2c40a2ed2..5d237735c 100644
--- a/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Shipped.md
+++ b/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Shipped.md
@@ -1,35 +1,67 @@
-; Shipped analyzer releases
-; https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
-
-## Release 1.1.1
-
-### New Rules
-Rule ID | Category | Severity | Notes
---------|----------|----------|-------
-CsWinRT1000 | Usage | Error | Property should have public `get` method
-CsWinRT1001 | Usage | Error | Namespaces should match the assembly namespace or be child namespaces of the assembly namespace
-CsWinRT1002 | Usage | Error | Namespaces cannot differ only by case
-CsWinRT1003 | Usage | Error | Component must have at least on public type
-CsWinRT1004 | Usage | Error | Public types cannot be generic
-CsWinRT1005 | Usage | Error | Classes exposed to CsWinRT should be sealed
-CsWinRT1006 | Usage | Error | Do not expose unsupported type
-CsWinRT1007 | Usage | Error | Structs should contain at least one public field
-CsWinRT1008 | Usage | Error | Interfaces should not inherit interfaces that are not valid in Windows Runtime
-CsWinRT1009 | Usage | Error | Class should not have multiple constructors that take the same amount of parameters
-CsWinRT1010 | Usage | Error | Methods should not use parameter names that conflict with generated parameter names
-CsWinRT1011 | Usage | Error | Structs should not have private fields
-CsWinRT1012 | Usage | Error | Structs should not have a constant field
-CsWinRT1013 | Usage | Error | Structs should only contain basic types or other structs
-CsWinRT1014 | Usage | Error | Types should not overload an operator
-CsWinRT1015 | Usage | Error | Do not use `DefaultOverloadAttribute` more than once for a set of overloads
-CsWinRT1016 | Usage | Error | Exactly one overload should be marked as DefaultOverload
-CsWinRT1017 | Usage | Error | Array types should be one dimensional, not jagged
-CsWinRT1018 | Usage | Error | Array types should be one dimensional
-CsWinRT1020 | Usage | Error | Do not pass parameters by `ref`
-CsWinRT1021 | Usage | Error | Array parameters should not be marked `InAttribute` or `OutAttribute`
-CsWinRT1022 | Usage | Error | Parameters should not be marked `InAttribute` or `OutAttribute`
-CsWinRT1023 | Usage | Error | Array parameters should not be marked both `ReadOnlyArrayAttribute` and `WriteOnlyArrayAttribute`
-CsWinRT1024 | Usage | Error | Array parameter marked `out` should not be declared `ReadOnlyArrayAttribute`
-CsWinRT1025 | Usage | Error | Array parameter should be marked either `ReadOnlyArrayAttribute` or `WriteOnlyArrayAttribute`
-CsWinRT1026 | Usage | Error | Non-array parameter should not be marked `ReadOnlyArrayAttribute` or `WriteOnlyArrayAttribute`
-CsWinRT1027 | Usage | Error | Class incorrectly implements an interface
\ No newline at end of file
+; Shipped analyzer releases
+; https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
+
+## Release 1.1.1
+
+### New Rules
+Rule ID | Category | Severity | Notes
+--------|----------|----------|-------
+CsWinRT1000 | Usage | Error | Property should have public `get` method
+CsWinRT1001 | Usage | Error | Namespaces should match the assembly namespace or be child namespaces of the assembly namespace
+CsWinRT1002 | Usage | Error | Namespaces cannot differ only by case
+CsWinRT1003 | Usage | Error | Component must have at least on public type
+CsWinRT1004 | Usage | Error | Public types cannot be generic
+CsWinRT1005 | Usage | Error | Classes exposed to CsWinRT should be sealed
+CsWinRT1006 | Usage | Error | Do not expose unsupported type
+CsWinRT1007 | Usage | Error | Structs should contain at least one public field
+CsWinRT1008 | Usage | Error | Interfaces should not inherit interfaces that are not valid in Windows Runtime
+CsWinRT1009 | Usage | Error | Class should not have multiple constructors that take the same amount of parameters
+CsWinRT1010 | Usage | Error | Methods should not use parameter names that conflict with generated parameter names
+CsWinRT1011 | Usage | Error | Structs should not have private fields
+CsWinRT1012 | Usage | Error | Structs should not have a constant field
+CsWinRT1013 | Usage | Error | Structs should only contain basic types or other structs
+CsWinRT1014 | Usage | Error | Types should not overload an operator
+CsWinRT1015 | Usage | Error | Do not use `DefaultOverloadAttribute` more than once for a set of overloads
+CsWinRT1016 | Usage | Error | Exactly one overload should be marked as DefaultOverload
+CsWinRT1017 | Usage | Error | Array types should be one dimensional, not jagged
+CsWinRT1018 | Usage | Error | Array types should be one dimensional
+CsWinRT1020 | Usage | Error | Do not pass parameters by `ref`
+CsWinRT1021 | Usage | Error | Array parameters should not be marked `InAttribute` or `OutAttribute`
+CsWinRT1022 | Usage | Error | Parameters should not be marked `InAttribute` or `OutAttribute`
+CsWinRT1023 | Usage | Error | Array parameters should not be marked both `ReadOnlyArrayAttribute` and `WriteOnlyArrayAttribute`
+CsWinRT1024 | Usage | Error | Array parameter marked `out` should not be declared `ReadOnlyArrayAttribute`
+CsWinRT1025 | Usage | Error | Array parameter should be marked either `ReadOnlyArrayAttribute` or `WriteOnlyArrayAttribute`
+CsWinRT1026 | Usage | Error | Non-array parameter should not be marked `ReadOnlyArrayAttribute` or `WriteOnlyArrayAttribute`
+CsWinRT1027 | Usage | Error | Class incorrectly implements an interface
+
+## Release 2.1.0
+
+### New Rules
+Rule ID | Category | Severity | Notes
+--------|----------|----------|-------
+CsWinRT1028 | Usage | Warning | Class should be marked partial
+CsWinRT1029 | Usage | Warning | Class implements WinRT interfaces generated using an older version of CsWinRT.
+
+## Release 2.1.2
+
+### New Rules
+Rule ID | Category | Severity | Notes
+--------|----------|----------|-------
+CsWinRT1030 | Usage | Warning | Project needs to be updated with 'true' to allow generic interface code generation.
+
+## Release 2.2.1
+
+### New Rules
+Rule ID | Category | Severity | Notes
+--------|----------|----------|-------
+CsWinRT1031 | Usage | Error | Source generator failed.
+
+## Release 2.3.0
+
+### New Rules
+Rule ID | Category | Severity | Notes
+--------|----------|----------|-------
+CsWinRT1032 | Usage | Warning | Collection expressions can only be used when statically verifiable for AOT support with WinRT.
+CsWinRT1033 | Usage | Warning | Casts to '[ComImport]' interface types are not supported with WinRT objects in AOT scenarios.
+CsWinRT1034 | Usage | Warning | Casts to WinRT runtime classes are not trim-safe.
+CsWinRT1035 | Usage | Warning | Casts to WinRT 'IReference`1' unboxed values are not trim-safe.
\ No newline at end of file
diff --git a/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Unshipped.md b/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Unshipped.md
index 7ca1472e8..6640189c3 100644
--- a/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Unshipped.md
+++ b/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Unshipped.md
@@ -1,2 +1,6 @@
-; Unshipped analyzer release
-; https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
\ No newline at end of file
+; Unshipped analyzer release
+; https://github.com/dotnet/roslyn-analyzers/blob/master/src/Microsoft.CodeAnalysis.Analyzers/ReleaseTrackingAnalyzers.Help.md
+
+### New Rules
+Rule ID | Category | Severity | Notes
+--------|----------|----------|-------
diff --git a/src/Authoring/WinRT.SourceGenerator/Analyzers/CollectionExpressionAnalyzer.cs b/src/Authoring/WinRT.SourceGenerator/Analyzers/CollectionExpressionAnalyzer.cs
new file mode 100644
index 000000000..939fbd6de
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator/Analyzers/CollectionExpressionAnalyzer.cs
@@ -0,0 +1,83 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#if ROSLYN_4_12_0_OR_GREATER
+
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis;
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis.Operations;
+using WinRT.SourceGenerator;
+
+#nullable enable
+
+namespace Generator;
+
+///
+/// A diagnostic analyzer to warn for collection expression that are not AOT compatible in WinRT scenarios.
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public sealed class CollectionExpressionAnalyzer : DiagnosticAnalyzer
+{
+ ///
+ public override ImmutableArray SupportedDiagnostics { get; } = [WinRTRules.NonEmptyCollectionExpressionTargetingNonBuilderInterfaceType];
+
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
+ context.EnableConcurrentExecution();
+
+ context.RegisterCompilationStartAction(static context =>
+ {
+ // We only need to emit warnings if CsWinRT is in 'auto' mode
+ if (!GeneratorExecutionContextHelper.IsCsWinRTAotOptimizerInAutoMode(context.Options.AnalyzerConfigOptionsProvider, context.Compilation))
+ {
+ return;
+ }
+
+ // Get the symbols for '[CollectionBuilder]', we need them for lookups. Note that we cannot just
+ // use 'GetTypeByMetadataName' here, as it's possible for the attribute to exist across multiple
+ // assemblies. This is the case if any referenced assemblies is using polyfills due to targeting
+ // an older TFM that does not have the attribute. We still want to work correctly in those cases.
+ // We can just use an array here, since in the vast majority of cases we only expect 1-2 items.
+ ImmutableArray collectionBuilderSymbols = context.Compilation.GetTypesByMetadataName("System.Runtime.CompilerServices.CollectionBuilderAttribute");
+
+ context.RegisterOperationAction(context =>
+ {
+ ICollectionExpressionOperation operation = (ICollectionExpressionOperation)context.Operation;
+
+ // We only possibly warn if the target type is a generic interface type
+ if (operation.Type is not INamedTypeSymbol { TypeKind: TypeKind.Interface, IsGenericType: true } typeSymbol)
+ {
+ return;
+ }
+
+ // We can also skip all cases where the collection expression is empty, those are fine
+ if (operation.Elements.IsEmpty)
+ {
+ return;
+ }
+
+ // We can skip 'ICollection' and 'IList', as those guarantee to use 'List'
+ if (typeSymbol.ConstructedFrom.SpecialType is
+ SpecialType.System_Collections_Generic_ICollection_T or
+ SpecialType.System_Collections_Generic_IList_T)
+ {
+ return;
+ }
+
+ // If the target interface type doesn't have '[CollectionBuilder]' on it, we should warn
+ if (!GeneratorHelper.HasAttributeWithAnyType(typeSymbol, collectionBuilderSymbols))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ WinRTRules.NonEmptyCollectionExpressionTargetingNonBuilderInterfaceType,
+ operation.Syntax.GetLocation(),
+ typeSymbol));
+ }
+ }, OperationKind.CollectionExpression);
+ });
+ }
+}
+
+#endif
diff --git a/src/Authoring/WinRT.SourceGenerator/Analyzers/ComImportInterfaceAnalyzer.cs b/src/Authoring/WinRT.SourceGenerator/Analyzers/ComImportInterfaceAnalyzer.cs
new file mode 100644
index 000000000..db1cc5c69
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator/Analyzers/ComImportInterfaceAnalyzer.cs
@@ -0,0 +1,118 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#if ROSLYN_4_12_0_OR_GREATER
+
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis;
+using System.Collections.Immutable;
+using Microsoft.CodeAnalysis.Operations;
+using WinRT.SourceGenerator;
+
+#nullable enable
+
+namespace Generator;
+
+///
+/// A diagnostic analyzer to warn for casts to interfaces.
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public sealed class ComImportInterfaceAnalyzer : DiagnosticAnalyzer
+{
+ ///
+ public override ImmutableArray SupportedDiagnostics { get; } = [WinRTRules.ComImportInterfaceCast];
+
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
+ context.EnableConcurrentExecution();
+
+ context.RegisterCompilationStartAction(static context =>
+ {
+ // We only need to emit warnings if CsWinRT is in 'auto' mode (same as the collection expressions analyzer), and if the AOT analyzer is enabled.
+ // This is because built-in COM is supported just fine when that is not the case, so no need to warn unless we need to be AOT compatible.
+ if (!GeneratorExecutionContextHelper.IsCsWinRTAotOptimizerInAutoMode(context.Options.AnalyzerConfigOptionsProvider, context.Compilation) ||
+ !GeneratorExecutionContextHelper.GetEnableAotAnalyzer(context.Options.AnalyzerConfigOptionsProvider))
+ {
+ return;
+ }
+
+ // This handles the following cases:
+ //
+ // IC c1 = (IC)obj;
+ // IC c2 = obj as IC;
+ context.RegisterOperationAction(context =>
+ {
+ if (context.Operation is IConversionOperation { Type: INamedTypeSymbol { TypeKind: TypeKind.Interface, IsComImport: true } interfaceType })
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ WinRTRules.ComImportInterfaceCast,
+ context.Operation.Syntax.GetLocation(),
+ interfaceType));
+ }
+ }, OperationKind.Conversion);
+
+ // This handles the following cases:
+ //
+ // if (obj is IC)
+ // {
+ // }
+ context.RegisterOperationAction(context =>
+ {
+ if (context.Operation is IIsTypeOperation { TypeOperand: INamedTypeSymbol { TypeKind: TypeKind.Interface, IsComImport: true } interfaceType })
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ WinRTRules.ComImportInterfaceCast,
+ context.Operation.Syntax.GetLocation(),
+ interfaceType));
+ }
+ }, OperationKind.IsType);
+
+ // This handles the following cases:
+ //
+ // if (obj is IC ic)
+ // {
+ // }
+ //
+ // List patterns are also handled:
+ //
+ // if (items is [IC ic, ..])
+ // {
+ // }
+ context.RegisterOperationAction(context =>
+ {
+ if (context.Operation is IDeclarationPatternOperation { MatchedType: INamedTypeSymbol { TypeKind: TypeKind.Interface, IsComImport: true } interfaceType })
+ {
+ // Adjust the location for 'obj is IC ic' patterns, to include the 'is' expression as well
+ Location location = context.Operation.Parent is IIsPatternOperation isPatternOperation
+ ? isPatternOperation.Syntax.GetLocation()
+ : context.Operation.Syntax.GetLocation();
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ WinRTRules.ComImportInterfaceCast,
+ location,
+ interfaceType));
+ }
+ }, OperationKind.DeclarationPattern);
+
+ // This handles the following cases:
+ //
+ // if (items is [IC, ..])
+ // {
+ // }
+ context.RegisterOperationAction(context =>
+ {
+ if (context.Operation is ITypePatternOperation { MatchedType: INamedTypeSymbol { TypeKind: TypeKind.Interface, IsComImport: true } interfaceType })
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ WinRTRules.ComImportInterfaceCast,
+ context.Operation.Syntax.GetLocation(),
+ interfaceType));
+ }
+ }, OperationKind.TypePattern);
+ });
+ }
+}
+
+#endif
diff --git a/src/Authoring/WinRT.SourceGenerator/Analyzers/RuntimeClassCastAnalyzer.cs b/src/Authoring/WinRT.SourceGenerator/Analyzers/RuntimeClassCastAnalyzer.cs
new file mode 100644
index 000000000..d37141729
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator/Analyzers/RuntimeClassCastAnalyzer.cs
@@ -0,0 +1,250 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#if ROSLYN_4_12_0_OR_GREATER
+
+using System.Collections.Immutable;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Operations;
+using WinRT.SourceGenerator;
+
+#nullable enable
+
+namespace Generator;
+
+///
+/// A diagnostic analyzer to warn on potentially trim-unsafe casts to WinRT runtime classes.
+///
+[DiagnosticAnalyzer(LanguageNames.CSharp)]
+public sealed class RuntimeClassCastAnalyzer : DiagnosticAnalyzer
+{
+ ///
+ /// The id of the parameter for the target type.
+ ///
+ public const string WindowsRuntimeTypeId = "WindowsRuntimeType";
+
+ ///
+ public override ImmutableArray SupportedDiagnostics { get; } = [WinRTRules.RuntimeClassCast, WinRTRules.IReferenceTypeCast];
+
+ ///
+ public override void Initialize(AnalysisContext context)
+ {
+ // We're intentionally not analyzing generated code, as we don't want to generate warnings for XAML-generated code.
+ // Casts in code-behind files are fine, for two main reasons:
+ // - Many code paths generated by the XAML compiler will use 'Cast', which will root metadata
+ // - All those types used in casts will also have lots of static references, which further roots them enough. The main
+ // issue with casts is mostly just with types that just "pop in" from some external call, without any static references.
+ context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
+ context.EnableConcurrentExecution();
+
+ context.RegisterCompilationStartAction(static context =>
+ {
+ // Enable the analyzer when in the warning level is at least 3, as these warnings can be quite noisy
+ if (GeneratorExecutionContextHelper.GetCsWinRTAotWarningLevel(context.Options.AnalyzerConfigOptionsProvider) < 3)
+ {
+ return;
+ }
+
+ // We should always have the '[WindowsRuntimeType]' attribute, and we need it to detect projected types
+ if (context.Compilation.GetTypeByMetadataName("WinRT.WindowsRuntimeTypeAttribute") is not INamedTypeSymbol windowsRuntimeTypeAttribute)
+ {
+ return;
+ }
+
+ // Also try to get 'WinRT.DynamicWindowsRuntimeCastAttribute ' which should always be present
+ if (context.Compilation.GetTypeByMetadataName("WinRT.DynamicWindowsRuntimeCastAttribute") is not INamedTypeSymbol dynamicWindowsRuntimeCastAttribute)
+ {
+ return;
+ }
+
+ // Helper to check a given method symbol
+ bool IsDynamicCastAttributePresentOnSymbol(ISymbol? symbol, INamedTypeSymbol classType)
+ {
+ if (symbol is null)
+ {
+ return false;
+ }
+
+ foreach (AttributeData attributeData in symbol.EnumerateAttributesWithType(dynamicWindowsRuntimeCastAttribute))
+ {
+ // Check that the type is actually the one used in this case. Otherwise, ignore the attribute (we might have several on the same symbol)
+ if (attributeData.ConstructorArguments is [{ Kind: TypedConstantKind.Type, IsNull: false, Value: INamedTypeSymbol typeSymbol }] &&
+ SymbolEqualityComparer.Default.Equals(typeSymbol, classType))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ // Helper to check if the containing method for a given operation has the right annotation already
+ bool IsDynamicCastAttributePresentOnOperation(IOperation? operation, INamedTypeSymbol classType)
+ {
+ return operation switch
+ {
+ IAnonymousFunctionOperation { Symbol: IMethodSymbol lambdaMethod } =>
+ IsDynamicCastAttributePresentOnSymbol(lambdaMethod, classType) ||
+ IsDynamicCastAttributePresentOnOperation(operation.Parent, classType),
+ ILocalFunctionOperation { Symbol: IMethodSymbol localMethod } =>
+ IsDynamicCastAttributePresentOnSymbol(localMethod, classType) ||
+ IsDynamicCastAttributePresentOnOperation(operation.Parent, classType),
+ IMethodBodyBaseOperation bodyOperation => IsDynamicCastAttributePresentOnSymbol(operation.SemanticModel?.GetDeclaredSymbol(operation.Syntax) as IMethodSymbol, classType),
+ IFieldInitializerOperation fieldInitializer => IsDynamicCastAttributePresentOnSymbol(fieldInitializer.InitializedFields.FirstOrDefault(), classType),
+ { } => IsDynamicCastAttributePresentOnOperation(operation.Parent, classType),
+ null => false
+ };
+ }
+
+ // This handles the following cases:
+ //
+ // C c1 = (C)obj;
+ // C c2 = obj as C;
+ context.RegisterOperationAction(context =>
+ {
+ if (context.Operation is not IConversionOperation { IsImplicit: false, Type: INamedTypeSymbol { IsStatic: false } outerTypeSymbol } conversion)
+ {
+ return;
+ }
+
+ // Match class, enum types, and also nullable enum types (and unwrap them, if so)
+ INamedTypeSymbol? innerTypeSymbol = outerTypeSymbol switch
+ {
+ { TypeKind: TypeKind.Class or TypeKind.Enum } => outerTypeSymbol,
+ {
+ TypeKind: TypeKind.Struct,
+ IsGenericType: true,
+ IsUnboundGenericType: false,
+ ConstructedFrom.SpecialType: SpecialType.System_Nullable_T,
+ TypeArguments: [INamedTypeSymbol { TypeKind: TypeKind.Enum } underlyingType]
+ } => underlyingType,
+ _ => null
+ };
+
+ if (innerTypeSymbol is null)
+ {
+ return;
+ }
+
+ if (innerTypeSymbol.HasAttributeWithType(windowsRuntimeTypeAttribute) &&
+ conversion.Operand is { Type.IsReferenceType: true } and not { ConstantValue: { HasValue: true, Value: null } } &&
+ !context.Compilation.HasImplicitConversion(conversion.Operand.Type, innerTypeSymbol) &&
+ !IsDynamicCastAttributePresentOnOperation(context.Operation, innerTypeSymbol))
+ {
+ // We're intentionally passing 'outerTypeSymbol' as argument, so we show the original type name in the message
+ context.ReportDiagnostic(Diagnostic.Create(
+ innerTypeSymbol.TypeKind is TypeKind.Class ? WinRTRules.RuntimeClassCast : WinRTRules.IReferenceTypeCast,
+ context.Operation.Syntax.GetLocation(),
+ ImmutableDictionary.Create().Add(WindowsRuntimeTypeId, innerTypeSymbol.GetFullyQualifiedMetadataName()),
+ outerTypeSymbol));
+ }
+ }, OperationKind.Conversion);
+
+ // This handles the following cases:
+ //
+ // if (obj is C)
+ // {
+ // }
+ context.RegisterOperationAction(context =>
+ {
+ if (context.Operation is IIsTypeOperation { IsImplicit: false, TypeOperand: INamedTypeSymbol { TypeKind: TypeKind.Class or TypeKind.Enum, IsStatic: false } typeSymbol } typeOperation &&
+ typeSymbol.HasAttributeWithType(windowsRuntimeTypeAttribute) &&
+ typeOperation.ValueOperand.Type is { IsReferenceType: true } &&
+ !context.Compilation.HasImplicitConversion(typeOperation.ValueOperand.Type, typeSymbol) &&
+ !IsDynamicCastAttributePresentOnOperation(context.Operation, typeSymbol))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ typeSymbol.TypeKind is TypeKind.Class ? WinRTRules.RuntimeClassCast : WinRTRules.IReferenceTypeCast,
+ context.Operation.Syntax.GetLocation(),
+ ImmutableDictionary.Create().Add(WindowsRuntimeTypeId, typeSymbol.GetFullyQualifiedMetadataName()),
+ typeSymbol));
+ }
+ }, OperationKind.IsType);
+
+ // This handles the following cases:
+ //
+ // if (obj is C ic)
+ // {
+ // }
+ //
+ // List patterns are also handled:
+ //
+ // if (items is [C ic, ..])
+ // {
+ // }
+ context.RegisterOperationAction(context =>
+ {
+ if (context.Operation is IDeclarationPatternOperation { IsImplicit: false, MatchedType: INamedTypeSymbol { TypeKind: TypeKind.Class or TypeKind.Enum, IsStatic: false } typeSymbol } patternOperation &&
+ typeSymbol.HasAttributeWithType(windowsRuntimeTypeAttribute) &&
+ patternOperation.InputType.IsReferenceType &&
+ !context.Compilation.HasImplicitConversion(patternOperation.InputType, typeSymbol) &&
+ !IsDynamicCastAttributePresentOnOperation(context.Operation, typeSymbol))
+ {
+ // Adjust the location for 'obj is C ic' patterns, to include the 'is' expression as well
+ Location location = context.Operation.Parent is IIsPatternOperation isPatternOperation
+ ? isPatternOperation.Syntax.GetLocation()
+ : context.Operation.Syntax.GetLocation();
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ typeSymbol.TypeKind is TypeKind.Class ? WinRTRules.RuntimeClassCast : WinRTRules.IReferenceTypeCast,
+ location,
+ ImmutableDictionary.Create().Add(WindowsRuntimeTypeId, typeSymbol.GetFullyQualifiedMetadataName()),
+ typeSymbol));
+ }
+ }, OperationKind.DeclarationPattern);
+
+ // This handles the following cases:
+ //
+ // if (items is [C, ..])
+ // {
+ // }
+ context.RegisterOperationAction(context =>
+ {
+ if (context.Operation is ITypePatternOperation { IsImplicit: false, MatchedType: INamedTypeSymbol { TypeKind: TypeKind.Class or TypeKind.Enum, IsStatic: false } typeSymbol } patternOperation &&
+ typeSymbol.HasAttributeWithType(windowsRuntimeTypeAttribute) &&
+ patternOperation.InputType.IsReferenceType &&
+ !context.Compilation.HasImplicitConversion(patternOperation.InputType, typeSymbol) &&
+ !IsDynamicCastAttributePresentOnOperation(context.Operation, typeSymbol))
+ {
+ context.ReportDiagnostic(Diagnostic.Create(
+ typeSymbol.TypeKind is TypeKind.Class ? WinRTRules.RuntimeClassCast : WinRTRules.IReferenceTypeCast,
+ context.Operation.Syntax.GetLocation(),
+ ImmutableDictionary.Create().Add(WindowsRuntimeTypeId, typeSymbol.GetFullyQualifiedMetadataName()),
+ typeSymbol));
+ }
+ }, OperationKind.TypePattern);
+
+ // This handles the following cases:
+ //
+ // case C:
+ // {
+ // }
+ context.RegisterOperationAction(context =>
+ {
+ if (context.Operation is IPatternCaseClauseOperation { IsImplicit: false, Pattern: { NarrowedType: INamedTypeSymbol { TypeKind: TypeKind.Class or TypeKind.Enum, IsStatic: false } typeSymbol } patternOperation } &&
+ typeSymbol.HasAttributeWithType(windowsRuntimeTypeAttribute) &&
+ patternOperation.InputType.IsReferenceType &&
+ !context.Compilation.HasImplicitConversion(patternOperation.InputType, typeSymbol) &&
+ !IsDynamicCastAttributePresentOnOperation(context.Operation, typeSymbol))
+ {
+ // Special case: we're already handling declaration patterns above, and we don't want to emit two
+ // warnings for the same code. This callback is just to handle simple patterns in 'switch' statements.
+ if (patternOperation is IDeclarationPatternOperation)
+ {
+ return;
+ }
+
+ context.ReportDiagnostic(Diagnostic.Create(
+ typeSymbol.TypeKind is TypeKind.Class ? WinRTRules.RuntimeClassCast : WinRTRules.IReferenceTypeCast,
+ patternOperation.Syntax.GetLocation(),
+ ImmutableDictionary.Create().Add(WindowsRuntimeTypeId, typeSymbol.GetFullyQualifiedMetadataName()),
+ typeSymbol));
+ }
+ }, OperationKind.CaseClause);
+ });
+ }
+}
+
+#endif
diff --git a/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs b/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs
new file mode 100644
index 000000000..d6210a7d9
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator/AotOptimizer.cs
@@ -0,0 +1,2043 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using Microsoft.CodeAnalysis.Diagnostics;
+using Microsoft.CodeAnalysis.Operations;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using System.Threading;
+using WinRT.SourceGenerator;
+
+namespace Generator
+{
+ [Generator]
+ public class WinRTAotSourceGenerator : IIncrementalGenerator
+ {
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ var properties = context.AnalyzerConfigOptionsProvider
+ .Combine(context.CompilationProvider)
+ .Select(static ((AnalyzerConfigOptionsProvider provider, Compilation compilation) value, CancellationToken _) =>
+ new CsWinRTAotOptimizerProperties(
+ value.provider.IsCsWinRTAotOptimizerEnabled(),
+ value.provider.IsCsWinRTComponent(),
+ value.provider.IsCsWinRTCcwLookupTableGeneratorEnabled(),
+ GeneratorExecutionContextHelper.IsCsWinRTAotOptimizerInAutoMode(value.provider, value.compilation))
+ );
+
+ var assemblyName = context.CompilationProvider.Select(static (compilation, _) => GeneratorHelper.EscapeAssemblyNameForIdentifier(compilation.AssemblyName));
+
+ var propertiesAndAssemblyName = properties.Combine(assemblyName);
+
+ var typeMapperAndProperties = context.AnalyzerConfigOptionsProvider
+ .Select(static (options, ct) => options.GetCsWinRTUseWindowsUIXamlProjections())
+ .Select(static (mode, ct) => new TypeMapper(mode))
+ .Combine(properties);
+
+ var vtablesToAddFromDetectedClassTypes = context.SyntaxProvider.CreateSyntaxProvider(
+ static (n, _) => NeedVtableAttribute(n),
+ static (n, _) => n)
+ .Combine(typeMapperAndProperties)
+ .Select(static ((GeneratorSyntaxContext generatorSyntaxContext, (TypeMapper typeMapper, CsWinRTAotOptimizerProperties properties) typeMapperAndProperties) value, CancellationToken _) =>
+ value.typeMapperAndProperties.properties.IsCsWinRTAotOptimizerInAutoMode ?
+ GetVtableAttributeToAdd(value.generatorSyntaxContext, value.typeMapperAndProperties.typeMapper, false, value.typeMapperAndProperties.properties.IsCsWinRTCcwLookupTableGeneratorEnabled) : default)
+ .Where(static vtableAttribute => vtableAttribute != default);
+
+ var autoDetectedVtableAttributesToAdd = vtablesToAddFromDetectedClassTypes.Select(static (vtable, _) => vtable.Item1);
+ var autoDetectedAdapterTypesToAddOnLookupTable = vtablesToAddFromDetectedClassTypes.SelectMany(static (vtable, _) => vtable.Item2);
+
+ var vtablesToAddFromOptInClassTypes = context.SyntaxProvider.ForAttributeWithMetadataName(
+ "WinRT.GeneratedWinRTExposedTypeAttribute",
+ static (n, _) => NeedVtableAttribute(n),
+ static (n, _) => n)
+ .Combine(typeMapperAndProperties)
+ .Select(static ((GeneratorAttributeSyntaxContext generatorSyntaxContext, (TypeMapper typeMapper, CsWinRTAotOptimizerProperties properties) typeMapperAndProperties) value, CancellationToken _) =>
+ value.typeMapperAndProperties.properties.IsCsWinRTAotOptimizerEnabled ?
+ GetVtableAttributeToAdd(value.generatorSyntaxContext, value.typeMapperAndProperties.typeMapper, false, value.typeMapperAndProperties.properties.IsCsWinRTCcwLookupTableGeneratorEnabled) : default)
+ .Where(static vtableAttribute => vtableAttribute != default);
+
+ var optinVtableAttributesToAdd = vtablesToAddFromOptInClassTypes.Select(static (vtable, _) => vtable.Item1);
+ var optinAdapterTypesToAddOnLookupTable = vtablesToAddFromOptInClassTypes.SelectMany(static (vtable, _) => vtable.Item2);
+
+ // Merge both auto detected vtables and opt-in vtables.
+ var vtableAttributesToAdd = autoDetectedVtableAttributesToAdd.Collect().Combine(optinVtableAttributesToAdd.Collect()).SelectMany(static (value, _) => value.Left.AddRange(value.Right).Distinct());
+ context.RegisterImplementationSourceOutput(vtableAttributesToAdd.Collect().Combine(propertiesAndAssemblyName), GenerateVtableAttributes);
+
+ // Get the vtables for component types. This is used for filtering out generic interfaces
+ // that will already be generated by the component generator.
+ var vtablesFromComponentTypes = context.SyntaxProvider.CreateSyntaxProvider(
+ static (n, _) => IsComponentType(n),
+ static (n, _) => n)
+ .Combine(typeMapperAndProperties)
+ // Get component types if only authoring scenario and if aot optimizer enabled.
+ .Select(static ((GeneratorSyntaxContext generatorSyntaxContext, (TypeMapper typeMapper, CsWinRTAotOptimizerProperties properties) typeMapperAndProperties) value, CancellationToken _) =>
+ value.typeMapperAndProperties.properties.IsCsWinRTAotOptimizerEnabled && value.typeMapperAndProperties.properties.IsCsWinRTComponent ?
+ GetVtableAttributeToAdd(value.generatorSyntaxContext, value.typeMapperAndProperties.typeMapper, true, true) : default)
+ .Where(static vtableAttribute => vtableAttribute != default);
+
+ var autoDetectedInstantiatedTypesToAddOnLookupTable = context.SyntaxProvider.CreateSyntaxProvider(
+ static (n, _) => NeedVtableOnLookupTable(n),
+ static (n, _) => n)
+ .Combine(typeMapperAndProperties)
+ .Select(static ((GeneratorSyntaxContext generatorSyntaxContext, (TypeMapper typeMapper, CsWinRTAotOptimizerProperties properties) typeMapperAndProperties) value,
+ CancellationToken _) =>
+ value.typeMapperAndProperties.properties.IsCsWinRTAotOptimizerInAutoMode && value.typeMapperAndProperties.properties.IsCsWinRTCcwLookupTableGeneratorEnabled ?
+ GetVtableAttributesToAddOnLookupTable(value.generatorSyntaxContext, value.typeMapperAndProperties.typeMapper, value.typeMapperAndProperties.properties.IsCsWinRTComponent) :
+ (EquatableArray)ImmutableArray.Empty)
+ .SelectMany(static (vtable, _) => vtable)
+ .Where(static vtableAttribute => vtableAttribute != null);
+
+ var optinInstantiatedTypesToAddOnLookupTable = context.SyntaxProvider.ForAttributeWithMetadataName(
+ "WinRT.GeneratedWinRTExposedExternalTypeAttribute",
+ static (n, _) => true,
+ static (n, _) => n)
+ .Combine(typeMapperAndProperties)
+ .Select(static ((GeneratorAttributeSyntaxContext generatorSyntaxContext, (TypeMapper typeMapper, CsWinRTAotOptimizerProperties properties) typeMapperAndProperties) value,
+ CancellationToken _) =>
+ value.typeMapperAndProperties.properties.IsCsWinRTAotOptimizerEnabled && value.typeMapperAndProperties.properties.IsCsWinRTCcwLookupTableGeneratorEnabled ?
+ GetVtableAttributesToAddOnLookupTable(value.generatorSyntaxContext, value.typeMapperAndProperties.typeMapper, value.typeMapperAndProperties.properties.IsCsWinRTComponent) :
+ (EquatableArray)ImmutableArray.Empty)
+ .SelectMany(static (vtable, _) => vtable)
+ .Where(static vtableAttribute => vtableAttribute != null);
+
+ var instantiatedTaskAdapters = context.SyntaxProvider.CreateSyntaxProvider(
+ static (n, _) => IsAsyncOperationMethodCall(n),
+ static (n, _) => n)
+ .Combine(typeMapperAndProperties)
+ .Select(static ((GeneratorSyntaxContext generatorSyntaxContext, (TypeMapper typeMapper, CsWinRTAotOptimizerProperties properties) typeMapperAndProperties) value,
+ CancellationToken _) =>
+ value.typeMapperAndProperties.properties.IsCsWinRTAotOptimizerInAutoMode && value.typeMapperAndProperties.properties.IsCsWinRTCcwLookupTableGeneratorEnabled ?
+ GetVtableAttributesForTaskAdapters(value.generatorSyntaxContext, value.typeMapperAndProperties.typeMapper, value.typeMapperAndProperties.properties.IsCsWinRTComponent) : default)
+ .Where(static vtableAttribute => vtableAttribute != default)
+ .Collect();
+
+ // Merge both adapter types lists and instantiated types lists.
+ var vtablesToAddOnLookupTable =
+ autoDetectedInstantiatedTypesToAddOnLookupTable.Collect().
+ Combine(autoDetectedAdapterTypesToAddOnLookupTable.Collect()).
+ Combine(optinInstantiatedTypesToAddOnLookupTable.Collect()).
+ Combine(optinAdapterTypesToAddOnLookupTable.Collect()).
+ Combine(instantiatedTaskAdapters).
+ SelectMany(static (value, _) =>
+ value.Left.Left.Left.Left
+ .AddRange(value.Left.Left.Left.Right)
+ .AddRange(value.Left.Left.Right)
+ .AddRange(value.Left.Right)
+ .AddRange(value.Right)
+ .Distinct()
+ );
+
+ var genericInterfacesFromVtableAttribute = vtableAttributesToAdd.Combine(properties).SelectMany(
+ static ((VtableAttribute vtableAttribute, CsWinRTAotOptimizerProperties properties) value, CancellationToken _) =>
+ // If this is a CsWinRT component, the public types are handled by the component source generator rather than
+ // the AOT source generator. So we filter those out here.
+ (!value.properties.IsCsWinRTComponent || (value.properties.IsCsWinRTComponent && !value.vtableAttribute.IsPublic)) ?
+ value.vtableAttribute.GenericInterfaces : (EquatableArray)ImmutableArray.Empty).Collect();
+ var genericInterfacesFromVtableLookupTable = vtablesToAddOnLookupTable.SelectMany(static (vtable, _) => vtable.GenericInterfaces).Collect();
+
+ // The component generator generates vtable attributes for public types. The generic interfaces used by those types or its adapter types
+ // can overlap with the ones being generated here. So get which ones are already generated to be able to filter them out.
+ var genericInterfacesGeneratedByComponentGenerator = vtablesFromComponentTypes.
+ SelectMany(static ((VtableAttribute vtableAttribute, EquatableArray adapterTypes) classType, CancellationToken _) =>
+ classType.vtableAttribute.GenericInterfaces.Union(classType.adapterTypes.SelectMany(static v => v.GenericInterfaces)).Distinct()).
+ Collect();
+
+ context.RegisterImplementationSourceOutput(
+ genericInterfacesFromVtableAttribute
+ .Combine(genericInterfacesFromVtableLookupTable)
+ .Combine(genericInterfacesGeneratedByComponentGenerator)
+ .Combine(propertiesAndAssemblyName),
+ GenerateCCWForGenericInstantiation);
+
+ context.RegisterImplementationSourceOutput(vtablesToAddOnLookupTable.Collect().Combine(propertiesAndAssemblyName), GenerateVtableLookupTable);
+
+ var bindableCustomPropertyAttributes = context.SyntaxProvider.ForAttributeWithMetadataName(
+ "WinRT.GeneratedBindableCustomPropertyAttribute",
+ static (n, _) => NeedCustomPropertyImplementation(n),
+ static (n, _) => n)
+ .Combine(properties)
+ .Select(static ((GeneratorAttributeSyntaxContext generatorSyntaxContext, CsWinRTAotOptimizerProperties properties) value, CancellationToken _) =>
+ value.properties.IsCsWinRTAotOptimizerEnabled ? GetBindableCustomProperties(value.generatorSyntaxContext) : default)
+ .Where(static bindableCustomProperties => bindableCustomProperties != default)
+ .Collect()
+ .Combine(properties);
+ context.RegisterImplementationSourceOutput(bindableCustomPropertyAttributes, GenerateBindableCustomProperties);
+ }
+
+ // Restrict to non-projected classes which can be instantiated
+ // and are partial allowing to add attributes.
+ private static bool NeedVtableAttribute(SyntaxNode node)
+ {
+ return node is ClassDeclarationSyntax declaration &&
+ !declaration.Modifiers.Any(static m => m.IsKind(SyntaxKind.StaticKeyword) || m.IsKind(SyntaxKind.AbstractKeyword)) &&
+ GeneratorHelper.IsPartial(declaration) &&
+ !GeneratorHelper.IsWinRTType(declaration); // Making sure it isn't an RCW we are projecting.
+ }
+
+ // Filters to non WinRT types which are public and can be instantiated.
+ // This is used to try to determine types which a component source generator will process.
+ private static bool IsComponentType(SyntaxNode node)
+ {
+ return node is ClassDeclarationSyntax declaration &&
+ !declaration.Modifiers.Any(static m => m.IsKind(SyntaxKind.StaticKeyword) || m.IsKind(SyntaxKind.AbstractKeyword)) &&
+ declaration.Modifiers.Any(static m => m.IsKind(SyntaxKind.PublicKeyword)) &&
+ !GeneratorHelper.IsWinRTType(declaration); // Making sure it isn't an RCW we are projecting.
+ }
+
+ private static bool NeedCustomPropertyImplementation(SyntaxNode node)
+ {
+ if ((node is ClassDeclarationSyntax classDeclaration && !classDeclaration.Modifiers.Any(static m => m.IsKind(SyntaxKind.StaticKeyword) || m.IsKind(SyntaxKind.AbstractKeyword))) ||
+ (node is RecordDeclarationSyntax recordDeclaration && !recordDeclaration.Modifiers.Any(static m => m.IsKind(SyntaxKind.StaticKeyword) || m.IsKind(SyntaxKind.AbstractKeyword))) ||
+ (node is StructDeclarationSyntax structDeclaration && !structDeclaration.Modifiers.Any(static m => m.IsKind(SyntaxKind.StaticKeyword))))
+ {
+ TypeDeclarationSyntax typeDeclaration = (TypeDeclarationSyntax)node;
+
+ return GeneratorHelper.IsPartial(typeDeclaration);
+ }
+
+ return false;
+ }
+
+ private static (VtableAttribute, EquatableArray) GetVtableAttributeToAdd(
+ GeneratorSyntaxContext context,
+ TypeMapper typeMapper,
+ bool checkForComponentTypes,
+ bool isCsWinRTCcwLookupTableGeneratorEnabled)
+ {
+ return GetVtableAttributeToAdd(
+ context.SemanticModel.GetDeclaredSymbol(context.Node as ClassDeclarationSyntax),
+ typeMapper,
+ context.SemanticModel.Compilation,
+ checkForComponentTypes,
+ isCsWinRTCcwLookupTableGeneratorEnabled);
+ }
+
+ private static (VtableAttribute, EquatableArray) GetVtableAttributeToAdd(
+ GeneratorAttributeSyntaxContext context,
+ TypeMapper typeMapper,
+ bool checkForComponentTypes,
+ bool isCsWinRTCcwLookupTableGeneratorEnabled)
+ {
+ return GetVtableAttributeToAdd(
+ (ITypeSymbol)context.TargetSymbol,
+ typeMapper,
+ context.SemanticModel.Compilation,
+ checkForComponentTypes,
+ isCsWinRTCcwLookupTableGeneratorEnabled);
+ }
+
+ private static (VtableAttribute, EquatableArray) GetVtableAttributeToAdd(
+ ITypeSymbol symbol,
+ TypeMapper typeMapper,
+ Compilation compilation,
+ bool checkForComponentTypes,
+ bool isCsWinRTCcwLookupTableGeneratorEnabled)
+ {
+ var isManagedOnlyType = GeneratorHelper.IsManagedOnlyType(compilation);
+ var isWinRTTypeFunc = GeneratorHelper.IsWinRTType(compilation, checkForComponentTypes);
+ var vtableAttribute = GetVtableAttributeToAdd(symbol, isManagedOnlyType, isWinRTTypeFunc, typeMapper, compilation, false);
+ if (vtableAttribute != default)
+ {
+ HashSet vtableAttributesForLookupTable = [];
+ // Add any adapter types which may be needed if certain functions
+ // from some known interfaces are called.
+ if (isCsWinRTCcwLookupTableGeneratorEnabled)
+ {
+ AddVtableAdapterTypeForKnownInterface(symbol, compilation, isManagedOnlyType, isWinRTTypeFunc, typeMapper, vtableAttributesForLookupTable);
+ }
+ return (vtableAttribute, vtableAttributesForLookupTable.ToImmutableArray());
+ }
+
+ return default;
+ }
+
+ // There are several async operation related methods that can be called.
+ // But they are all under the AsyncInfo static class or is the AsAsyncOperation
+ // extension method.
+ static bool IsAsyncOperationMethodCall(SyntaxNode node)
+ {
+ if (node is InvocationExpressionSyntax methodInvoke &&
+ methodInvoke.Expression is MemberAccessExpressionSyntax methodAccess)
+ {
+ // Check for static class as a way of handling all the async functions from it.
+ if (methodAccess.Expression is IdentifierNameSyntax className &&
+ className.Identifier.ValueText == "AsyncInfo")
+ {
+ return true;
+ }
+
+ // Check for calling the fully qualified static class.
+ // i.e. System.Runtime.InteropServices.WindowsRuntime.AsyncInfo
+ if (methodAccess.Expression is MemberAccessExpressionSyntax memberAccess &&
+ memberAccess.Name.Identifier.ValueText == "AsyncInfo")
+ {
+ return true;
+ }
+
+ // Check for function call for the scenario that doesn't use the static class.
+ if (methodAccess.Name is IdentifierNameSyntax methodName)
+ {
+ return methodName.Identifier.ValueText == "AsAsyncOperation";
+ }
+ }
+
+ return false;
+ }
+
+ // Detect if AsAsyncOperation or similar function is being called and if so,
+ // make sure the generic adapter type we use for it is on the lookup table.
+ // We do this both assuming this is not an authoring component and is an authoring
+ // component as we don't know that at this stage and the results can vary based on that.
+ // We will choose the right one later when we can combine with properties.
+ private static VtableAttribute GetVtableAttributesForTaskAdapters(GeneratorSyntaxContext context, TypeMapper typeMapper, bool isCsWinRTComponent)
+ {
+ // Generic instantiation of task adapters make of use of unsafe.
+ // This will be caught by GetVtableAttributeToAdd, but catching it early here too.
+ if (!GeneratorHelper.AllowUnsafe(context.SemanticModel.Compilation))
+ {
+ return default;
+ }
+
+ if (context.SemanticModel.GetSymbolInfo(context.Node as InvocationExpressionSyntax).Symbol is IMethodSymbol symbol)
+ {
+ var adapterTypeStr = GeneratorHelper.GetTaskAdapterIfAsyncMethod(symbol);
+ if (!string.IsNullOrEmpty(adapterTypeStr))
+ {
+ var adpaterType = context.SemanticModel.Compilation.GetTypeByMetadataName(adapterTypeStr);
+ if (adpaterType is not null)
+ {
+ var constructedAdapterType = adpaterType.Construct([.. symbol.TypeArguments]);
+ return GetVtableAttributeToAdd(
+ constructedAdapterType,
+ GeneratorHelper.IsManagedOnlyType(context.SemanticModel.Compilation),
+ GeneratorHelper.IsWinRTType(context.SemanticModel.Compilation, isCsWinRTComponent),
+ typeMapper,
+ context.SemanticModel.Compilation,
+ false);
+ }
+ }
+ }
+
+ return default;
+ }
+
+#nullable enable
+ private static BindableCustomProperties GetBindableCustomProperties(GeneratorAttributeSyntaxContext context)
+ {
+ // We expect a class with a single attribute.
+ var symbol = (INamedTypeSymbol)context.TargetSymbol;
+ var attributeData = context.Attributes.First();
+
+ List bindableCustomProperties = new();
+
+ // Make all public properties in the class bindable including ones in base type.
+ if (attributeData.ConstructorArguments.Length == 0)
+ {
+ for (var curSymbol = symbol; curSymbol != null; curSymbol = curSymbol.BaseType)
+ {
+ foreach (var propertySymbol in curSymbol.GetMembers().
+ Where(static m => m.Kind == SymbolKind.Property &&
+ m.DeclaredAccessibility == Accessibility.Public))
+ {
+ AddProperty(propertySymbol);
+ }
+ }
+ }
+ // Make specified public properties in the class bindable including ones in base type.
+ else if (attributeData.ConstructorArguments is
+ [
+ { Kind: TypedConstantKind.Array, Values: [..] propertyNames },
+ { Kind: TypedConstantKind.Array, Values: [..] propertyIndexerTypes }
+ ])
+ {
+ for (var curSymbol = symbol; curSymbol != null; curSymbol = curSymbol.BaseType)
+ {
+ foreach (var member in curSymbol.GetMembers())
+ {
+ if (member is IPropertySymbol propertySymbol &&
+ member.DeclaredAccessibility == Accessibility.Public)
+ {
+ if (!propertySymbol.IsIndexer &&
+ propertyNames.Any(p => p.Value is string value && value == propertySymbol.Name))
+ {
+ AddProperty(propertySymbol);
+ }
+ else if (propertySymbol.IsIndexer &&
+ // ICustomProperty only supports single indexer parameter.
+ propertySymbol.Parameters.Length == 1 &&
+ propertyIndexerTypes.Any(p => p.Value is ISymbol typeSymbol && typeSymbol.Equals(propertySymbol.Parameters[0].Type, SymbolEqualityComparer.Default)))
+ {
+ AddProperty(propertySymbol);
+ }
+ }
+ }
+ }
+ }
+
+ var typeName = ToFullyQualifiedString(symbol);
+ bool isGlobalNamespace = symbol.ContainingNamespace == null || symbol.ContainingNamespace.IsGlobalNamespace;
+ var @namespace = symbol.ContainingNamespace?.ToDisplayString();
+ if (!isGlobalNamespace)
+ {
+ typeName = typeName[(@namespace!.Length + 1)..];
+ }
+
+ EquatableArray classHierarchy = ImmutableArray.Empty;
+
+ // Gather the type hierarchy, only if the type is nested (as an optimization)
+ if (symbol.ContainingType is not null)
+ {
+ List hierarchyList = new();
+
+ for (ITypeSymbol parent = symbol; parent is not null; parent = parent.ContainingType)
+ {
+ hierarchyList.Add(new TypeInfo(
+ parent.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat),
+ parent.TypeKind,
+ parent.IsRecord));
+ }
+
+ classHierarchy = ImmutableArray.CreateRange(hierarchyList);
+ }
+
+ return new BindableCustomProperties(
+ @namespace,
+ isGlobalNamespace,
+ typeName,
+ symbol.TypeKind,
+ symbol.IsRecord,
+ classHierarchy,
+ ToFullyQualifiedString(symbol, false),
+ bindableCustomProperties.ToImmutableArray());
+
+ void AddProperty(ISymbol symbol)
+ {
+ if (symbol is IPropertySymbol propertySymbol)
+ {
+ bindableCustomProperties.Add(new BindableCustomProperty(
+ propertySymbol.MetadataName,
+ ToFullyQualifiedString(propertySymbol.Type, false),
+ // Make sure the property accessors are also public even if property itself is public.
+ propertySymbol.GetMethod != null && propertySymbol.GetMethod.DeclaredAccessibility == Accessibility.Public,
+ propertySymbol.SetMethod != null && !propertySymbol.SetMethod.IsInitOnly && propertySymbol.SetMethod.DeclaredAccessibility == Accessibility.Public,
+ propertySymbol.IsIndexer,
+ propertySymbol.IsIndexer ? ToFullyQualifiedString(propertySymbol.Parameters[0].Type, false) : "",
+ propertySymbol.IsStatic
+ ));
+ }
+ }
+ }
+#nullable disable
+
+ private static string ToFullyQualifiedString(ISymbol symbol, bool trimGlobal = true)
+ {
+ // Used to ensure class names within generics are fully qualified to avoid
+ // having issues when put in ABI namespaces.
+ var symbolDisplayString = new SymbolDisplayFormat(
+ globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included,
+ typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
+ genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
+ miscellaneousOptions: SymbolDisplayMiscellaneousOptions.UseSpecialTypes | SymbolDisplayMiscellaneousOptions.ExpandNullable);
+
+ var qualifiedString = symbol.ToDisplayString(symbolDisplayString);
+ return trimGlobal && qualifiedString.StartsWith("global::") ? qualifiedString[8..] : qualifiedString;
+ }
+
+ private static string ToVtableLookupString(ISymbol symbol)
+ {
+ List genericArguments = [];
+ var fullName = ToVtableLookupString(symbol, genericArguments);
+ if (genericArguments.Count == 0)
+ {
+ return fullName;
+ }
+
+ return $$"""{{fullName}}[{{string.Join(",", genericArguments)}}]""";
+ }
+
+ private static string ToVtableLookupString(ISymbol symbol, List genericArguments, bool ignoreTypeArguments = false)
+ {
+ if (symbol is INamedTypeSymbol namedTypeSymbol &&
+ !ignoreTypeArguments &&
+ namedTypeSymbol.TypeArguments.Length != 0)
+ {
+ // Ignore type arguments and get the string representation for the rest of
+ // the type to properly handle nested types.
+ var fullName = ToVtableLookupString(symbol, genericArguments, true);
+ // Type arguments are collected but not added to the type name until the end
+ // per the format of Type.ToString(). ToVtableLookupString on the symbol is
+ // also called first to ensure any type arguments from any nested parent types
+ // are added first.
+ foreach (var typeArgument in namedTypeSymbol.TypeArguments)
+ {
+ genericArguments.Add(ToVtableLookupString(typeArgument));
+ }
+ return fullName;
+ }
+ else
+ {
+ // If it is a generic type argument or the type is directly under a namspace, we just use the ToDisplayString API.
+ if (symbol is not INamedTypeSymbol || symbol.ContainingSymbol is INamespaceSymbol || symbol.ContainingSymbol is null)
+ {
+ var arity = symbol is INamedTypeSymbol namedType && namedType.Arity > 0 ? "`" + namedType.Arity : "";
+ var symbolDisplayString = new SymbolDisplayFormat(
+ globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Omitted,
+ typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
+ miscellaneousOptions: SymbolDisplayMiscellaneousOptions.ExpandNullable);
+ return symbol.ToDisplayString(symbolDisplayString) + arity;
+ }
+ else
+ {
+ // Nested types use + in the fully qualified name rather than .
+ return ToVtableLookupString(symbol.ContainingSymbol, genericArguments) + "+" + symbol.MetadataName;
+ }
+ }
+ }
+
+ private static string GetRuntimeClassName(
+ INamedTypeSymbol type,
+ Func isWinRTType,
+ TypeMapper mapper)
+ {
+ if (type == null)
+ {
+ return string.Empty;
+ }
+
+ string metadataName = string.Join(".", type.ContainingNamespace?.ToDisplayString(), type.MetadataName);
+ if (type.IsGenericType && !type.IsDefinition)
+ {
+ StringBuilder builder = new();
+
+ builder.Append(GetRuntimeClassName(type.OriginalDefinition, isWinRTType, mapper));
+ builder.Append("<");
+
+ bool first = true;
+ foreach (var genericArg in type.TypeArguments)
+ {
+ if (!first)
+ {
+ builder.Append(", ");
+ }
+
+ builder.Append(GetRuntimeClassName(genericArg as INamedTypeSymbol, isWinRTType, mapper));
+ first = false;
+ }
+
+ builder.Append(">");
+
+ return builder.ToString();
+ }
+ else if (type.SpecialType == SpecialType.System_Object)
+ {
+ return "Object";
+ }
+ else if (type.SpecialType == SpecialType.System_Byte)
+ {
+ return "UInt8";
+ }
+ else if (type.SpecialType == SpecialType.System_SByte)
+ {
+ return "Int8";
+ }
+ else if (mapper.HasMappingForType(metadataName))
+ {
+ var mapping = mapper.GetMappedType(metadataName).GetMapping();
+ return mapping.Item1 + "." + mapping.Item2;
+ }
+ else if (type.SpecialType != SpecialType.None)
+ {
+ return type.Name;
+ }
+ else if (isWinRTType(type, mapper))
+ {
+ return metadataName;
+ }
+ else
+ {
+ // If we end up here, this is most likely an authoring scenario where the type is being authored
+ // for WinRT projection in this component.
+ return metadataName;
+ }
+ }
+
+ internal static VtableAttribute GetVtableAttributeToAdd(
+ ITypeSymbol symbol,
+ Func isManagedOnlyType,
+ Func isWinRTType,
+ TypeMapper mapper,
+ Compilation compilation,
+ bool isAuthoring,
+ string authoringDefaultInterface = "")
+ {
+ if (symbol is null)
+ {
+ return default;
+ }
+
+ if (GeneratorHelper.HasNonInstantiatedWinRTGeneric(symbol, mapper))
+ {
+ return default;
+ }
+
+ // Skip all types explicitly blocked for marshalling.
+ // We don't want them to affect the codegen at all.
+ if (isManagedOnlyType(symbol))
+ {
+ return default;
+ }
+
+ HashSet interfacesToAddToVtable = new();
+ HashSet genericInterfacesToAddToVtable = new();
+
+ if (!string.IsNullOrEmpty(authoringDefaultInterface))
+ {
+ interfacesToAddToVtable.Add(authoringDefaultInterface);
+ }
+
+ // If the attribute is already placed on the type, don't generate a new one as we will
+ // use the specified one. Also for authoring scenarios where we call this for authored WinRT types,
+ // don't generate the runtimeclass name for them as we will rely on the full name for them as we do today.
+ var checkForRuntimeClasName = !GeneratorHelper.HasWinRTRuntimeClassNameAttribute(symbol, compilation) &&
+ (!isAuthoring || (isAuthoring && !isWinRTType(symbol, mapper)));
+ INamedTypeSymbol interfaceToUseForRuntimeClassName = null;
+ foreach (var iface in symbol.AllInterfaces)
+ {
+ if (isWinRTType(iface, mapper))
+ {
+ // If the interface projection was generated using an older CsWinRT version,
+ // it won't have the necessary properties to generate the AOT compatible code
+ // and we don't want to result in compiler errors.
+ // We exclude generic types as they are either defined in WinRT.Runtime or the
+ // Windows SDK projection, so we don't need to check them.
+ if (!iface.IsGenericType &&
+ GeneratorHelper.IsOldProjectionAssembly(iface.ContainingAssembly))
+ {
+ return default;
+ }
+
+ interfacesToAddToVtable.Add(ToFullyQualifiedString(iface));
+ AddGenericInterfaceInstantiation(iface);
+ CheckForInterfaceToUseForRuntimeClassName(iface);
+ }
+
+ if (iface.IsGenericType && TryGetCompatibleWindowsRuntimeTypesForVariantType(iface, mapper, null, isWinRTType, compilation.ObjectType, out var compatibleIfaces))
+ {
+ foreach (var compatibleIface in compatibleIfaces)
+ {
+ // For covariant interfaces which are exclusive interfaces that the projection implemented
+ // such as overrides / protected interfaces in composable types, we don't include them in
+ // the vtable as they are today marked internal and we can't reference them due to that.
+ // If this scenarios matters where native callers do indeed QI for these exclusive
+ // covariant interfaces, we can in the future project them as public, but for now
+ // leaving as is.
+ if (GeneratorHelper.IsInternalInterfaceFromReferences(compatibleIface, compilation.Assembly))
+ {
+ continue;
+ }
+
+ interfacesToAddToVtable.Add(ToFullyQualifiedString(compatibleIface));
+ AddGenericInterfaceInstantiation(compatibleIface);
+ CheckForInterfaceToUseForRuntimeClassName(compatibleIface);
+ }
+ }
+ }
+
+ // KeyValueType is a value type in C#, but it is projected as a reference type in WinRT.
+ if (symbol.TypeKind == TypeKind.Struct && symbol.MetadataName == "KeyValuePair`2" && isWinRTType(symbol, mapper))
+ {
+ interfacesToAddToVtable.Add(ToFullyQualifiedString(symbol));
+ AddGenericInterfaceInstantiation(symbol as INamedTypeSymbol);
+
+ // KeyValuePair is projected as an interface.
+ CheckForInterfaceToUseForRuntimeClassName(symbol as INamedTypeSymbol);
+ }
+
+ bool isDelegate = false;
+ if (symbol.TypeKind == TypeKind.Delegate)
+ {
+ isDelegate = true;
+ interfacesToAddToVtable.Add(ToFullyQualifiedString(symbol));
+ AddGenericInterfaceInstantiation(symbol as INamedTypeSymbol);
+ }
+
+ if (!interfacesToAddToVtable.Any())
+ {
+ return default;
+ }
+
+ // If there are generic interfaces, the generic interface instantiations make use of
+ // unsafe. But if it isn't enabled, we don't want to fail to compile in case it is
+ // not a WinRT scenario. So we instead, don't generate the code that needs the unsafe
+ // and there would be a diagnostic produced by the analyzer.
+ if (genericInterfacesToAddToVtable.Any() && !GeneratorHelper.AllowUnsafe(compilation))
+ {
+ return default;
+ }
+
+ var typeName = ToFullyQualifiedString(symbol);
+ bool isGlobalNamespace = symbol.ContainingNamespace == null || symbol.ContainingNamespace.IsGlobalNamespace;
+ var @namespace = symbol.ContainingNamespace?.ToDisplayString();
+ if (!isGlobalNamespace)
+ {
+ typeName = typeName[(@namespace.Length + 1)..];
+ }
+
+ EquatableArray classHierarchy = ImmutableArray.Empty;
+
+ // Gather the type hierarchy, only if the type is nested (as an optimization)
+ if (symbol.ContainingType is not null)
+ {
+ List hierarchyList = new();
+
+ for (ITypeSymbol parent = symbol; parent is not null; parent = parent.ContainingType)
+ {
+ hierarchyList.Add(new TypeInfo(
+ parent.ToDisplayString(SymbolDisplayFormat.MinimallyQualifiedFormat),
+ parent.TypeKind,
+ parent.IsRecord));
+ }
+
+ classHierarchy = ImmutableArray.CreateRange(hierarchyList);
+ }
+
+ return new VtableAttribute(
+ isAuthoring ? "ABI.Impl." + @namespace : @namespace,
+ isGlobalNamespace,
+ typeName,
+ classHierarchy,
+ ToVtableLookupString(symbol),
+ interfacesToAddToVtable.ToImmutableArray(),
+ genericInterfacesToAddToVtable.ToImmutableArray(),
+ symbol is IArrayTypeSymbol,
+ isDelegate,
+ symbol.DeclaredAccessibility == Accessibility.Public,
+ GetRuntimeClassName(interfaceToUseForRuntimeClassName, isWinRTType, mapper));
+
+ void AddGenericInterfaceInstantiation(INamedTypeSymbol iface)
+ {
+ if (iface.IsGenericType)
+ {
+ List genericParameters = new();
+ foreach (var genericParameter in iface.TypeArguments)
+ {
+ var isNullable = genericParameter.IsValueType && genericParameter.NullableAnnotation.HasFlag(NullableAnnotation.Annotated);
+
+ // Handle initialization of nested generics as they may not be
+ // initialized already.
+ if (!isNullable &&
+ genericParameter is INamedTypeSymbol genericParameterIface &&
+ genericParameterIface.IsGenericType)
+ {
+ AddGenericInterfaceInstantiation(genericParameterIface);
+ }
+
+ genericParameters.Add(new GenericParameter(
+ ToFullyQualifiedString(genericParameter),
+ GeneratorHelper.GetAbiType(genericParameter, mapper),
+ isNullable ? TypeKind.Interface : genericParameter.TypeKind,
+ ComputeTypeFlags(genericParameter, compilation)));
+ }
+
+ genericInterfacesToAddToVtable.Add(new GenericInterface(
+ ToFullyQualifiedString(iface),
+ $$"""{{iface.ContainingNamespace}}.{{iface.MetadataName}}""",
+ genericParameters.ToImmutableArray()));
+ }
+ }
+
+ bool IsExternalInternalInterface(INamedTypeSymbol iface)
+ {
+ return (iface.DeclaredAccessibility == Accessibility.Internal && !SymbolEqualityComparer.Default.Equals(iface.ContainingAssembly, compilation.Assembly)) ||
+ (iface.IsGenericType && iface.TypeArguments.Any(typeArgument => IsExternalInternalInterface(typeArgument as INamedTypeSymbol)));
+ }
+
+ // Determines the interface to use to represent the type when GetRuntimeClassName is called.
+ // Given these are non WinRT types implementing WinRT interfaces, we find the most derived
+ // interface to represent it so that it applies for most scenarios.
+ void CheckForInterfaceToUseForRuntimeClassName(INamedTypeSymbol iface)
+ {
+ if (!checkForRuntimeClasName)
+ {
+ return;
+ }
+
+ if (interfaceToUseForRuntimeClassName is null || compilation.HasImplicitConversion(iface, interfaceToUseForRuntimeClassName))
+ {
+ interfaceToUseForRuntimeClassName = iface;
+ }
+ }
+ }
+
+ private static TypeFlags ComputeTypeFlags(ITypeSymbol symbol, Compilation compilation)
+ {
+ TypeFlags typeFlags = TypeFlags.None;
+
+ // Check for exception types
+ if (symbol.TypeKind is TypeKind.Class)
+ {
+ var exceptionType = compilation.GetTypeByMetadataName("System.Exception")!;
+
+ if (SymbolEqualityComparer.Default.Equals(symbol, exceptionType) ||
+ symbol.InheritsFromType(exceptionType))
+ {
+ typeFlags |= TypeFlags.Exception;
+ }
+ }
+
+ return typeFlags;
+ }
+
+ private static bool TryGetCompatibleWindowsRuntimeTypesForVariantType(INamedTypeSymbol type, TypeMapper mapper, Stack typeStack, Func isWinRTType, INamedTypeSymbol objectType, out IList compatibleTypes)
+ {
+ compatibleTypes = null;
+
+ // Out of all the C# interfaces which are valid WinRT interfaces and
+ // support covariance, they all only have one generic parameter,
+ // so scoping to only handle that.
+ if (type is not { IsGenericType: true, TypeParameters: [{ Variance: VarianceKind.Out }], TypeArguments: [{ IsValueType: false }] })
+ {
+ return false;
+ }
+
+ var definition = type.OriginalDefinition;
+ if (!isWinRTType(definition, mapper))
+ {
+ return false;
+ }
+
+ if (typeStack == null)
+ {
+ typeStack = new Stack();
+ }
+ else
+ {
+ if (typeStack.Contains(type))
+ {
+ return false;
+ }
+ }
+ typeStack.Push(type);
+
+ HashSet compatibleTypesForGeneric = new(SymbolEqualityComparer.Default);
+
+ if (isWinRTType(type.TypeArguments[0], mapper))
+ {
+ compatibleTypesForGeneric.Add(type.TypeArguments[0]);
+ }
+
+ foreach (var iface in type.TypeArguments[0].AllInterfaces)
+ {
+ if (isWinRTType(iface, mapper))
+ {
+ compatibleTypesForGeneric.Add(iface);
+ }
+
+ if (iface.IsGenericType
+ && TryGetCompatibleWindowsRuntimeTypesForVariantType(iface, mapper, typeStack, isWinRTType, objectType, out var compatibleIfaces))
+ {
+ compatibleTypesForGeneric.UnionWith(compatibleIfaces);
+ }
+ }
+
+ // BaseType reports null for interfaces, but interfaces still can be passed as an object.
+ // So we handle that separately.
+ var typeArgument = type.TypeArguments[0];
+ var baseType = typeArgument.TypeKind == TypeKind.Interface ? objectType : typeArgument.BaseType;
+ while (baseType != null)
+ {
+ if (isWinRTType(baseType, mapper))
+ {
+ compatibleTypesForGeneric.Add(baseType);
+ }
+ baseType = baseType.BaseType;
+ }
+
+ typeStack.Pop();
+
+ compatibleTypes = new List(compatibleTypesForGeneric.Count);
+ foreach (var compatibleType in compatibleTypesForGeneric)
+ {
+ compatibleTypes.Add(definition.Construct(compatibleType));
+ }
+
+ return true;
+ }
+
+ private static void GenerateVtableAttributes(
+ SourceProductionContext sourceProductionContext,
+ (ImmutableArray vtableAttributes, (CsWinRTAotOptimizerProperties properties, string escapedAssemblyName) context) value)
+ {
+ if (!value.context.properties.IsCsWinRTAotOptimizerEnabled)
+ {
+ return;
+ }
+
+ GenerateVtableAttributes(sourceProductionContext.AddSource, value.vtableAttributes, value.context.properties.IsCsWinRTComponent, value.context.escapedAssemblyName);
+ }
+
+ internal static string GenerateVtableEntry(VtableEntry vtableEntry, string escapedAssemblyName)
+ {
+ StringBuilder source = new();
+
+ foreach (var genericInterface in vtableEntry.GenericInterfaces)
+ {
+ source.AppendLine(GenericVtableInitializerStrings.GetInstantiationInitFunction(
+ genericInterface.GenericDefinition,
+ genericInterface.GenericParameters,
+ escapedAssemblyName));
+ }
+
+ if (vtableEntry.IsDelegate)
+ {
+ var @interface = vtableEntry.Interfaces.First();
+ source.AppendLine();
+ source.AppendLine($$"""
+ var delegateInterface = new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry
+ {
+ IID = global::ABI.{{@interface}}.IID,
+ Vtable = global::ABI.{{@interface}}.AbiToProjectionVftablePtr
+ };
+
+ return global::WinRT.DelegateTypeDetails<{{@interface}}>.GetExposedInterfaces(delegateInterface);
+ """);
+ }
+ else if (vtableEntry.Interfaces.Any())
+ {
+ source.AppendLine();
+ source.AppendLine($$"""
+ return new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry[]
+ {
+ """);
+
+ foreach (var @interface in vtableEntry.Interfaces)
+ {
+ var genericStartIdx = @interface.IndexOf('<');
+ var interfaceStaticsMethod = @interface[..(genericStartIdx == -1 ? @interface.Length : genericStartIdx)] + "Methods";
+ if (genericStartIdx != -1)
+ {
+ interfaceStaticsMethod += @interface[genericStartIdx..@interface.Length];
+ }
+
+ source.AppendLine($$"""
+ new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry
+ {
+ IID = global::ABI.{{interfaceStaticsMethod}}.IID,
+ Vtable = global::ABI.{{interfaceStaticsMethod}}.AbiToProjectionVftablePtr
+ },
+ """);
+ }
+ source.AppendLine($$"""
+ };
+ """);
+ }
+ else
+ {
+ source.AppendLine($$"""
+ return global::System.Array.Empty();
+ """);
+ }
+
+ return source.ToString();
+ }
+
+ internal static void GenerateVtableAttributes(Action addSource, ImmutableArray vtableAttributes, bool isCsWinRTComponentFromAotOptimizer, string escapedAssemblyName)
+ {
+ var vtableEntryToVtableClassName = new Dictionary();
+ StringBuilder vtableClassesSource = new();
+ bool firstVtableClass = true;
+
+ // Using ToImmutableHashSet to avoid duplicate entries from the use of partial classes by the developer
+ // to split out their implementation. When they do that, we will get multiple entries here for that
+ // and try to generate the same attribute and file with the same data as we use the semantic model
+ // to get all the symbol data rather than the data at an instance of a partial class definition.
+ foreach (var vtableAttribute in vtableAttributes.ToImmutableHashSet())
+ {
+ // If this is a WinRT component project and this call is coming
+ // from the AOT optimizer, then any public types are not handled
+ // right now as they are handled by the WinRT component source generator
+ // calling this.
+ if (((isCsWinRTComponentFromAotOptimizer && !vtableAttribute.IsPublic) || !isCsWinRTComponentFromAotOptimizer) &&
+ vtableAttribute.Interfaces.Any())
+ {
+ StringBuilder source = new();
+ if (!vtableAttribute.IsGlobalNamespace)
+ {
+ source.AppendLine($$"""
+ namespace {{vtableAttribute.Namespace}}
+ {
+ """);
+ }
+
+ // Check if this class shares the same vtable as another class. If so, reuse the same generated class for it.
+ VtableEntry entry = new(vtableAttribute.Interfaces, vtableAttribute.GenericInterfaces, vtableAttribute.IsDelegate);
+ bool vtableEntryExists = vtableEntryToVtableClassName.TryGetValue(entry, out var ccwClassName);
+ if (!vtableEntryExists)
+ {
+ var @namespace = vtableAttribute.IsGlobalNamespace ? "" : $"{vtableAttribute.Namespace}.";
+ ccwClassName = GeneratorHelper.EscapeTypeNameForIdentifier(@namespace + vtableAttribute.ClassName);
+ vtableEntryToVtableClassName.Add(entry, ccwClassName);
+ }
+
+ var escapedClassName = GeneratorHelper.EscapeTypeNameForIdentifier(vtableAttribute.ClassName);
+
+ // Simple case when the type is not nested
+ if (vtableAttribute.ClassHierarchy.IsEmpty)
+ {
+ if (!string.IsNullOrEmpty(vtableAttribute.RuntimeClassName))
+ {
+ source.AppendLine($$"""[global::WinRT.WinRTRuntimeClassName("{{vtableAttribute.RuntimeClassName}}")]""");
+ }
+
+ source.AppendLine($$"""
+ [global::WinRT.WinRTExposedType(typeof(global::WinRT.{{escapedAssemblyName}}VtableClasses.{{ccwClassName}}WinRTTypeDetails))]
+ partial class {{vtableAttribute.ClassName}}
+ {
+ }
+ """);
+ }
+ else
+ {
+ ReadOnlySpan classHierarchy = vtableAttribute.ClassHierarchy.AsSpan();
+
+ // If the type is nested, correctly nest the type definition
+ for (int i = classHierarchy.Length - 1; i > 0; i--)
+ {
+ source.AppendLine($$"""
+ partial {{classHierarchy[i].GetTypeKeyword()}} {{classHierarchy[i].QualifiedName}}
+ {
+ """);
+ }
+
+ // Define the inner-most item with the attribute
+ if (!string.IsNullOrEmpty(vtableAttribute.RuntimeClassName))
+ {
+ source.AppendLine($$"""[global::WinRT.WinRTRuntimeClassName("{{vtableAttribute.RuntimeClassName}}")]""");
+ }
+
+ source.AppendLine($$"""
+ [global::WinRT.WinRTExposedType(typeof(global::WinRT.{{escapedAssemblyName}}VtableClasses.{{ccwClassName}}WinRTTypeDetails))]
+ partial {{classHierarchy[0].GetTypeKeyword()}} {{classHierarchy[0].QualifiedName}}
+ {
+ }
+ """);
+
+ // Close all brackets
+ for (int i = classHierarchy.Length - 1; i > 0; i--)
+ {
+ source.AppendLine("}");
+ }
+ }
+
+ // Only generate class, if this is the first time we run into this set of vtables.
+ if (!vtableEntryExists)
+ {
+ if (firstVtableClass)
+ {
+ vtableClassesSource.AppendLine($$"""
+ namespace WinRT.{{escapedAssemblyName}}VtableClasses
+ {
+ """);
+ firstVtableClass = false;
+ }
+ else
+ {
+ vtableClassesSource.AppendLine();
+ }
+
+ vtableClassesSource.AppendLine($$"""
+ internal sealed class {{ccwClassName}}WinRTTypeDetails : global::WinRT.IWinRTExposedTypeDetails
+ {
+ public global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry[] GetExposedInterfaces()
+ {
+ """);
+
+ if (vtableAttribute.Interfaces.Any())
+ {
+ foreach (var genericInterface in vtableAttribute.GenericInterfaces)
+ {
+ vtableClassesSource.AppendLine(GenericVtableInitializerStrings.GetInstantiationInitFunction(
+ genericInterface.GenericDefinition,
+ genericInterface.GenericParameters,
+ escapedAssemblyName));
+ }
+
+ vtableClassesSource.AppendLine();
+ vtableClassesSource.AppendLine($$"""
+ return new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry[]
+ {
+ """);
+
+ foreach (var @interface in vtableAttribute.Interfaces)
+ {
+ var genericStartIdx = @interface.IndexOf('<');
+ var interfaceStaticsMethod = @interface[..(genericStartIdx == -1 ? @interface.Length : genericStartIdx)] + "Methods";
+ if (genericStartIdx != -1)
+ {
+ interfaceStaticsMethod += @interface[genericStartIdx..@interface.Length];
+ }
+
+ vtableClassesSource.AppendLine($$"""
+ new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry
+ {
+ IID = global::ABI.{{interfaceStaticsMethod}}.IID,
+ Vtable = global::ABI.{{interfaceStaticsMethod}}.AbiToProjectionVftablePtr
+ },
+ """);
+ }
+ vtableClassesSource.AppendLine($$"""
+ };
+ """);
+ }
+ else
+ {
+ vtableClassesSource.AppendLine($$"""
+ return global::System.Array.Empty();
+ """);
+ }
+
+ vtableClassesSource.AppendLine($$"""
+ }
+ }
+ """);
+ }
+
+ if (!vtableAttribute.IsGlobalNamespace)
+ {
+ source.AppendLine($@"}}");
+ }
+
+ string prefix = vtableAttribute.IsGlobalNamespace ? "" : $"{vtableAttribute.Namespace}.";
+ addSource($"{prefix}{escapedClassName}.WinRTVtable.g.cs", source.ToString());
+ }
+ }
+
+ if (vtableClassesSource.Length != 0)
+ {
+ vtableClassesSource.AppendLine("}");
+ addSource($"WinRTCCWVtable.g.cs", vtableClassesSource.ToString());
+ }
+ }
+
+ private static void GenerateCCWForGenericInstantiation(
+ SourceProductionContext sourceProductionContext,
+ (((ImmutableArray vtableAttributeList, ImmutableArray lookupTableList) interfacesToGenerate,
+ ImmutableArray componentGeneratorList) genericInterfaces,
+ (CsWinRTAotOptimizerProperties properties, string escapedAssemblyName) context) value)
+ {
+ if (!value.context.properties.IsCsWinRTAotOptimizerEnabled)
+ {
+ return;
+ }
+
+ HashSet genericInterfacesHashSet = new(value.genericInterfaces.interfacesToGenerate.vtableAttributeList);
+ if (value.context.properties.IsCsWinRTCcwLookupTableGeneratorEnabled)
+ {
+ genericInterfacesHashSet.UnionWith(value.genericInterfaces.interfacesToGenerate.lookupTableList);
+ }
+
+ // Remove all the generic interfaces that are beign generated by the component generator.
+ foreach (var i in value.genericInterfaces.componentGeneratorList)
+ {
+ genericInterfacesHashSet.Remove(i);
+ }
+
+ GenerateCCWForGenericInstantiation(sourceProductionContext.AddSource, genericInterfacesHashSet.ToImmutableArray(), value.context.escapedAssemblyName);
+ }
+
+ internal static void GenerateCCWForGenericInstantiation(Action addSource, ImmutableArray genericInterfaces, string escapedAssemblyName)
+ {
+ StringBuilder source = new();
+
+ if (genericInterfaces.Any())
+ {
+ source.AppendLine($$"""
+ using System;
+ using System.Runtime.InteropServices;
+ using System.Runtime.CompilerServices;
+
+ namespace WinRT.{{escapedAssemblyName}}GenericHelpers
+ {
+ """);
+ }
+
+ var updatedGenericInterfaces = GenericVtableInitializerStrings.AddDependentGenericInterfaces(genericInterfaces.ToImmutableHashSet());
+ foreach (var genericInterface in updatedGenericInterfaces)
+ {
+ source.AppendLine();
+ source.AppendLine("// " + genericInterface.Interface);
+ source.AppendLine(GenericVtableInitializerStrings.GetInstantiation(
+ genericInterface.GenericDefinition,
+ genericInterface.GenericParameters));
+ }
+
+ if (genericInterfaces.Any())
+ {
+ source.AppendLine("}");
+ addSource($"WinRTGenericInstantiation.g.cs", source.ToString());
+ }
+ }
+
+ private static bool NeedVtableOnLookupTable(SyntaxNode node)
+ {
+ return (node is InvocationExpressionSyntax invocation && invocation.ArgumentList.Arguments.Count != 0) ||
+ node is AssignmentExpressionSyntax ||
+ node is VariableDeclarationSyntax ||
+ node is PropertyDeclarationSyntax ||
+ node is ReturnStatementSyntax ||
+ node is CollectionExpressionSyntax;
+ }
+
+ private static EquatableArray GetVtableAttributesToAddOnLookupTable(
+ GeneratorSyntaxContext context,
+ TypeMapper typeMapper,
+ bool isCsWinRTComponent)
+ {
+ var isWinRTType = GeneratorHelper.IsWinRTType(context.SemanticModel.Compilation, isCsWinRTComponent);
+ return GetVtableAttributesToAddOnLookupTable(
+ context,
+ typeMapper,
+ GeneratorHelper.IsManagedOnlyType(context.SemanticModel.Compilation),
+ isWinRTType,
+ GeneratorHelper.IsWinRTClassOrInterface(context.SemanticModel.Compilation, isWinRTType, typeMapper));
+ }
+
+ private static EquatableArray GetVtableAttributesToAddOnLookupTable(
+ GeneratorSyntaxContext context,
+ TypeMapper typeMapper,
+ Func isManagedOnlyType,
+ Func isWinRTType,
+ Func isWinRTClassOrInterface)
+ {
+ HashSet visitedTypes = new(SymbolEqualityComparer.Default);
+ HashSet vtableAttributes = new();
+
+ if (context.Node is InvocationExpressionSyntax invocation)
+ {
+ var invocationSymbol = context.SemanticModel.GetSymbolInfo(invocation).Symbol;
+ if (invocationSymbol is IMethodSymbol methodSymbol &&
+ // Filter checks for boxing and casts to ones calling CsWinRT projected functions and
+ // functions within same assembly. Functions within same assembly can take a boxed value
+ // and end up calling a projection function (i.e. ones generated by XAML compiler)
+ // In theory, another library can also be called which can call a projected function
+ // but not handling those scenarios for now.
+ (isWinRTClassOrInterface(methodSymbol.ContainingSymbol, true) ||
+ SymbolEqualityComparer.Default.Equals(methodSymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly)))
+ {
+ // Get the concrete types directly from the argument rather than
+ // using what the method accepts, which might just be an interface, so
+ // that we can try to include any other WinRT interfaces implemented by
+ // that type on the CCW when it is marshaled.
+ for (int idx = 0, paramsIdx = 0; idx < invocation.ArgumentList.Arguments.Count; idx++)
+ {
+ if (methodSymbol.Parameters[paramsIdx].RefKind != RefKind.Out)
+ {
+ var argumentType = context.SemanticModel.GetTypeInfo(invocation.ArgumentList.Arguments[idx].Expression);
+ AddVtableAttributesForType(argumentType, methodSymbol.Parameters[paramsIdx].Type);
+ }
+
+ // The method parameter can be declared as params which means
+ // an array of arguments can be passed for it and it is the
+ // last argument.
+ if (!methodSymbol.Parameters[paramsIdx].IsParams)
+ {
+ paramsIdx++;
+ }
+ }
+ }
+ }
+ else if (context.Node is AssignmentExpressionSyntax assignment)
+ {
+ var isGeneratedBindableCustomPropertyClass = false;
+ var leftSymbol = context.SemanticModel.GetSymbolInfo(assignment.Left).Symbol;
+ // Check if we are assigning to a property that is a WinRT class / interface or
+ // a generated bindable custom property class as that will probably be binded to
+ // or is being assigned to another property in the same assembly as it might then be
+ // used elswhere after casting.
+ if (leftSymbol is IPropertySymbol propertySymbol &&
+ (isWinRTClassOrInterface(propertySymbol.ContainingSymbol, true) ||
+ (isGeneratedBindableCustomPropertyClass = GeneratorHelper.IsGeneratedBindableCustomPropertyClass(context.SemanticModel.Compilation, propertySymbol.ContainingSymbol)) ||
+ SymbolEqualityComparer.Default.Equals(propertySymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly)))
+ {
+ AddVtableAttributesForType(context.SemanticModel.GetTypeInfo(assignment.Right), propertySymbol.Type, isGeneratedBindableCustomPropertyClass);
+ }
+ else if (leftSymbol is IFieldSymbol fieldSymbol &&
+ // WinRT interfaces don't have fields, so we don't need to check for them.
+ (isWinRTClassOrInterface(fieldSymbol.ContainingSymbol, false) ||
+ (isGeneratedBindableCustomPropertyClass = GeneratorHelper.IsGeneratedBindableCustomPropertyClass(context.SemanticModel.Compilation, fieldSymbol.ContainingSymbol)) ||
+ SymbolEqualityComparer.Default.Equals(fieldSymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly)))
+ {
+ AddVtableAttributesForType(context.SemanticModel.GetTypeInfo(assignment.Right), fieldSymbol.Type, isGeneratedBindableCustomPropertyClass);
+ }
+ }
+ else if (context.Node is VariableDeclarationSyntax variableDeclaration)
+ {
+ // Detect scenarios where the variable declaration is to a boxed or cast type during initialization.
+ var leftSymbol = context.SemanticModel.GetSymbolInfo(variableDeclaration.Type).Symbol;
+ if (leftSymbol is INamedTypeSymbol namedType)
+ {
+ foreach (var variable in variableDeclaration.Variables)
+ {
+ if (variable.Initializer != null)
+ {
+ var instantiatedType = context.SemanticModel.GetTypeInfo(variable.Initializer.Value);
+ AddVtableAttributesForType(instantiatedType, namedType);
+ }
+ }
+ }
+ }
+ else if (context.Node is PropertyDeclarationSyntax propertyDeclaration)
+ {
+ // Detect scenarios where the property declaration has an initializer and is to a boxed or cast type during initialization.
+ if (propertyDeclaration.Initializer != null)
+ {
+ var leftSymbol = context.SemanticModel.GetSymbolInfo(propertyDeclaration.Type).Symbol;
+ if (leftSymbol is INamedTypeSymbol namedType)
+ {
+ var instantiatedType = context.SemanticModel.GetTypeInfo(propertyDeclaration.Initializer.Value);
+ AddVtableAttributesForType(instantiatedType, namedType);
+ }
+ }
+ else if (propertyDeclaration.ExpressionBody != null)
+ {
+ var leftSymbol = context.SemanticModel.GetSymbolInfo(propertyDeclaration.Type).Symbol;
+ if (leftSymbol is INamedTypeSymbol namedType)
+ {
+ var instantiatedType = context.SemanticModel.GetTypeInfo(propertyDeclaration.ExpressionBody.Expression);
+ AddVtableAttributesForType(instantiatedType, namedType);
+ }
+ }
+ }
+ else if (context.Node is ReturnStatementSyntax { Expression: not null } returnDeclaration)
+ {
+ // Detect scenarios where the method or property being returned from is doing a box or cast of the type
+ // in the return statement.
+ var returnSymbol = context.SemanticModel.GetTypeInfo(returnDeclaration.Expression);
+ var parent = returnDeclaration.Ancestors().OfType().FirstOrDefault();
+ if (parent is MethodDeclarationSyntax methodDeclaration)
+ {
+ var methodReturnSymbol = context.SemanticModel.GetSymbolInfo(methodDeclaration.ReturnType).Symbol;
+ if (methodReturnSymbol is ITypeSymbol typeSymbol)
+ {
+ AddVtableAttributesForType(returnSymbol, typeSymbol);
+ }
+ }
+ else if (parent is BasePropertyDeclarationSyntax propertyDeclarationSyntax)
+ {
+ var propertyTypeSymbol = context.SemanticModel.GetSymbolInfo(propertyDeclarationSyntax.Type).Symbol;
+ if (propertyTypeSymbol is ITypeSymbol typeSymbol)
+ {
+ AddVtableAttributesForType(returnSymbol, typeSymbol);
+ }
+ }
+ }
+#if ROSLYN_4_12_0_OR_GREATER
+ else if (context.Node is CollectionExpressionSyntax collectionExpression)
+ {
+ // Detect collection expressions scenarios targeting interfaces, where we can rely on the concrete type
+ if (context.SemanticModel.GetOperation(collectionExpression) is ICollectionExpressionOperation operation &&
+ operation.Type is INamedTypeSymbol { TypeKind: TypeKind.Interface, IsGenericType: true, IsUnboundGenericType: false, ConstructedFrom: not null } collectionType)
+ {
+ // Case 1: empty collection expression targeting 'IEnumerable', 'IReadOnlyCollection', or 'IReadOnlyList'.
+ // In this case, we can rely on Roslyn using an empty array. So let's pretend we saw it, and gather it for lookups.
+ if (operation.Elements.IsEmpty &&
+ collectionType.ConstructedFrom.SpecialType is
+ SpecialType.System_Collections_Generic_IEnumerable_T or
+ SpecialType.System_Collections_Generic_IReadOnlyCollection_T or
+ SpecialType.System_Collections_Generic_IReadOnlyList_T)
+ {
+ IArrayTypeSymbol arrayTypeSymbol = context.SemanticModel.Compilation.CreateArrayTypeSymbol(collectionType.TypeArguments[0], rank: 1);
+
+ AddVtableAttributesForTypeDirect(arrayTypeSymbol, null, collectionType);
+ }
+ else if (collectionType.ConstructedFrom.SpecialType is SpecialType.System_Collections_Generic_ICollection_T or SpecialType.System_Collections_Generic_IList_T)
+ {
+ // Case 2: a collection expression (empty or not) targeting 'ICollection' or 'IList'.
+ // In this case, Roslyn guarantees that 'List' will be used, so we can gather that type.
+ INamedTypeSymbol listOfTSymbol = context.SemanticModel.Compilation.GetTypeByMetadataName("System.Collections.Generic.List`1")!;
+ INamedTypeSymbol constructedListSymbol = listOfTSymbol.Construct(collectionType.TypeArguments[0]);
+
+ AddVtableAttributesForTypeDirect(constructedListSymbol, null, collectionType);
+ }
+ }
+ }
+#endif
+
+ return vtableAttributes.ToImmutableArray();
+
+ // Helper to directly use 'AddVtableAttributesForTypeDirect' with 'TypeInfo' values
+ void AddVtableAttributesForType(Microsoft.CodeAnalysis.TypeInfo instantiatedType, ITypeSymbol convertedToTypeSymbol, bool isGeneratedBindableCustomPropertyClass = false)
+ {
+ AddVtableAttributesForTypeDirect(instantiatedType.Type, instantiatedType.ConvertedType, convertedToTypeSymbol, isGeneratedBindableCustomPropertyClass);
+ }
+
+ // This handles adding vtable information for types for which we can not directly put an attribute on them.
+ // This includes generic types that are boxed and and non-WinRT types for which our AOT source generator hasn't
+ // ran on but implements WinRT interfaces.
+ void AddVtableAttributesForTypeDirect(ITypeSymbol instantiatedTypeSymbol, ITypeSymbol instantiatedConvertedTypeSymbol, ITypeSymbol convertedToTypeSymbol, bool isGeneratedBindableCustomPropertyClass = false)
+ {
+ // This handles the case where there is an WinRT array possibly being assigned
+ // to an object type or a list. In this case, the IList interfaces of the array
+ // type need to be put on the CCW.
+ if (instantiatedTypeSymbol is IArrayTypeSymbol arrayType)
+ {
+ if (convertedToTypeSymbol is not IArrayTypeSymbol &&
+ // Make sure we aren't just assigning it to a value type such as ReadOnlySpan
+ !convertedToTypeSymbol.IsValueType)
+ {
+ if (visitedTypes.Contains(arrayType))
+ {
+ return;
+ }
+ visitedTypes.Add(arrayType);
+
+ var vtableAtribute = GetVtableAttributeToAdd(arrayType, isManagedOnlyType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
+ if (vtableAtribute != default)
+ {
+ vtableAttributes.Add(vtableAtribute);
+ }
+
+ // Also add the enumerator type to the lookup table as the native caller can call it.
+ AddEnumeratorAdapterForType(arrayType.ElementType, typeMapper, context.SemanticModel.Compilation, isManagedOnlyType, isWinRTType, vtableAttributes);
+ }
+ }
+ else if (instantiatedTypeSymbol is not null || instantiatedConvertedTypeSymbol is not null)
+ {
+ // Type might be null such as for lambdas, so check converted type in that case.
+ instantiatedTypeSymbol ??= instantiatedConvertedTypeSymbol;
+
+ if (visitedTypes.Contains(instantiatedTypeSymbol))
+ {
+ return;
+ }
+ visitedTypes.Add(instantiatedTypeSymbol);
+
+ // This handles the case where a generic delegate is passed to a parameter
+ // statically declared as an object and thereby we won't be able to detect
+ // its actual type and handle it at compile time within the generated projection.
+ // When it is not declared as an object parameter but rather the generic delegate
+ // type itself, the generated marshaler code in the function makes sure the vtable
+ // information is available.
+ if (instantiatedTypeSymbol.TypeKind == TypeKind.Delegate &&
+ instantiatedTypeSymbol.MetadataName.Contains("`") &&
+ isWinRTType(instantiatedTypeSymbol, typeMapper) &&
+ convertedToTypeSymbol.SpecialType == SpecialType.System_Object)
+ {
+ var argumentClassNamedTypeSymbol = instantiatedTypeSymbol as INamedTypeSymbol;
+ var vtableAtribute = GetVtableAttributeToAdd(instantiatedTypeSymbol, isManagedOnlyType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
+ if (vtableAtribute != default)
+ {
+ vtableAttributes.Add(vtableAtribute);
+ }
+ }
+
+ // This handles the case where the source generator wasn't able to run
+ // and put the WinRTExposedType attribute on the class. This can be in the
+ // scenario where the caller defined their own generic class and
+ // pass it as a parameter. With generic classes, the interface itself
+ // might be generic too and due to that we handle it here.
+ // This also handles the case where the type being passed is from a different
+ // library which happened to not run the AOT optimizer. So as a best effort,
+ // we handle it here.
+ if (instantiatedTypeSymbol.TypeKind == TypeKind.Class)
+ {
+ bool addClassOnLookupTable = false;
+ if (instantiatedTypeSymbol.MetadataName.Contains("`"))
+ {
+ addClassOnLookupTable =
+ !GeneratorHelper.HasWinRTExposedTypeAttribute(instantiatedTypeSymbol) &&
+ // If the type is defined in the same assembly as what the source generator is running on,
+ // we let the WinRTExposedType attribute generator handle it. The only scenario the generator
+ // doesn't handle which we handle here is if it is a generic type implementing generic WinRT interfaces.
+ (!SymbolEqualityComparer.Default.Equals(instantiatedTypeSymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly) ||
+ GeneratorHelper.HasNonInstantiatedWinRTGeneric(instantiatedTypeSymbol.OriginalDefinition, typeMapper)) &&
+ // Make sure the type we are passing is being boxed or cast to another interface or being assigned to a member in a bindable class.
+ (!SymbolEqualityComparer.Default.Equals(instantiatedTypeSymbol, convertedToTypeSymbol) || isGeneratedBindableCustomPropertyClass);
+ }
+ else if (!isWinRTType(instantiatedTypeSymbol, typeMapper))
+ {
+ addClassOnLookupTable =
+ !GeneratorHelper.HasWinRTExposedTypeAttribute(instantiatedTypeSymbol) &&
+ // If the type is defined in the same assembly as what the source generator is running on,
+ // we let the WinRTExposedType attribute generator handle it.
+ !SymbolEqualityComparer.Default.Equals(instantiatedTypeSymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly) &&
+ // Make sure the type we are passing is being boxed or cast to another interface or being assigned to a member in a bindable class.
+ (!SymbolEqualityComparer.Default.Equals(instantiatedTypeSymbol, convertedToTypeSymbol) || isGeneratedBindableCustomPropertyClass);
+ }
+
+ if (addClassOnLookupTable)
+ {
+ var vtableAtribute = GetVtableAttributeToAdd(instantiatedTypeSymbol, isManagedOnlyType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
+ if (vtableAtribute != default)
+ {
+ vtableAttributes.Add(vtableAtribute);
+ }
+
+ AddVtableAdapterTypeForKnownInterface(instantiatedTypeSymbol, context.SemanticModel.Compilation, isManagedOnlyType, isWinRTType, typeMapper, vtableAttributes);
+ }
+ }
+ }
+ }
+ }
+
+ private static EquatableArray GetVtableAttributesToAddOnLookupTable(
+ GeneratorAttributeSyntaxContext context,
+ TypeMapper typeMapper,
+ bool isCsWinRTComponent)
+ {
+ var isManagedOnlyType = GeneratorHelper.IsManagedOnlyType(context.SemanticModel.Compilation);
+ var isWinRTType = GeneratorHelper.IsWinRTType(context.SemanticModel.Compilation, isCsWinRTComponent);
+ HashSet vtableAttributes = new();
+
+ foreach (var attributeData in context.Attributes)
+ {
+ if (attributeData.ConstructorArguments is [{ Kind: TypedConstantKind.Type, Value : ITypeSymbol vtableType }])
+ {
+ if (vtableType is IArrayTypeSymbol arrayType)
+ {
+ var vtableAtribute = GetVtableAttributeToAdd(arrayType, isManagedOnlyType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
+ if (vtableAtribute != default)
+ {
+ vtableAttributes.Add(vtableAtribute);
+ }
+
+ // Also add the enumerator type to the lookup table as the native caller can call it.
+ AddEnumeratorAdapterForType(arrayType.ElementType, typeMapper, context.SemanticModel.Compilation, isManagedOnlyType, isWinRTType, vtableAttributes);
+ }
+ else
+ {
+ var vtableAtribute = GetVtableAttributeToAdd(vtableType, isManagedOnlyType, isWinRTType, typeMapper, context.SemanticModel.Compilation, false);
+ if (vtableAtribute != default)
+ {
+ vtableAttributes.Add(vtableAtribute);
+ }
+
+ AddVtableAdapterTypeForKnownInterface(vtableType, context.SemanticModel.Compilation, isManagedOnlyType, isWinRTType, typeMapper, vtableAttributes);
+ }
+ }
+ }
+
+ return vtableAttributes.ToImmutableArray();
+ }
+
+ // Any of the IEnumerable interfaces on the vtable can be used to get the enumerator. Given IEnumerable is
+ // a covariant interface, it means that we can end up getting an instance of the enumerable adapter for any one
+ // of those covariant interfaces and thereby need vtable lookup entries for all of them.
+ private static void AddEnumeratorAdapterForType(
+ ITypeSymbol type,
+ TypeMapper mapper,
+ Compilation compilation,
+ Func isManagedOnlyType,
+ Func isWinRTType,
+ HashSet vtableAttributes)
+ {
+ var enumerableType = compilation.GetTypeByMetadataName("System.Collections.Generic.IEnumerable`1");
+ if (enumerableType != null)
+ {
+ var constructedEnumerableType = enumerableType.Construct(type);
+ if (isWinRTType(constructedEnumerableType, mapper) &&
+ !GeneratorHelper.IsInternalInterfaceFromReferences(constructedEnumerableType, compilation.Assembly))
+ {
+ AddEnumeratorAdapter(constructedEnumerableType);
+ }
+
+ if (TryGetCompatibleWindowsRuntimeTypesForVariantType(constructedEnumerableType, mapper, null, isWinRTType, compilation.ObjectType, out var compatibleIfaces))
+ {
+ foreach (var compatibleIface in compatibleIfaces)
+ {
+ if (compatibleIface.MetadataName == "IEnumerable`1" &&
+ !GeneratorHelper.IsInternalInterfaceFromReferences(compatibleIface, compilation.Assembly))
+ {
+ AddEnumeratorAdapter(compatibleIface);
+ }
+ }
+ }
+ }
+
+ void AddEnumeratorAdapter(INamedTypeSymbol iface)
+ {
+ var enumeratorAdapterType = compilation.GetTypeByMetadataName("ABI.System.Collections.Generic.ToAbiEnumeratorAdapter`1");
+ if (enumeratorAdapterType != null)
+ {
+ var constructedEnumeratorAdapterType = enumeratorAdapterType.Construct(iface.TypeArguments[0]);
+ var vtableAttribute = GetVtableAttributeToAdd(constructedEnumeratorAdapterType, isManagedOnlyType, isWinRTType, mapper, compilation, false);
+ if (vtableAttribute != default)
+ {
+ vtableAttributes.Add(vtableAttribute);
+ }
+ }
+ }
+ }
+
+ internal static void AddVtableAdapterTypeForKnownInterface(
+ ITypeSymbol classType,
+ Compilation compilation,
+ Func isManagedOnlyType,
+ Func isWinRTType,
+ TypeMapper mapper,
+ HashSet vtableAttributes)
+ {
+ // If the type is blocked for marshalling, don't generate any code for any interfaces
+ if (isManagedOnlyType(classType))
+ {
+ return;
+ }
+
+ foreach (var iface in classType.AllInterfaces)
+ {
+ if (iface.MetadataName == "IEnumerable`1")
+ {
+ AddEnumeratorAdapterForType(iface.TypeArguments[0], mapper, compilation, isManagedOnlyType, isWinRTType, vtableAttributes);
+ }
+ else if (iface.MetadataName == "IDictionary`2")
+ {
+ LookupAndAddVtableAttributeForGenericType("System.Collections.ObjectModel.ReadOnlyDictionary`2", iface.TypeArguments);
+ LookupAndAddVtableAttributeForGenericType("System.Collections.Generic.KeyValuePair`2", iface.TypeArguments);
+ LookupAndAddVtableAttributeForGenericType("ABI.System.Collections.Generic.ConstantSplittableMap`2", iface.TypeArguments);
+ }
+ else if (iface.MetadataName == "IList`1")
+ {
+ LookupAndAddVtableAttributeForGenericType("System.Collections.ObjectModel.ReadOnlyCollection`1", iface.TypeArguments);
+ }
+ }
+
+ if (classType is INamedTypeSymbol namedType && IsDerivedFromOrIsObservableCollection(namedType))
+ {
+ // ObservableCollection make use of two internal built-in types as part of its
+ // implementation for INotifyPropertyChanged. Handling those manually here.
+ var genericInterfaces = new List() { "System.Collections.IList", "System.Collections.IEnumerable" };
+ var mapping = mapper.GetMappedType("System.Collections.IList").GetMapping();
+ var runtimeClassName = mapping.Item1 + "." + mapping.Item2;
+
+ // System.Collections.Specialized.ReadOnlyList
+ vtableAttributes.Add(
+ new VtableAttribute(
+ "System.Collections.Specialized",
+ false,
+ "ReadOnlyList",
+ ImmutableArray.Empty,
+ "System.Collections.Specialized.ReadOnlyList",
+ genericInterfaces.ToImmutableArray(),
+ ImmutableArray.Empty,
+ false,
+ false,
+ false,
+ runtimeClassName));
+
+ // System.Collections.Specialized.SingleItemReadOnlyList
+ vtableAttributes.Add(
+ new VtableAttribute(
+ "System.Collections.Specialized",
+ false,
+ "SingleItemReadOnlyList",
+ ImmutableArray.Empty,
+ "System.Collections.Specialized.SingleItemReadOnlyList",
+ genericInterfaces.ToImmutableArray(),
+ ImmutableArray.Empty,
+ false,
+ false,
+ false,
+ runtimeClassName));
+ }
+
+ void LookupAndAddVtableAttributeForGenericType(string type, ImmutableArray genericArgs)
+ {
+ var genericType = compilation.GetTypeByMetadataName(type);
+ if (genericType != default)
+ {
+ var constructedGenericType = genericType.Construct([.. genericArgs]);
+ var vtableAttribute = GetVtableAttributeToAdd(constructedGenericType, isManagedOnlyType, isWinRTType, mapper, compilation, false);
+ if (vtableAttribute != default)
+ {
+ vtableAttributes.Add(vtableAttribute);
+ }
+ }
+ }
+
+ bool IsDerivedFromOrIsObservableCollection(INamedTypeSymbol namedType)
+ {
+ return namedType.MetadataName == "ObservableCollection`1" ||
+ (namedType.BaseType is not null && IsDerivedFromOrIsObservableCollection(namedType.BaseType));
+ }
+ }
+
+ private static void GenerateVtableLookupTable(
+ SourceProductionContext sourceProductionContext,
+ (ImmutableArray vtableAttributes, (CsWinRTAotOptimizerProperties properties, string)) value)
+ {
+ GenerateVtableLookupTable(sourceProductionContext.AddSource, value);
+ }
+
+ internal static void GenerateVtableLookupTable(
+ Action addSource,
+ (ImmutableArray vtableAttributes, (CsWinRTAotOptimizerProperties properties, string escapedAssemblyName) context) value,
+ bool isComponentGenerator = false)
+ {
+ if (!value.context.properties.IsCsWinRTAotOptimizerEnabled || !value.context.properties.IsCsWinRTCcwLookupTableGeneratorEnabled)
+ {
+ return;
+ }
+
+ StringBuilder source = new();
+ string classPrefix = isComponentGenerator ? "Authoring" : "";
+
+ // The generated lookup table is based on lookup by string rather than type to avoid 2 different issues:
+ //
+ // 1) We can run into nested private types in generics which we can't reference from here but still need to be able to create the vtable for.
+ // 2) While looking for a type in the lookup table, you can trigger module initializers for other modules
+ // referenced and that can run into issues because they can have their own lookup tables that
+ // get registered in their module initializer, but will fail to due to the reader writer lock we have around it
+ // (i.e. we are traversing the lookup tables here while one is being registered).
+ //
+ // Note: just like with the authoring metadata function, we don't use a method group expression but rather construct a 'Func' ourselves.
+ // This is done to opt-out of the implicit caching that Roslyn does. We don't need that extra code and overhead, as this is only done once.
+ var hasRuntimeClasNameEntries = value.vtableAttributes.Any(static v => !string.IsNullOrEmpty(v.RuntimeClassName));
+ if (value.vtableAttributes.Any())
+ {
+ source.AppendLine($$"""
+ using System;
+ using System.Runtime.InteropServices;
+ using System.Runtime.CompilerServices;
+
+ namespace WinRT.{{value.context.escapedAssemblyName}}GenericHelpers
+ {
+
+ internal static class {{classPrefix}}GlobalVtableLookup
+ {
+
+ [System.Runtime.CompilerServices.ModuleInitializer]
+ internal static void InitializeGlobalVtableLookup()
+ {
+ ComWrappersSupport.RegisterTypeComInterfaceEntriesLookup(new Func(LookupVtableEntries));
+ {{(hasRuntimeClasNameEntries ? "ComWrappersSupport.RegisterTypeRuntimeClassNameLookup(new Func(LookupRuntimeClassName));" : "")}}
+ }
+
+ private static ComWrappers.ComInterfaceEntry[] LookupVtableEntries(Type type)
+ {
+ string typeName = type.ToString();
+ """);
+ }
+
+ // We gather all the class names that have the same vtable and generate it
+ // as part of one if to reduce generated code.
+ var vtableEntryToClassNameList = new Dictionary>();
+ foreach (var vtableAttribute in value.vtableAttributes.ToImmutableHashSet())
+ {
+ VtableEntry entry = new(vtableAttribute.Interfaces, vtableAttribute.GenericInterfaces, vtableAttribute.IsDelegate);
+ if (!vtableEntryToClassNameList.TryGetValue(entry, out var classNameList))
+ {
+ classNameList = new List();
+ vtableEntryToClassNameList.Add(entry, classNameList);
+ }
+ classNameList.Add(vtableAttribute.VtableLookupClassName);
+ }
+
+ foreach (var vtableEntry in vtableEntryToClassNameList)
+ {
+ source.AppendLine($$"""
+ if (typeName == "{{vtableEntry.Value[0]}}"
+ """);
+
+ for (var i = 1; i < vtableEntry.Value.Count; i++)
+ {
+ source.AppendLine($$"""
+ || typeName == "{{vtableEntry.Value[i]}}"
+ """);
+ }
+
+ source.AppendLine($$"""
+ )
+ {
+ {{GenerateVtableEntry(vtableEntry.Key, value.context.escapedAssemblyName)}}
+ }
+ """);
+ }
+
+ if (value.vtableAttributes.Any())
+ {
+ source.AppendLine($$"""
+ return default;
+ }
+ """);
+
+ if (hasRuntimeClasNameEntries)
+ {
+ source.AppendLine($$"""
+ private static string LookupRuntimeClassName(Type type)
+ {
+ string typeName = type.ToString();
+ """);
+
+ var runtimeClassNameToClassNameList = new Dictionary>();
+ foreach (var vtableAttribute in value.vtableAttributes.ToImmutableHashSet().Where(static v => !string.IsNullOrEmpty(v.RuntimeClassName)))
+ {
+ if (!runtimeClassNameToClassNameList.TryGetValue(vtableAttribute.RuntimeClassName, out var classNameList))
+ {
+ classNameList = new List();
+ runtimeClassNameToClassNameList.Add(vtableAttribute.RuntimeClassName, classNameList);
+ }
+ classNameList.Add(vtableAttribute.VtableLookupClassName);
+ }
+
+ foreach (var entry in runtimeClassNameToClassNameList)
+ {
+ source.AppendLine($$"""
+ if (typeName == "{{entry.Value[0]}}"
+ """);
+
+ for (var i = 1; i < entry.Value.Count; i++)
+ {
+ source.AppendLine($$"""
+ || typeName == "{{entry.Value[i]}}"
+ """);
+ }
+
+ source.AppendLine($$"""
+ )
+ {
+ return "{{entry.Key}}";
+ }
+ """);
+ }
+
+ source.AppendLine($$"""
+ return default;
+ }
+ """);
+ }
+
+ source.AppendLine($$"""
+ }
+ }
+ """);
+ addSource($"WinRT{classPrefix}GlobalVtableLookup.g.cs", source.ToString());
+ }
+ }
+
+ private static void GenerateBindableCustomProperties(
+ SourceProductionContext sourceProductionContext,
+ (ImmutableArray bindableCustomProperties, CsWinRTAotOptimizerProperties properties) value)
+ {
+ if (!value.properties.IsCsWinRTAotOptimizerEnabled || value.bindableCustomProperties.Length == 0)
+ {
+ return;
+ }
+
+ StringBuilder source = new();
+
+ foreach (var bindableCustomProperties in value.bindableCustomProperties)
+ {
+ if (!bindableCustomProperties.IsGlobalNamespace)
+ {
+ source.AppendLine($$"""
+ namespace {{bindableCustomProperties.Namespace}}
+ {
+ """);
+ }
+
+ var escapedClassName = GeneratorHelper.EscapeTypeNameForIdentifier(bindableCustomProperties.TypeName);
+
+ ReadOnlySpan classHierarchy = bindableCustomProperties.ClassHierarchy.AsSpan();
+ // If the type is nested, correctly nest the type definition
+ for (int i = classHierarchy.Length - 1; i > 0; i--)
+ {
+ source.AppendLine($$"""
+ partial {{classHierarchy[i].GetTypeKeyword()}} {{classHierarchy[i].QualifiedName}}
+ {
+ """);
+ }
+
+ string typeKeyword = TypeInfo.GetTypeKeyword(bindableCustomProperties.TypeKind, bindableCustomProperties.IsRecord);
+
+ source.AppendLine($$"""
+ partial {{typeKeyword}} {{(classHierarchy.IsEmpty ? bindableCustomProperties.TypeName : classHierarchy[0].QualifiedName)}} : global::Microsoft.UI.Xaml.Data.IBindableCustomPropertyImplementation
+ {
+ global::Microsoft.UI.Xaml.Data.BindableCustomProperty global::Microsoft.UI.Xaml.Data.IBindableCustomPropertyImplementation.GetProperty(string name)
+ {
+ """);
+
+ foreach (var property in bindableCustomProperties.Properties.Where(static p => !p.IsIndexer))
+ {
+ var instanceAccessor = property.IsStatic ? bindableCustomProperties.QualifiedClassName : $$"""(({{bindableCustomProperties.QualifiedClassName}})instance)""";
+
+ source.AppendLine($$"""
+ if (name == "{{property.Name}}")
+ {
+ return new global::Microsoft.UI.Xaml.Data.BindableCustomProperty(
+ {{GetBoolAsString(property.CanRead)}},
+ {{GetBoolAsString(property.CanWrite)}},
+ "{{property.Name}}",
+ typeof({{property.Type}}),
+ {{ (property.CanRead ? $$"""static (instance) => {{instanceAccessor}}.{{property.Name}}""" : "null") }},
+ {{ (property.CanWrite ? $$"""static (instance, value) => {{instanceAccessor}}.{{property.Name}} = ({{property.Type}})value""" : "null") }},
+ null,
+ null);
+ }
+ """);
+ }
+
+ source.AppendLine($$"""
+ return default;
+ }
+
+ global::Microsoft.UI.Xaml.Data.BindableCustomProperty global::Microsoft.UI.Xaml.Data.IBindableCustomPropertyImplementation.GetProperty(global::System.Type indexParameterType)
+ {
+ """);
+
+ foreach (var property in bindableCustomProperties.Properties.Where(static p => p.IsIndexer))
+ {
+ var instanceAccessor = property.IsStatic ? bindableCustomProperties.QualifiedClassName : $$"""(({{bindableCustomProperties.QualifiedClassName}})instance)""";
+
+ source.AppendLine($$"""
+ if (indexParameterType == typeof({{property.IndexerType}}))
+ {
+ return new global::Microsoft.UI.Xaml.Data.BindableCustomProperty(
+ {{GetBoolAsString(property.CanRead)}},
+ {{GetBoolAsString(property.CanWrite)}},
+ "{{property.Name}}",
+ typeof({{property.Type}}),
+ null,
+ null,
+ {{ (property.CanRead ? $$"""static (instance, index) => {{instanceAccessor}}[({{property.IndexerType}})index]""" : "null") }},
+ {{ (property.CanWrite ? $$"""static (instance, value, index) => {{instanceAccessor}}[({{property.IndexerType}})index] = ({{property.Type}})value""" : "null") }});
+ }
+ """);
+ }
+
+ source.AppendLine($$"""
+ return default;
+ }
+ }
+ """);
+
+ // Close all brackets
+ for (int i = classHierarchy.Length - 1; i > 0; i--)
+ {
+ source.AppendLine("}");
+ }
+
+ if (!bindableCustomProperties.IsGlobalNamespace)
+ {
+ source.AppendLine($@"}}");
+ }
+
+ source.AppendLine();
+ }
+
+ sourceProductionContext.AddSource("WinRTCustomBindableProperties.g.cs", source.ToString());
+
+ static string GetBoolAsString(bool value) => value ? "true" : "false";
+ }
+ }
+
+ internal readonly record struct GenericParameter(
+ string ProjectedType,
+ string AbiType,
+ TypeKind TypeKind,
+ TypeFlags TypeFlags);
+
+ internal readonly record struct GenericInterface(
+ string Interface,
+ string GenericDefinition,
+ EquatableArray GenericParameters);
+
+ internal sealed record VtableAttribute(
+ string Namespace,
+ bool IsGlobalNamespace,
+ string ClassName,
+ EquatableArray ClassHierarchy,
+ string VtableLookupClassName,
+ EquatableArray Interfaces,
+ EquatableArray GenericInterfaces,
+ bool IsArray,
+ bool IsDelegate,
+ bool IsPublic,
+ string RuntimeClassName = default);
+
+ sealed record VtableEntry(
+ EquatableArray Interfaces,
+ EquatableArray GenericInterfaces,
+ bool IsDelegate);
+
+ internal readonly record struct BindableCustomProperty(
+ string Name,
+ string Type,
+ bool CanRead,
+ bool CanWrite,
+ bool IsIndexer,
+ string IndexerType,
+ bool IsStatic);
+
+ internal readonly record struct BindableCustomProperties(
+ string Namespace,
+ bool IsGlobalNamespace,
+ string TypeName,
+ TypeKind TypeKind,
+ bool IsRecord,
+ EquatableArray ClassHierarchy,
+ string QualifiedClassName,
+ EquatableArray Properties);
+
+ internal readonly record struct CsWinRTAotOptimizerProperties(
+ bool IsCsWinRTAotOptimizerEnabled,
+ bool IsCsWinRTComponent,
+ bool IsCsWinRTCcwLookupTableGeneratorEnabled,
+ bool IsCsWinRTAotOptimizerInAutoMode);
+
+ ///
+ /// Additional flags for discovered types.
+ ///
+ [Flags]
+ internal enum TypeFlags
+ {
+ None = 0x0,
+
+ ///
+ /// The type derives from .
+ ///
+ Exception = 0x1 << 0
+ }
+
+ ///
+ /// A model describing a type info in a type hierarchy.
+ ///
+ /// The qualified name for the type.
+ /// The kind of the type in the hierarchy.
+ /// Whether the type is a record type.
+ // Ported from https://github.com/Sergio0694/ComputeSharp
+ internal sealed record TypeInfo(string QualifiedName, TypeKind Kind, bool IsRecord)
+ {
+ ///
+ /// Gets the keyword for the current type kind.
+ ///
+ /// The keyword for the current type kind.
+ public string GetTypeKeyword()
+ {
+ return GetTypeKeyword(Kind, IsRecord);
+ }
+
+ ///
+ /// Gets the keyword for a given kind and record option.
+ ///
+ /// The type kind.
+ /// Whether the type is a record.
+ /// The keyword for a given kind and record option.
+ public static string GetTypeKeyword(TypeKind kind, bool isRecord)
+ {
+ return kind switch
+ {
+ TypeKind.Struct when isRecord => "record struct",
+ TypeKind.Struct => "struct",
+ TypeKind.Interface => "interface",
+ TypeKind.Class when isRecord => "record",
+ _ => "class"
+ };
+ }
+ }
+}
diff --git a/src/Authoring/WinRT.SourceGenerator/CodeFixers/RuntimeClassCastCodeFixer.cs b/src/Authoring/WinRT.SourceGenerator/CodeFixers/RuntimeClassCastCodeFixer.cs
new file mode 100644
index 000000000..d4a03e59f
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator/CodeFixers/RuntimeClassCastCodeFixer.cs
@@ -0,0 +1,245 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+// Ported from 'MissingAttributeCodeFixer' in ComputeSharp (https://github.com/Sergio0694/ComputeSharp).
+// Licensed under the MIT License (MIT) (see: https://github.com/Sergio0694/ComputeSharp?tab=MIT-1-ov-file).
+// Source: https://github.com/Sergio0694/ComputeSharp/blob/main/src/ComputeSharp.CodeFixing/MissingAttributeCodeFixer.cs.
+
+#if ROSLYN_4_12_0_OR_GREATER
+
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Composition;
+using System.Diagnostics.CodeAnalysis;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CodeActions;
+using Microsoft.CodeAnalysis.CodeFixes;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.Editing;
+using Microsoft.CodeAnalysis.Simplification;
+using Microsoft.CodeAnalysis.Text;
+
+#nullable enable
+
+namespace Generator;
+
+///
+/// A code fixer that adds the annotations for .
+///
+[ExportCodeFixProvider(LanguageNames.CSharp)]
+[Shared]
+public sealed class RuntimeClassCastCodeFixer : CodeFixProvider
+{
+ ///
+ public sealed override ImmutableArray FixableDiagnosticIds { get; } = ["CsWinRT1034", "CsWinRT1035"];
+
+ ///
+ public sealed override Microsoft.CodeAnalysis.CodeFixes.FixAllProvider? GetFixAllProvider()
+ {
+ return new FixAllProvider(this);
+ }
+
+ ///
+ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context)
+ {
+ Diagnostic diagnostic = context.Diagnostics[0];
+ TextSpan diagnosticSpan = context.Span;
+
+ SyntaxNode? root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
+
+ // Retrieve the property passed by the analyzer
+ if (!diagnostic.Properties.TryGetValue(RuntimeClassCastAnalyzer.WindowsRuntimeTypeId, out string? windowsRuntimeTypeId))
+ {
+ return;
+ }
+
+ // Get the member declaration from the target diagnostic
+ if (TryGetTargetNode(root, diagnosticSpan, out SyntaxNode? targetNode))
+ {
+ // Register the code fix to update the return type to be Task instead
+ context.RegisterCodeFix(
+ CodeAction.Create(
+ title: "Add '[DynamicWindowsRuntimeCast]' attribute",
+ createChangedDocument: token => AddMissingAttributeAsync(context.Document, root, targetNode, windowsRuntimeTypeId, token),
+ equivalenceKey: "Add '[DynamicWindowsRuntimeCast]' attribute"),
+ diagnostic);
+ }
+ }
+
+ ///
+ /// Tries to resolve the target syntax node to edit.
+ ///
+ /// The root of the document to edit.
+ /// The target span for the node to retrieve.
+ /// The resulting node to edit, if found.
+ /// Whether or not was retrieved correctly.
+ private static bool TryGetTargetNode([NotNullWhen(true)] SyntaxNode? root, TextSpan span, [NotNullWhen(true)] out SyntaxNode? result)
+ {
+ result = root?.FindNode(span).FirstAncestorOrSelf(static n =>
+ n.IsKind(SyntaxKind.FieldDeclaration) ||
+ n.IsKind(SyntaxKind.MethodDeclaration) ||
+ n.IsKind(SyntaxKind.ConstructorDeclaration) ||
+ n.IsKind(SyntaxKind.GetAccessorDeclaration) ||
+ n.IsKind(SyntaxKind.SetAccessorDeclaration) ||
+ n.IsKind(SyntaxKind.InitAccessorDeclaration) ||
+ n.IsKind(SyntaxKind.AddAccessorDeclaration) ||
+ n.IsKind(SyntaxKind.RemoveAccessorDeclaration));
+
+ return result is not null;
+ }
+
+ ///
+ /// Applies the code fix to add the missing attribute to a target type.
+ ///
+ /// The original document being fixed.
+ /// The original tree root belonging to the current document.
+ /// The to update.
+ /// The id of the type symbols to target.
+ /// The cancellation token for the operation.
+ /// An updated document with the applied code fix, and the return type of the method being .
+ private async Task AddMissingAttributeAsync(
+ Document document,
+ SyntaxNode root,
+ SyntaxNode targetNode,
+ string? windowsRuntimeTypeId,
+ CancellationToken cancellationToken)
+ {
+ // Get the compilation (bail if it's not available)
+ if (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false) is not { Compilation: Compilation compilation })
+ {
+ return document;
+ }
+
+ cancellationToken.ThrowIfCancellationRequested();
+
+ // Get the new member declaration
+ SyntaxNode updatedMemberDeclaration = AddMissingAttribute(
+ document,
+ compilation,
+ targetNode,
+ windowsRuntimeTypeId);
+
+ // Replace the node in the document tree
+ return document.WithSyntaxRoot(root.ReplaceNode(targetNode, updatedMemberDeclaration));
+ }
+
+ ///
+ /// Applies the code fix to add the missing attribute to a target type.
+ ///
+ /// The original document being fixed.
+ /// The to update.
+ /// The id of the type symbols to target.
+ /// An updated document with the applied code fix.
+ private SyntaxNode AddMissingAttribute(
+ Document document,
+ Compilation compilation,
+ SyntaxNode targetNode,
+ string? windowsRuntimeTypeId)
+ {
+ // Bail if we can't resolve the target attribute symbol (this should really never happen)
+ if (compilation.GetTypeByMetadataName("WinRT.DynamicWindowsRuntimeCastAttribute") is not INamedTypeSymbol attributeSymbol)
+ {
+ return targetNode;
+ }
+
+ // Also bail if we can't resolve the target type symbol
+ if (windowsRuntimeTypeId is null || compilation.GetTypeByMetadataName(windowsRuntimeTypeId) is not INamedTypeSymbol windowsRuntimeTypeSymbol)
+ {
+ return targetNode;
+ }
+
+ SyntaxGenerator syntaxGenerator = SyntaxGenerator.GetGenerator(document);
+
+ // Create the attribute syntax for the new attribute. Also annotate it
+ // to automatically add using directives to the document, if needed.
+ // Then create the attribute syntax and insert it at the right position.
+ SyntaxNode attributeTypeSyntax = syntaxGenerator.TypeExpression(attributeSymbol).WithAdditionalAnnotations(Simplifier.AddImportsAnnotation);
+ SyntaxNode targetTypeSyntax = syntaxGenerator.TypeExpression(windowsRuntimeTypeSymbol).WithAdditionalAnnotations(Simplifier.AddImportsAnnotation);
+ SyntaxNode attributeArgumentSyntax = syntaxGenerator.AttributeArgument(syntaxGenerator.TypeOfExpression(targetTypeSyntax));
+ SyntaxNode attributeSyntax = syntaxGenerator.Attribute(attributeTypeSyntax, [attributeArgumentSyntax]);
+ SyntaxNode updatedMemberDeclarationSyntax = syntaxGenerator.AddAttributes(targetNode, attributeSyntax);
+
+ // Replace the node in the syntax tree
+ return updatedMemberDeclarationSyntax;
+ }
+
+ ///
+ /// A custom with the logic from .
+ ///
+ /// The owning instance.
+ private sealed class FixAllProvider(RuntimeClassCastCodeFixer codeFixer) : DocumentBasedFixAllProvider
+ {
+ ///
+ protected override async Task FixAllAsync(FixAllContext fixAllContext, Document document, ImmutableArray diagnostics)
+ {
+ // Get the document root (this should always succeed)
+ if (await document.GetSyntaxRootAsync(fixAllContext.CancellationToken).ConfigureAwait(false) is not SyntaxNode root)
+ {
+ return document;
+ }
+
+ fixAllContext.CancellationToken.ThrowIfCancellationRequested();
+
+ // Get the compilation (bail if it's not available)
+ if (await document.GetSemanticModelAsync(fixAllContext.CancellationToken).ConfigureAwait(false) is not { Compilation: Compilation compilation })
+ {
+ return document;
+ }
+
+ fixAllContext.CancellationToken.ThrowIfCancellationRequested();
+
+ SyntaxEditor syntaxEditor = new(root, fixAllContext.Solution.Services);
+
+ // We need to deduplicate attributes being added on each target node, in case there's multiple casts to the same type.
+ // If that's the case, we just add an attribute for the first time it appears, and then ignore all duplicate ones.
+ Dictionary> symbolsPerNodeMap = [];
+
+ foreach (Diagnostic diagnostic in diagnostics)
+ {
+ fixAllContext.CancellationToken.ThrowIfCancellationRequested();
+
+ // Get the current node to annotate
+ if (!TryGetTargetNode(root, diagnostic.Location.SourceSpan, out SyntaxNode? targetNode))
+ {
+ continue;
+ }
+
+ // Retrieve the property passed by the analyzer
+ if (!diagnostic.Properties.TryGetValue(RuntimeClassCastAnalyzer.WindowsRuntimeTypeId, out string? windowsRuntimeTypeId) || windowsRuntimeTypeId is null)
+ {
+ continue;
+ }
+
+ // Get the map for the current target symbol
+ if (!symbolsPerNodeMap.TryGetValue(targetNode, out HashSet? symbolsSet))
+ {
+ symbolsSet = [];
+
+ symbolsPerNodeMap.Add(targetNode, symbolsSet);
+ }
+
+ // If the symbol has already been encountered, skip this diagnostic (it'll be fixed by another iteration)
+ if (!symbolsSet.Add(windowsRuntimeTypeId))
+ {
+ continue;
+ }
+
+ // Replace the node via the editor (needs a lambda to ensure multiple fixes are combined correctly)
+ syntaxEditor.ReplaceNode(targetNode, (targetNode, _) =>
+ {
+ return codeFixer.AddMissingAttribute(
+ document,
+ compilation,
+ targetNode,
+ windowsRuntimeTypeId);
+ });
+ }
+
+ return document.WithSyntaxRoot(syntaxEditor.GetChangedRoot());
+ }
+ }
+}
+
+#endif
diff --git a/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.Designer.cs b/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.Designer.cs
index 4ee84c43d..827754872 100644
--- a/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.Designer.cs
+++ b/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.Designer.cs
@@ -1,666 +1,840 @@
-//------------------------------------------------------------------------------
-//
-// This code was generated by a tool.
-// Runtime Version:4.0.30319.42000
-//
-// Changes to this file may cause incorrect behavior and will be lost if
-// the code is regenerated.
-//
-//------------------------------------------------------------------------------
-
-namespace WinRT.SourceGenerator {
- using System;
-
-
- ///
- /// A strongly-typed resource class, for looking up localized strings, etc.
- ///
- // This class was auto-generated by the StronglyTypedResourceBuilder
- // class via a tool like ResGen or Visual Studio.
- // To add or remove a member, edit your .ResX file then rerun ResGen
- // with the /str option, or rebuild your VS project.
- [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")]
- [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
- [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
- internal class CsWinRTDiagnosticStrings {
-
- private static global::System.Resources.ResourceManager resourceMan;
-
- private static global::System.Globalization.CultureInfo resourceCulture;
-
- [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
- internal CsWinRTDiagnosticStrings() {
- }
-
- ///
- /// Returns the cached ResourceManager instance used by this class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Resources.ResourceManager ResourceManager {
- get {
- if (object.ReferenceEquals(resourceMan, null)) {
- global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WinRT.SourceGenerator.CsWinRTDiagnosticStrings", typeof(CsWinRTDiagnosticStrings).Assembly);
- resourceMan = temp;
- }
- return resourceMan;
- }
- }
-
- ///
- /// Overrides the current thread's CurrentUICulture property for all
- /// resource lookups using this strongly typed resource class.
- ///
- [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
- internal static global::System.Globalization.CultureInfo Culture {
- get {
- return resourceCulture;
- }
- set {
- resourceCulture = value;
- }
- }
-
- ///
- /// Looks up a localized string similar to Array parameter marked InAttribute or OutAttribute.
- ///
- internal static string ArrayMarkedInOrOut_Brief {
- get {
- return ResourceManager.GetString("ArrayMarkedInOrOut_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Method '{0}' has parameter '{1}' which is an array, and which has either a System.Runtime.InteropServices.InAttribute or a System.Runtime.InteropServices.OutAttribute..
- ///
- internal static string ArrayMarkedInOrOut_Text1 {
- get {
- return ResourceManager.GetString("ArrayMarkedInOrOut_Text1", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to In the Windows Runtime, array parameters must have either ReadOnlyArray or WriteOnlyArray..
- ///
- internal static string ArrayMarkedInOrOut_Text2 {
- get {
- return ResourceManager.GetString("ArrayMarkedInOrOut_Text2", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Please remove these attributes or replace them with the appropriate Windows Runtime attribute if necessary..
- ///
- internal static string ArrayMarkedInOrOut_Text3 {
- get {
- return ResourceManager.GetString("ArrayMarkedInOrOut_Text3", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Array parameter marked `out` and ReadOnlyArray.
- ///
- internal static string ArrayOutputParamMarkedRead_Brief {
- get {
- return ResourceManager.GetString("ArrayOutputParamMarkedRead_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Method '{0}' has an output parameter '{1}' which is an array, but which has ReadOnlyArray attribute..
- ///
- internal static string ArrayOutputParamMarkedRead_Text1 {
- get {
- return ResourceManager.GetString("ArrayOutputParamMarkedRead_Text1", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to In the Windows Runtime, the contents of output arrays are writable. Please remove the attribute from '{1}'..
- ///
- internal static string ArrayOutputParamMarkedRead_Text2 {
- get {
- return ResourceManager.GetString("ArrayOutputParamMarkedRead_Text2", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Array paramter marked both ReadOnlyArray and WriteOnlyArray.
- ///
- internal static string ArrayParamMarkedBoth_Brief {
- get {
- return ResourceManager.GetString("ArrayParamMarkedBoth_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Method '{0}' has parameter '{1}' which is an array, and which has both ReadOnlyArray and WriteOnlyArray..
- ///
- internal static string ArrayParamMarkedBoth_Text1 {
- get {
- return ResourceManager.GetString("ArrayParamMarkedBoth_Text1", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to In the Windows Runtime, the contents array parameters must be either readable or writable, please remove one of the attributes from '{1}'..
- ///
- internal static string ArrayParamMarkedBoth_Text2 {
- get {
- return ResourceManager.GetString("ArrayParamMarkedBoth_Text2", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Array parameter not marked ReadOnlyArray or WriteOnlyArray way.
- ///
- internal static string ArrayParamNotMarked_Brief {
- get {
- return ResourceManager.GetString("ArrayParamNotMarked_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Method '{0}' has parameter '{1}' which is an array..
- ///
- internal static string ArrayParamNotMarked_Text1 {
- get {
- return ResourceManager.GetString("ArrayParamNotMarked_Text1", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to In the Windows Runtime, the contents of array parameters must be either readable or writable; please apply either ReadOnlyArray or WriteOnlyArray to '{1}'..
- ///
- internal static string ArrayParamNotMarked_Text2 {
- get {
- return ResourceManager.GetString("ArrayParamNotMarked_Text2", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Class Constructor Rule.
- ///
- internal static string ClassConstructorRule_Brief {
- get {
- return ResourceManager.GetString("ClassConstructorRule_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Classes cannot have multiple constructors of the same arity in the Windows Runtime, class {0} has multiple {1}-arity constructors.
- ///
- internal static string ClassConstructorRule_Text {
- get {
- return ResourceManager.GetString("ClassConstructorRule_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Namespace is disjoint from main (winmd) namespace.
- ///
- internal static string DisjointNamespaceRule_Brief {
- get {
- return ResourceManager.GetString("DisjointNamespaceRule_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to A public type has a namespace ('{1}') that shares no common prefix with other namespaces ('{0}')..
- ///
- internal static string DisjointNamespaceRule_Text1 {
- get {
- return ResourceManager.GetString("DisjointNamespaceRule_Text1", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to All types within a Windows Metadata file must exist in a sub namespace of the namespace that is implied by the file name..
- ///
- internal static string DisjointNamespaceRule_Text2 {
- get {
- return ResourceManager.GetString("DisjointNamespaceRule_Text2", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Class (or interface) is generic.
- ///
- internal static string GenericTypeRule_Brief {
- get {
- return ResourceManager.GetString("GenericTypeRule_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Type {0} is generic, Windows Runtime types cannot be generic.
- ///
- internal static string GenericTypeRule_Text {
- get {
- return ResourceManager.GetString("GenericTypeRule_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Array signature found with jagged array, which is not a valid WinRT type.
- ///
- internal static string JaggedArrayRule_Brief {
- get {
- return ResourceManager.GetString("JaggedArrayRule_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Method {0} has a nested array of type {1} in its signature; arrays in Windows Runtime method signature cannot be nested.
- ///
- internal static string JaggedArrayRule_Text {
- get {
- return ResourceManager.GetString("JaggedArrayRule_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Array signature found with multi-dimensional array, which is not a valid Windows Runtime type.
- ///
- internal static string MultiDimensionalArrayRule_Brief {
- get {
- return ResourceManager.GetString("MultiDimensionalArrayRule_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Method '{0}' has a multi-dimensional array of type '{1}' in its signature; arrays in Windows Runtime method signatures must be one dimensional.
- ///
- internal static string MultiDimensionalArrayRule_Text {
- get {
- return ResourceManager.GetString("MultiDimensionalArrayRule_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Only one overload should be designated default.
- ///
- internal static string MultipleDefaultOverloadAttribute_Brief {
- get {
- return ResourceManager.GetString("MultipleDefaultOverloadAttribute_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to In class {2}: Multiple {0}-parameter overloads of '{1}' are decorated with Windows.Foundation.Metadata.DefaultOverloadAttribute..
- ///
- internal static string MultipleDefaultOverloadAttribute_Text1 {
- get {
- return ResourceManager.GetString("MultipleDefaultOverloadAttribute_Text1", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to The attribute may only be applied to one overload of the method..
- ///
- internal static string MultipleDefaultOverloadAttribute_Text2 {
- get {
- return ResourceManager.GetString("MultipleDefaultOverloadAttribute_Text2", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Namespace names cannot differ only by case.
- ///
- internal static string NamespacesDifferByCase_Brief {
- get {
- return ResourceManager.GetString("NamespacesDifferByCase_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Multiple namespaces found with the name '{0}'; namespace names cannot differ only by case in the Windows Runtime.
- ///
- internal static string NamespacesDifferByCase_Text {
- get {
- return ResourceManager.GetString("NamespacesDifferByCase_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Multiple overloads seen without DefaultOverload attribute.
- ///
- internal static string NeedDefaultOverloadAttribute_Brief {
- get {
- return ResourceManager.GetString("NeedDefaultOverloadAttribute_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to In class {2}: The {0}-parameter overloads of {1} must have exactly one method specified as the default overload by decorating it with Windows.Foundation.Metadata.DefaultOverloadAttribute.
- ///
- internal static string NeedDefaultOverloadAttribute_Text {
- get {
- return ResourceManager.GetString("NeedDefaultOverloadAttribute_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Non-array parameter marked with ReadOnlyArray or WriteOnlyArray.
- ///
- internal static string NonArrayMarked_Brief {
- get {
- return ResourceManager.GetString("NonArrayMarked_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Method '{0}' has parameter '{1}' which is not an array, and which has either a ReadOnlyArray attribute or a WriteOnlyArray attribute..
- ///
- internal static string NonArrayMarked_Text1 {
- get {
- return ResourceManager.GetString("NonArrayMarked_Text1", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Windows Runtime does not support marking non-array parameters with ReadOnlyArray or WriteOnlyArray..
- ///
- internal static string NonArrayMarked_Text2 {
- get {
- return ResourceManager.GetString("NonArrayMarked_Text2", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Parameter (not array type) marked InAttribute or OutAttribute.
- ///
- internal static string NonArrayMarkedInOrOut_Brief {
- get {
- return ResourceManager.GetString("NonArrayMarkedInOrOut_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Method '{0}' has parameter '{1}' with a System.Runtime.InteropServices.InAttribute or System.Runtime.InteropServices.OutAttribute.Windows Runtime does not support marking parameters with System.Runtime.InteropServices.InAttribute or System.Runtime.InteropServices.OutAttribute..
- ///
- internal static string NonArrayMarkedInOrOut_Text1 {
- get {
- return ResourceManager.GetString("NonArrayMarkedInOrOut_Text1", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Please consider removing System.Runtime.InteropServices.InAttribute and replace System.Runtime.InteropServices.OutAttribute with 'out' modifier instead..
- ///
- internal static string NonArrayMarkedInOrOut_Text2 {
- get {
- return ResourceManager.GetString("NonArrayMarkedInOrOut_Text2", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Invalid Interface Inherited.
- ///
- internal static string NonWinRTInterface_Brief {
- get {
- return ResourceManager.GetString("NonWinRTInterface_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Windows Runtime component type {0} cannot implement interface {1}, as the interface is not a valid Windows Runtime interface.
- ///
- internal static string NonWinRTInterface_Text {
- get {
- return ResourceManager.GetString("NonWinRTInterface_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to No public types defined.
- ///
- internal static string NoPublicTypesRule_Brief {
- get {
- return ResourceManager.GetString("NoPublicTypesRule_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Windows Runtime components must have at least one public type.
- ///
- internal static string NoPublicTypesRule_Text {
- get {
- return ResourceManager.GetString("NoPublicTypesRule_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Operator overload exposed.
- ///
- internal static string OperatorOverloadedRule_Brief {
- get {
- return ResourceManager.GetString("OperatorOverloadedRule_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to {0} is an operator overload, managed types cannot expose operator overloads in the Windows Runtime.
- ///
- internal static string OperatorOverloadedRule_Text {
- get {
- return ResourceManager.GetString("OperatorOverloadedRule_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Parameter Named Value Rule.
- ///
- internal static string ParameterNamedValueRule_Brief {
- get {
- return ResourceManager.GetString("ParameterNamedValueRule_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to The parameter name {1} in method {0} is the same as the return value parameter name used in the generated C#/WinRT interop; use a different parameter name.
- ///
- internal static string ParameterNamedValueRule_Text {
- get {
- return ResourceManager.GetString("ParameterNamedValueRule_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Property must have public getter.
- ///
- internal static string PrivateGetterRule_Brief {
- get {
- return ResourceManager.GetString("PrivateGetterRule_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Property '{0}' does not have a public getter method. Windows Runtime does not support setter-only properties..
- ///
- internal static string PrivateGetterRule_Text {
- get {
- return ResourceManager.GetString("PrivateGetterRule_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Parameter passed by reference.
- ///
- internal static string RefParameterFound_Brief {
- get {
- return ResourceManager.GetString("RefParameterFound_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Method '{0}' has parameter '{1}' marked `ref`; reference parameters are not allowed in Windows Runtime.
- ///
- internal static string RefParameterFound_Text {
- get {
- return ResourceManager.GetString("RefParameterFound_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Const field in struct.
- ///
- internal static string StructHasConstFieldRule_Brief {
- get {
- return ResourceManager.GetString("StructHasConstFieldRule_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Structure {0} has const field - constants can only appear on Windows Runtime enumerations..
- ///
- internal static string StructHasConstFieldRule_Text {
- get {
- return ResourceManager.GetString("StructHasConstFieldRule_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Invalid field in struct.
- ///
- internal static string StructHasInvalidFieldRule_Brief {
- get {
- return ResourceManager.GetString("StructHasInvalidFieldRule_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Structure {0} has field of type {1}; {1} is not a valid Windows Runtime field type..
- ///
- internal static string StructHasInvalidFieldRule_Text1 {
- get {
- return ResourceManager.GetString("StructHasInvalidFieldRule_Text1", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Each field in a Windows Runtime structure can only be UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Boolean, String, Enum, or itself a structure..
- ///
- internal static string StructHasInvalidFieldRule_Text2 {
- get {
- return ResourceManager.GetString("StructHasInvalidFieldRule_Text2", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Private field in struct.
- ///
- internal static string StructHasPrivateFieldRule_Brief {
- get {
- return ResourceManager.GetString("StructHasPrivateFieldRule_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Structure {0} has non-public field. All fields must be public for Windows Runtime structures..
- ///
- internal static string StructHasPrivateFieldRule_Text {
- get {
- return ResourceManager.GetString("StructHasPrivateFieldRule_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Empty struct rule.
- ///
- internal static string StructWithNoFieldsRule_Brief {
- get {
- return ResourceManager.GetString("StructWithNoFieldsRule_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Structure {0} contains no public fields. Windows Runtime structures must contain at least one public field..
- ///
- internal static string StructWithNoFieldsRule_Text {
- get {
- return ResourceManager.GetString("StructWithNoFieldsRule_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Class incorrectly implements an interface.
- ///
- internal static string UnimplementedInterface_Brief {
- get {
- return ResourceManager.GetString("UnimplementedInterface_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Class '{0}' does not correctly implement interface '{1}' because member '{2}' is missing or non-public.
- ///
- internal static string UnimplementedInterface_Text {
- get {
- return ResourceManager.GetString("UnimplementedInterface_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Class is unsealed.
- ///
- internal static string UnsealedClassRule_Brief {
- get {
- return ResourceManager.GetString("UnsealedClassRule_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Exporting unsealed types is not supported in CsWinRT, please mark type {0} as sealed.
- ///
- internal static string UnsealedClassRule_Text {
- get {
- return ResourceManager.GetString("UnsealedClassRule_Text", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Exposing unsupported type.
- ///
- internal static string UnsupportedTypeRule_Brief {
- get {
- return ResourceManager.GetString("UnsupportedTypeRule_Brief", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to The member '{0}' has the type '{1}' in its signature..
- ///
- internal static string UnsupportedTypeRule_Text1 {
- get {
- return ResourceManager.GetString("UnsupportedTypeRule_Text1", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to The type '{1}' is not a valid Windows Runtime type..
- ///
- internal static string UnsupportedTypeRule_Text2 {
- get {
- return ResourceManager.GetString("UnsupportedTypeRule_Text2", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Yet, the type (or its generic parameters) implement interfaces that are valid Windows Runtime types..
- ///
- internal static string UnsupportedTypeRule_Text3 {
- get {
- return ResourceManager.GetString("UnsupportedTypeRule_Text3", resourceCulture);
- }
- }
-
- ///
- /// Looks up a localized string similar to Consider changing the type '{1} in the member signature to one of the following types from System.Collections.Generic: {2}..
- ///
- internal static string UnsupportedTypeRule_Text4 {
- get {
- return ResourceManager.GetString("UnsupportedTypeRule_Text4", resourceCulture);
- }
- }
- }
-}
+//------------------------------------------------------------------------------
+//
+// This code was generated by a tool.
+// Runtime Version:4.0.30319.42000
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+//------------------------------------------------------------------------------
+
+namespace WinRT.SourceGenerator {
+ using System;
+
+
+ ///
+ /// A strongly-typed resource class, for looking up localized strings, etc.
+ ///
+ // This class was auto-generated by the StronglyTypedResourceBuilder
+ // class via a tool like ResGen or Visual Studio.
+ // To add or remove a member, edit your .ResX file then rerun ResGen
+ // with the /str option, or rebuild your VS project.
+ [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
+ [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
+ [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ internal class CsWinRTDiagnosticStrings {
+
+ private static global::System.Resources.ResourceManager resourceMan;
+
+ private static global::System.Globalization.CultureInfo resourceCulture;
+
+ [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
+ internal CsWinRTDiagnosticStrings() {
+ }
+
+ ///
+ /// Returns the cached ResourceManager instance used by this class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Resources.ResourceManager ResourceManager {
+ get {
+ if (object.ReferenceEquals(resourceMan, null)) {
+ global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("WinRT.SourceGenerator.CsWinRTDiagnosticStrings", typeof(CsWinRTDiagnosticStrings).Assembly);
+ resourceMan = temp;
+ }
+ return resourceMan;
+ }
+ }
+
+ ///
+ /// Overrides the current thread's CurrentUICulture property for all
+ /// resource lookups using this strongly typed resource class.
+ ///
+ [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
+ internal static global::System.Globalization.CultureInfo Culture {
+ get {
+ return resourceCulture;
+ }
+ set {
+ resourceCulture = value;
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Array parameter marked InAttribute or OutAttribute.
+ ///
+ internal static string ArrayMarkedInOrOut_Brief {
+ get {
+ return ResourceManager.GetString("ArrayMarkedInOrOut_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Method '{0}' has parameter '{1}' which is an array, and which has either a System.Runtime.InteropServices.InAttribute or a System.Runtime.InteropServices.OutAttribute..
+ ///
+ internal static string ArrayMarkedInOrOut_Text1 {
+ get {
+ return ResourceManager.GetString("ArrayMarkedInOrOut_Text1", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to In the Windows Runtime, array parameters must have either ReadOnlyArray or WriteOnlyArray..
+ ///
+ internal static string ArrayMarkedInOrOut_Text2 {
+ get {
+ return ResourceManager.GetString("ArrayMarkedInOrOut_Text2", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Please remove these attributes or replace them with the appropriate Windows Runtime attribute if necessary..
+ ///
+ internal static string ArrayMarkedInOrOut_Text3 {
+ get {
+ return ResourceManager.GetString("ArrayMarkedInOrOut_Text3", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Array parameter marked `out` and ReadOnlyArray.
+ ///
+ internal static string ArrayOutputParamMarkedRead_Brief {
+ get {
+ return ResourceManager.GetString("ArrayOutputParamMarkedRead_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Method '{0}' has an output parameter '{1}' which is an array, but which has ReadOnlyArray attribute..
+ ///
+ internal static string ArrayOutputParamMarkedRead_Text1 {
+ get {
+ return ResourceManager.GetString("ArrayOutputParamMarkedRead_Text1", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to In the Windows Runtime, the contents of output arrays are writable. Please remove the attribute from '{1}'..
+ ///
+ internal static string ArrayOutputParamMarkedRead_Text2 {
+ get {
+ return ResourceManager.GetString("ArrayOutputParamMarkedRead_Text2", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Array paramter marked both ReadOnlyArray and WriteOnlyArray.
+ ///
+ internal static string ArrayParamMarkedBoth_Brief {
+ get {
+ return ResourceManager.GetString("ArrayParamMarkedBoth_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Method '{0}' has parameter '{1}' which is an array, and which has both ReadOnlyArray and WriteOnlyArray..
+ ///
+ internal static string ArrayParamMarkedBoth_Text1 {
+ get {
+ return ResourceManager.GetString("ArrayParamMarkedBoth_Text1", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to In the Windows Runtime, the contents array parameters must be either readable or writable, please remove one of the attributes from '{1}'..
+ ///
+ internal static string ArrayParamMarkedBoth_Text2 {
+ get {
+ return ResourceManager.GetString("ArrayParamMarkedBoth_Text2", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Array parameter not marked ReadOnlyArray or WriteOnlyArray way.
+ ///
+ internal static string ArrayParamNotMarked_Brief {
+ get {
+ return ResourceManager.GetString("ArrayParamNotMarked_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Method '{0}' has parameter '{1}' which is an array..
+ ///
+ internal static string ArrayParamNotMarked_Text1 {
+ get {
+ return ResourceManager.GetString("ArrayParamNotMarked_Text1", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to In the Windows Runtime, the contents of array parameters must be either readable or writable; please apply either ReadOnlyArray or WriteOnlyArray to '{1}'..
+ ///
+ internal static string ArrayParamNotMarked_Text2 {
+ get {
+ return ResourceManager.GetString("ArrayParamNotMarked_Text2", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Class '{0}' has attribute GeneratedBindableCustomProperty but it or a parent type isn't marked partial. Type and any parent types should be marked partial to allow source generation for trimming and AOT compatibility..
+ ///
+ internal static string BindableCustomPropertyClassNotMarkedPartial_Text {
+ get {
+ return ResourceManager.GetString("BindableCustomPropertyClassNotMarkedPartial_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Class Constructor Rule.
+ ///
+ internal static string ClassConstructorRule_Brief {
+ get {
+ return ResourceManager.GetString("ClassConstructorRule_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Classes cannot have multiple constructors of the same arity in the Windows Runtime, class {0} has multiple {1}-arity constructors.
+ ///
+ internal static string ClassConstructorRule_Text {
+ get {
+ return ResourceManager.GetString("ClassConstructorRule_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Class not trimming / AOT compatible.
+ ///
+ internal static string ClassImplementsOldProjection_Brief {
+ get {
+ return ResourceManager.GetString("ClassImplementsOldProjection_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Class '{0}' implements WinRT interface(s) {1} generated using an older version of CsWinRT. Update to a projection generated using CsWinRT 2.1.0 or later for trimming and AOT compatibility..
+ ///
+ internal static string ClassImplementsOldProjection_Text {
+ get {
+ return ResourceManager.GetString("ClassImplementsOldProjection_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Class is not marked partial.
+ ///
+ internal static string ClassNotMarkedPartial_Brief {
+ get {
+ return ResourceManager.GetString("ClassNotMarkedPartial_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Class '{0}' implements WinRT interfaces but it or a parent type isn't marked partial. Type and any parent types should be marked partial for trimming and AOT compatibility if passed across the WinRT ABI..
+ ///
+ internal static string ClassNotMarkedPartial_Text {
+ get {
+ return ResourceManager.GetString("ClassNotMarkedPartial_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Class '{0}' was generated using an older version of CsWinRT and due to the type being defined in multiple DLLs, CsWinRT can not generate compat code to make it trimming safe. Update to a projection generated using CsWinRT 2.1.0 or later for trimming and AOT compatibility..
+ ///
+ internal static string ClassOldProjectionMultipleInstances_Text {
+ get {
+ return ResourceManager.GetString("ClassOldProjectionMultipleInstances_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Cast to [ComImport] type not supported.
+ ///
+ internal static string ComImportInterfaceCast_Brief
+ {
+ get
+ {
+ return ResourceManager.GetString("ComImportInterfaceCast_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The type '{0}' used in a cast operation is a [ComImport] interface, which is not compatible with CsWinRT objects in AOT scenarios. Consider using the COM generators to define the interface, or manually handling the interface query on the underlying native object..
+ ///
+ internal static string ComImportInterfaceCast_Text
+ {
+ get
+ {
+ return ResourceManager.GetString("ComImportInterfaceCast_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Namespace is disjoint from main (winmd) namespace.
+ ///
+ internal static string DisjointNamespaceRule_Brief {
+ get {
+ return ResourceManager.GetString("DisjointNamespaceRule_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to A public type has a namespace ('{1}') that shares no common prefix with other namespaces ('{0}')..
+ ///
+ internal static string DisjointNamespaceRule_Text1 {
+ get {
+ return ResourceManager.GetString("DisjointNamespaceRule_Text1", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to All types within a Windows Metadata file must exist in a sub namespace of the namespace that is implied by the file name..
+ ///
+ internal static string DisjointNamespaceRule_Text2 {
+ get {
+ return ResourceManager.GetString("DisjointNamespaceRule_Text2", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Project does not enable unsafe blocks.
+ ///
+ internal static string EnableUnsafe_Brief {
+ get {
+ return ResourceManager.GetString("EnableUnsafe_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Type '{0}' implements generic WinRT interfaces which requires generated code using unsafe for trimming and AOT compatibility if passed across the WinRT ABI. Project needs to be updated with '<AllowUnsafeBlocks>true</AllowUnsafeBlocks>'..
+ ///
+ internal static string EnableUnsafe_Text {
+ get {
+ return ResourceManager.GetString("EnableUnsafe_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Class (or interface) is generic.
+ ///
+ internal static string GenericTypeRule_Brief {
+ get {
+ return ResourceManager.GetString("GenericTypeRule_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Type {0} is generic, Windows Runtime types cannot be generic.
+ ///
+ internal static string GenericTypeRule_Text {
+ get {
+ return ResourceManager.GetString("GenericTypeRule_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Casts to WinRT 'IReference`1<T>' unboxed values are not trim-safe.
+ ///
+ internal static string IReferenceTypeCast_Brief
+ {
+ get
+ {
+ return ResourceManager.GetString("IReferenceTypeCast_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Casting to type '{0}' is not trim-safe, as it is unboxing a WinRT 'IReference`1<T>' value type. Consider using the 'WinRT.CastExtensions.Cast<T>' extension method instead, which will ensure the necessary metadata is kept. Alternatively, you can use '[DynamicDependency]' to manually root the necessary metadata, and suppress the warning via '[UnconditionalSuppressMessage]'..
+ ///
+ internal static string IReferenceTypeCast_Text
+ {
+ get
+ {
+ return ResourceManager.GetString("IReferenceTypeCast_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Array signature found with jagged array, which is not a valid WinRT type.
+ ///
+ internal static string JaggedArrayRule_Brief {
+ get {
+ return ResourceManager.GetString("JaggedArrayRule_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Method {0} has a nested array of type {1} in its signature; arrays in Windows Runtime method signature cannot be nested.
+ ///
+ internal static string JaggedArrayRule_Text {
+ get {
+ return ResourceManager.GetString("JaggedArrayRule_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Array signature found with multi-dimensional array, which is not a valid Windows Runtime type.
+ ///
+ internal static string MultiDimensionalArrayRule_Brief {
+ get {
+ return ResourceManager.GetString("MultiDimensionalArrayRule_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Method '{0}' has a multi-dimensional array of type '{1}' in its signature; arrays in Windows Runtime method signatures must be one dimensional.
+ ///
+ internal static string MultiDimensionalArrayRule_Text {
+ get {
+ return ResourceManager.GetString("MultiDimensionalArrayRule_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Only one overload should be designated default.
+ ///
+ internal static string MultipleDefaultOverloadAttribute_Brief {
+ get {
+ return ResourceManager.GetString("MultipleDefaultOverloadAttribute_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to In class {2}: Multiple {0}-parameter overloads of '{1}' are decorated with Windows.Foundation.Metadata.DefaultOverloadAttribute..
+ ///
+ internal static string MultipleDefaultOverloadAttribute_Text1 {
+ get {
+ return ResourceManager.GetString("MultipleDefaultOverloadAttribute_Text1", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The attribute may only be applied to one overload of the method..
+ ///
+ internal static string MultipleDefaultOverloadAttribute_Text2 {
+ get {
+ return ResourceManager.GetString("MultipleDefaultOverloadAttribute_Text2", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Namespace names cannot differ only by case.
+ ///
+ internal static string NamespacesDifferByCase_Brief {
+ get {
+ return ResourceManager.GetString("NamespacesDifferByCase_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Multiple namespaces found with the name '{0}'; namespace names cannot differ only by case in the Windows Runtime.
+ ///
+ internal static string NamespacesDifferByCase_Text {
+ get {
+ return ResourceManager.GetString("NamespacesDifferByCase_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Multiple overloads seen without DefaultOverload attribute.
+ ///
+ internal static string NeedDefaultOverloadAttribute_Brief {
+ get {
+ return ResourceManager.GetString("NeedDefaultOverloadAttribute_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to In class {2}: The {0}-parameter overloads of {1} must have exactly one method specified as the default overload by decorating it with Windows.Foundation.Metadata.DefaultOverloadAttribute.
+ ///
+ internal static string NeedDefaultOverloadAttribute_Text {
+ get {
+ return ResourceManager.GetString("NeedDefaultOverloadAttribute_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Non-array parameter marked with ReadOnlyArray or WriteOnlyArray.
+ ///
+ internal static string NonArrayMarked_Brief {
+ get {
+ return ResourceManager.GetString("NonArrayMarked_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Method '{0}' has parameter '{1}' which is not an array, and which has either a ReadOnlyArray attribute or a WriteOnlyArray attribute..
+ ///
+ internal static string NonArrayMarked_Text1 {
+ get {
+ return ResourceManager.GetString("NonArrayMarked_Text1", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Windows Runtime does not support marking non-array parameters with ReadOnlyArray or WriteOnlyArray..
+ ///
+ internal static string NonArrayMarked_Text2 {
+ get {
+ return ResourceManager.GetString("NonArrayMarked_Text2", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Parameter (not array type) marked InAttribute or OutAttribute.
+ ///
+ internal static string NonArrayMarkedInOrOut_Brief {
+ get {
+ return ResourceManager.GetString("NonArrayMarkedInOrOut_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Method '{0}' has parameter '{1}' with a System.Runtime.InteropServices.InAttribute or System.Runtime.InteropServices.OutAttribute.Windows Runtime does not support marking parameters with System.Runtime.InteropServices.InAttribute or System.Runtime.InteropServices.OutAttribute..
+ ///
+ internal static string NonArrayMarkedInOrOut_Text1 {
+ get {
+ return ResourceManager.GetString("NonArrayMarkedInOrOut_Text1", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Please consider removing System.Runtime.InteropServices.InAttribute and replace System.Runtime.InteropServices.OutAttribute with 'out' modifier instead..
+ ///
+ internal static string NonArrayMarkedInOrOut_Text2 {
+ get {
+ return ResourceManager.GetString("NonArrayMarkedInOrOut_Text2", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Collection expression not safe for WinRT.
+ ///
+ internal static string NonEmptyCollectionExpressionTargetingNonBuilderInterfaceType_Brief {
+ get {
+ return ResourceManager.GetString("NonEmptyCollectionExpressionTargetingNonBuilderInterfaceType_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Collection expressions targeting non mutable interface type '{0}' do not have a defined concrete type, and are not supported for trimming and AOT compatibility with WinRT scenarios (consider changing the target type to be a concrete type, a mutable interface type, or avoid using a collection expression in this case).
+ ///
+ internal static string NonEmptyCollectionExpressionTargetingNonBuilderInterfaceType_Text {
+ get {
+ return ResourceManager.GetString("NonEmptyCollectionExpressionTargetingNonBuilderInterfaceType_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Invalid Interface Inherited.
+ ///
+ internal static string NonWinRTInterface_Brief {
+ get {
+ return ResourceManager.GetString("NonWinRTInterface_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Windows Runtime component type {0} cannot implement interface {1}, as the interface is not a valid Windows Runtime interface.
+ ///
+ internal static string NonWinRTInterface_Text {
+ get {
+ return ResourceManager.GetString("NonWinRTInterface_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to No public types defined.
+ ///
+ internal static string NoPublicTypesRule_Brief {
+ get {
+ return ResourceManager.GetString("NoPublicTypesRule_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Windows Runtime components must have at least one public type.
+ ///
+ internal static string NoPublicTypesRule_Text {
+ get {
+ return ResourceManager.GetString("NoPublicTypesRule_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Operator overload exposed.
+ ///
+ internal static string OperatorOverloadedRule_Brief {
+ get {
+ return ResourceManager.GetString("OperatorOverloadedRule_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to {0} is an operator overload, managed types cannot expose operator overloads in the Windows Runtime.
+ ///
+ internal static string OperatorOverloadedRule_Text {
+ get {
+ return ResourceManager.GetString("OperatorOverloadedRule_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Parameter Named Value Rule.
+ ///
+ internal static string ParameterNamedValueRule_Brief {
+ get {
+ return ResourceManager.GetString("ParameterNamedValueRule_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The parameter name {1} in method {0} is the same as the return value parameter name used in the generated C#/WinRT interop; use a different parameter name.
+ ///
+ internal static string ParameterNamedValueRule_Text {
+ get {
+ return ResourceManager.GetString("ParameterNamedValueRule_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Property must have public getter.
+ ///
+ internal static string PrivateGetterRule_Brief {
+ get {
+ return ResourceManager.GetString("PrivateGetterRule_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Property '{0}' does not have a public getter method. Windows Runtime does not support setter-only properties..
+ ///
+ internal static string PrivateGetterRule_Text {
+ get {
+ return ResourceManager.GetString("PrivateGetterRule_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Parameter passed by reference.
+ ///
+ internal static string RefParameterFound_Brief {
+ get {
+ return ResourceManager.GetString("RefParameterFound_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Method '{0}' has parameter '{1}' marked `ref`; reference parameters are not allowed in Windows Runtime.
+ ///
+ internal static string RefParameterFound_Text {
+ get {
+ return ResourceManager.GetString("RefParameterFound_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Casts to WinRT runtime class types are not trim-safe.
+ ///
+ internal static string RuntimeClassCast_Brief
+ {
+ get
+ {
+ return ResourceManager.GetString("RuntimeClassCast_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Casting to type '{0}' is not trim-safe, as it is a WinRT runtime class type. Consider using the 'WinRT.CastExtensions.Cast<T>' extension method instead, which will ensure the necessary metadata is kept. Alternatively, you can use '[DynamicDependency]' to manually root the necessary metadata, and suppress the warning via '[UnconditionalSuppressMessage]'..
+ ///
+ internal static string RuntimeClassCast_Text
+ {
+ get
+ {
+ return ResourceManager.GetString("RuntimeClassCast_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Source generator failed.
+ ///
+ internal static string SourceGeneratorFailed_Brief {
+ get {
+ return ResourceManager.GetString("SourceGeneratorFailed_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to CsWinRT component authoring source generator 'WinRT.SourceGenerator' failed to generate WinMD and projection because of '{0}'.
+ ///
+ internal static string SourceGeneratorFailed_Text {
+ get {
+ return ResourceManager.GetString("SourceGeneratorFailed_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Const field in struct.
+ ///
+ internal static string StructHasConstFieldRule_Brief {
+ get {
+ return ResourceManager.GetString("StructHasConstFieldRule_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Structure {0} has const field - constants can only appear on Windows Runtime enumerations..
+ ///
+ internal static string StructHasConstFieldRule_Text {
+ get {
+ return ResourceManager.GetString("StructHasConstFieldRule_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Invalid field in struct.
+ ///
+ internal static string StructHasInvalidFieldRule_Brief {
+ get {
+ return ResourceManager.GetString("StructHasInvalidFieldRule_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Structure {0} has field of type {1}; {1} is not a valid Windows Runtime field type..
+ ///
+ internal static string StructHasInvalidFieldRule_Text1 {
+ get {
+ return ResourceManager.GetString("StructHasInvalidFieldRule_Text1", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Each field in a Windows Runtime structure can only be UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Boolean, String, Enum, or itself a structure..
+ ///
+ internal static string StructHasInvalidFieldRule_Text2 {
+ get {
+ return ResourceManager.GetString("StructHasInvalidFieldRule_Text2", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Private field in struct.
+ ///
+ internal static string StructHasPrivateFieldRule_Brief {
+ get {
+ return ResourceManager.GetString("StructHasPrivateFieldRule_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Structure {0} has non-public field. All fields must be public for Windows Runtime structures..
+ ///
+ internal static string StructHasPrivateFieldRule_Text {
+ get {
+ return ResourceManager.GetString("StructHasPrivateFieldRule_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Empty struct rule.
+ ///
+ internal static string StructWithNoFieldsRule_Brief {
+ get {
+ return ResourceManager.GetString("StructWithNoFieldsRule_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Structure {0} contains no public fields. Windows Runtime structures must contain at least one public field..
+ ///
+ internal static string StructWithNoFieldsRule_Text {
+ get {
+ return ResourceManager.GetString("StructWithNoFieldsRule_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Class incorrectly implements an interface.
+ ///
+ internal static string UnimplementedInterface_Brief {
+ get {
+ return ResourceManager.GetString("UnimplementedInterface_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Class '{0}' does not correctly implement interface '{1}' because member '{2}' is missing or non-public.
+ ///
+ internal static string UnimplementedInterface_Text {
+ get {
+ return ResourceManager.GetString("UnimplementedInterface_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Class is unsealed.
+ ///
+ internal static string UnsealedClassRule_Brief {
+ get {
+ return ResourceManager.GetString("UnsealedClassRule_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Exporting unsealed types is not supported in CsWinRT, please mark type {0} as sealed.
+ ///
+ internal static string UnsealedClassRule_Text {
+ get {
+ return ResourceManager.GetString("UnsealedClassRule_Text", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Exposing unsupported type.
+ ///
+ internal static string UnsupportedTypeRule_Brief {
+ get {
+ return ResourceManager.GetString("UnsupportedTypeRule_Brief", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The member '{0}' has the type '{1}' in its signature..
+ ///
+ internal static string UnsupportedTypeRule_Text1 {
+ get {
+ return ResourceManager.GetString("UnsupportedTypeRule_Text1", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to The type '{1}' is not a valid Windows Runtime type..
+ ///
+ internal static string UnsupportedTypeRule_Text2 {
+ get {
+ return ResourceManager.GetString("UnsupportedTypeRule_Text2", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Yet, the type (or its generic parameters) implement interfaces that are valid Windows Runtime types..
+ ///
+ internal static string UnsupportedTypeRule_Text3 {
+ get {
+ return ResourceManager.GetString("UnsupportedTypeRule_Text3", resourceCulture);
+ }
+ }
+
+ ///
+ /// Looks up a localized string similar to Consider changing the type '{1} in the member signature to one of the following types from System.Collections.Generic: {2}..
+ ///
+ internal static string UnsupportedTypeRule_Text4 {
+ get {
+ return ResourceManager.GetString("UnsupportedTypeRule_Text4", resourceCulture);
+ }
+ }
+ }
+}
diff --git a/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.resx b/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.resx
index 96a9f4098..24ed2e57a 100644
--- a/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.resx
+++ b/src/Authoring/WinRT.SourceGenerator/CsWinRTDiagnosticStrings.resx
@@ -1,271 +1,384 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- text/microsoft-resx
-
-
- 2.0
-
-
- System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
-
-
- Array parameter marked InAttribute or OutAttribute
-
-
- Method '{0}' has parameter '{1}' which is an array, and which has either a System.Runtime.InteropServices.InAttribute or a System.Runtime.InteropServices.OutAttribute.
-
-
- In the Windows Runtime, array parameters must have either ReadOnlyArray or WriteOnlyArray.
-
-
- Please remove these attributes or replace them with the appropriate Windows Runtime attribute if necessary.
-
-
- Array parameter marked `out` and ReadOnlyArray
-
-
- Method '{0}' has an output parameter '{1}' which is an array, but which has ReadOnlyArray attribute.
-
-
- In the Windows Runtime, the contents of output arrays are writable. Please remove the attribute from '{1}'.
-
-
- Array paramter marked both ReadOnlyArray and WriteOnlyArray
-
-
- Method '{0}' has parameter '{1}' which is an array, and which has both ReadOnlyArray and WriteOnlyArray.
-
-
- In the Windows Runtime, the contents array parameters must be either readable or writable, please remove one of the attributes from '{1}'.
-
-
- Array parameter not marked ReadOnlyArray or WriteOnlyArray way
-
-
- Method '{0}' has parameter '{1}' which is an array.
-
-
- In the Windows Runtime, the contents of array parameters must be either readable or writable; please apply either ReadOnlyArray or WriteOnlyArray to '{1}'.
-
-
- Class Constructor Rule
-
-
- Classes cannot have multiple constructors of the same arity in the Windows Runtime, class {0} has multiple {1}-arity constructors
-
-
- Namespace is disjoint from main (winmd) namespace
-
-
- A public type has a namespace ('{1}') that shares no common prefix with other namespaces ('{0}').
- {1} and {0} will be some user-defined keyword
-
-
- All types within a Windows Metadata file must exist in a sub namespace of the namespace that is implied by the file name.
- "sub namespace" means a namespace defined within another namespace
-
-
- Class (or interface) is generic
-
-
- Type {0} is generic, Windows Runtime types cannot be generic
- {0} will be some user-defined keyword
-
-
- Array signature found with jagged array, which is not a valid WinRT type
-
-
- Method {0} has a nested array of type {1} in its signature; arrays in Windows Runtime method signature cannot be nested
-
-
- Array signature found with multi-dimensional array, which is not a valid Windows Runtime type
-
-
- Method '{0}' has a multi-dimensional array of type '{1}' in its signature; arrays in Windows Runtime method signatures must be one dimensional
-
-
- Only one overload should be designated default
-
-
- In class {2}: Multiple {0}-parameter overloads of '{1}' are decorated with Windows.Foundation.Metadata.DefaultOverloadAttribute.
-
-
- The attribute may only be applied to one overload of the method.
-
-
- Namespace names cannot differ only by case
-
-
- Multiple namespaces found with the name '{0}'; namespace names cannot differ only by case in the Windows Runtime
- {0} will be some user-defined keyword
-
-
- Multiple overloads seen without DefaultOverload attribute
-
-
- In class {2}: The {0}-parameter overloads of {1} must have exactly one method specified as the default overload by decorating it with Windows.Foundation.Metadata.DefaultOverloadAttribute
-
-
- Parameter (not array type) marked InAttribute or OutAttribute
-
-
- Method '{0}' has parameter '{1}' with a System.Runtime.InteropServices.InAttribute or System.Runtime.InteropServices.OutAttribute.Windows Runtime does not support marking parameters with System.Runtime.InteropServices.InAttribute or System.Runtime.InteropServices.OutAttribute.
-
-
- Please consider removing System.Runtime.InteropServices.InAttribute and replace System.Runtime.InteropServices.OutAttribute with 'out' modifier instead.
-
-
- Non-array parameter marked with ReadOnlyArray or WriteOnlyArray
-
-
- Method '{0}' has parameter '{1}' which is not an array, and which has either a ReadOnlyArray attribute or a WriteOnlyArray attribute.
-
-
- Windows Runtime does not support marking non-array parameters with ReadOnlyArray or WriteOnlyArray.
-
-
- Invalid Interface Inherited
-
-
- Windows Runtime component type {0} cannot implement interface {1}, as the interface is not a valid Windows Runtime interface
-
-
- No public types defined
-
-
- Windows Runtime components must have at least one public type
-
-
- Operator overload exposed
-
-
- {0} is an operator overload, managed types cannot expose operator overloads in the Windows Runtime
-
-
- Parameter Named Value Rule
-
-
- The parameter name {1} in method {0} is the same as the return value parameter name used in the generated C#/WinRT interop; use a different parameter name
-
-
- Property must have public getter
-
-
- Property '{0}' does not have a public getter method. Windows Runtime does not support setter-only properties.
- {0} will be some user-defined keyword
-
-
- Parameter passed by reference
-
-
- Method '{0}' has parameter '{1}' marked `ref`; reference parameters are not allowed in Windows Runtime
-
-
- Const field in struct
-
-
- Structure {0} has const field - constants can only appear on Windows Runtime enumerations.
-
-
- Invalid field in struct
-
-
- Structure {0} has field of type {1}; {1} is not a valid Windows Runtime field type.
-
-
- Each field in a Windows Runtime structure can only be UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Boolean, String, Enum, or itself a structure.
-
-
- Private field in struct
-
-
- Structure {0} has non-public field. All fields must be public for Windows Runtime structures.
-
-
- Empty struct rule
-
-
- Structure {0} contains no public fields. Windows Runtime structures must contain at least one public field.
-
-
- Class incorrectly implements an interface
-
-
- Class '{0}' does not correctly implement interface '{1}' because member '{2}' is missing or non-public
-
-
- Class is unsealed
-
-
- Exporting unsealed types is not supported in CsWinRT, please mark type {0} as sealed
- {0} will be some user-defined keyword
-
-
- Exposing unsupported type
-
-
- The member '{0}' has the type '{1}' in its signature.
- {0} and {1} will be user-defined keywords
-
-
- The type '{1}' is not a valid Windows Runtime type.
- {1} will be some user-defined keyword
-
-
- Yet, the type (or its generic parameters) implement interfaces that are valid Windows Runtime types.
-
-
- Consider changing the type '{1} in the member signature to one of the following types from System.Collections.Generic: {2}.
- {1} and {2} will be keywords (types) from DotNet
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ Array parameter marked InAttribute or OutAttribute
+
+
+ Method '{0}' has parameter '{1}' which is an array, and which has either a System.Runtime.InteropServices.InAttribute or a System.Runtime.InteropServices.OutAttribute.
+
+
+ In the Windows Runtime, array parameters must have either ReadOnlyArray or WriteOnlyArray.
+
+
+ Please remove these attributes or replace them with the appropriate Windows Runtime attribute if necessary.
+
+
+ Array parameter marked `out` and ReadOnlyArray
+
+
+ Method '{0}' has an output parameter '{1}' which is an array, but which has ReadOnlyArray attribute.
+
+
+ In the Windows Runtime, the contents of output arrays are writable. Please remove the attribute from '{1}'.
+
+
+ Array paramter marked both ReadOnlyArray and WriteOnlyArray
+
+
+ Method '{0}' has parameter '{1}' which is an array, and which has both ReadOnlyArray and WriteOnlyArray.
+
+
+ In the Windows Runtime, the contents array parameters must be either readable or writable, please remove one of the attributes from '{1}'.
+
+
+ Array parameter not marked ReadOnlyArray or WriteOnlyArray way
+
+
+ Method '{0}' has parameter '{1}' which is an array.
+
+
+ In the Windows Runtime, the contents of array parameters must be either readable or writable; please apply either ReadOnlyArray or WriteOnlyArray to '{1}'.
+
+
+ Class Constructor Rule
+
+
+ Classes cannot have multiple constructors of the same arity in the Windows Runtime, class {0} has multiple {1}-arity constructors
+
+
+ Class not trimming / AOT compatible
+
+
+ Class '{0}' implements WinRT interface(s) {1} generated using an older version of CsWinRT. Update to a projection generated using CsWinRT 2.1.0 or later for trimming and AOT compatibility.
+
+
+ Class '{0}' was generated using an older version of CsWinRT and due to the type being defined in multiple DLLs, CsWinRT can not generate compat code to make it trimming safe. Update to a projection generated using CsWinRT 2.1.0 or later for trimming and AOT compatibility.
+
+
+ Class is not marked partial
+
+
+ Class '{0}' implements WinRT interfaces but it or a parent type isn't marked partial. Type and any parent types should be marked partial for trimming and AOT compatibility if passed across the WinRT ABI.
+
+
+ Class '{0}' has attribute GeneratedBindableCustomProperty but it or a parent type isn't marked partial. Type and any parent types should be marked partial to allow source generation for trimming and AOT compatibility.
+
+
+ Namespace is disjoint from main (winmd) namespace
+
+
+ A public type has a namespace ('{1}') that shares no common prefix with other namespaces ('{0}').
+ {1} and {0} will be some user-defined keyword
+
+
+ All types within a Windows Metadata file must exist in a sub namespace of the namespace that is implied by the file name.
+ "sub namespace" means a namespace defined within another namespace
+
+
+ Project does not enable unsafe blocks
+
+
+ Type '{0}' implements generic WinRT interfaces which requires generated code using unsafe for trimming and AOT compatibility if passed across the WinRT ABI. Project needs to be updated with '<AllowUnsafeBlocks>true</AllowUnsafeBlocks>'.
+
+
+ Class (or interface) is generic
+
+
+ Type {0} is generic, Windows Runtime types cannot be generic
+ {0} will be some user-defined keyword
+
+
+ Array signature found with jagged array, which is not a valid WinRT type
+
+
+ Method {0} has a nested array of type {1} in its signature; arrays in Windows Runtime method signature cannot be nested
+
+
+ Array signature found with multi-dimensional array, which is not a valid Windows Runtime type
+
+
+ Method '{0}' has a multi-dimensional array of type '{1}' in its signature; arrays in Windows Runtime method signatures must be one dimensional
+
+
+ Only one overload should be designated default
+
+
+ In class {2}: Multiple {0}-parameter overloads of '{1}' are decorated with Windows.Foundation.Metadata.DefaultOverloadAttribute.
+
+
+ The attribute may only be applied to one overload of the method.
+
+
+ Namespace names cannot differ only by case
+
+
+ Multiple namespaces found with the name '{0}'; namespace names cannot differ only by case in the Windows Runtime
+ {0} will be some user-defined keyword
+
+
+ Multiple overloads seen without DefaultOverload attribute
+
+
+ In class {2}: The {0}-parameter overloads of {1} must have exactly one method specified as the default overload by decorating it with Windows.Foundation.Metadata.DefaultOverloadAttribute
+
+
+ Parameter (not array type) marked InAttribute or OutAttribute
+
+
+ Method '{0}' has parameter '{1}' with a System.Runtime.InteropServices.InAttribute or System.Runtime.InteropServices.OutAttribute.Windows Runtime does not support marking parameters with System.Runtime.InteropServices.InAttribute or System.Runtime.InteropServices.OutAttribute.
+
+
+ Please consider removing System.Runtime.InteropServices.InAttribute and replace System.Runtime.InteropServices.OutAttribute with 'out' modifier instead.
+
+
+ Non-array parameter marked with ReadOnlyArray or WriteOnlyArray
+
+
+ Method '{0}' has parameter '{1}' which is not an array, and which has either a ReadOnlyArray attribute or a WriteOnlyArray attribute.
+
+
+ Windows Runtime does not support marking non-array parameters with ReadOnlyArray or WriteOnlyArray.
+
+
+ Invalid Interface Inherited
+
+
+ Windows Runtime component type {0} cannot implement interface {1}, as the interface is not a valid Windows Runtime interface
+
+
+ No public types defined
+
+
+ Windows Runtime components must have at least one public type
+
+
+ Operator overload exposed
+
+
+ {0} is an operator overload, managed types cannot expose operator overloads in the Windows Runtime
+
+
+ Parameter Named Value Rule
+
+
+ The parameter name {1} in method {0} is the same as the return value parameter name used in the generated C#/WinRT interop; use a different parameter name
+
+
+ Property must have public getter
+
+
+ Property '{0}' does not have a public getter method. Windows Runtime does not support setter-only properties.
+ {0} will be some user-defined keyword
+
+
+ Parameter passed by reference
+
+
+ Const field in struct
+
+
+ Structure {0} has const field - constants can only appear on Windows Runtime enumerations.
+
+
+ Invalid field in struct
+
+
+ Structure {0} has field of type {1}; {1} is not a valid Windows Runtime field type.
+
+
+ Each field in a Windows Runtime structure can only be UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Boolean, String, Enum, or itself a structure.
+
+
+ Private field in struct
+
+
+ Structure {0} has non-public field. All fields must be public for Windows Runtime structures.
+
+
+ Empty struct rule
+
+
+ Structure {0} contains no public fields. Windows Runtime structures must contain at least one public field.
+
+
+ Class incorrectly implements an interface
+
+
+ Class '{0}' does not correctly implement interface '{1}' because member '{2}' is missing or non-public
+
+
+ Class is unsealed
+
+
+ Exporting unsealed types is not supported in CsWinRT, please mark type {0} as sealed
+ {0} will be some user-defined keyword
+
+
+ Exposing unsupported type
+
+
+ The member '{0}' has the type '{1}' in its signature.
+ {0} and {1} will be user-defined keywords
+
+
+ The type '{1}' is not a valid Windows Runtime type.
+ {1} will be some user-defined keyword
+
+
+ Yet, the type (or its generic parameters) implement interfaces that are valid Windows Runtime types.
+
+
+ Consider changing the type '{1} in the member signature to one of the following types from System.Collections.Generic: {2}.
+ {1} and {2} will be keywords (types) from DotNet
+
+
+ Method '{0}' has parameter '{1}' marked `ref`; reference parameters are not allowed in Windows Runtime
+
+
+ Source generator failed
+
+
+ CsWinRT component authoring source generator 'WinRT.SourceGenerator' failed to generate WinMD and projection because of '{0}'
+
+
+ Collection expression not safe for WinRT
+
+
+ Collection expressions targeting non mutable interface type '{0}' do not have a defined concrete type, and are not supported for trimming and AOT compatibility with WinRT scenarios (consider changing the target type to be a concrete type, a mutable interface type, or add an explicit cast for either of these on the collection expression, eg. '(string[])["A", "B", "C"]')
+
+
+ Cast to [ComImport] type not supported
+
+
+ The type '{0}' used in a cast operation is a [ComImport] interface, which is not compatible with CsWinRT objects in AOT scenarios. Consider using the COM generators to define the interface, or manually handling the interface query on the underlying native object.
+
+
+ Casts to WinRT runtime class types are not trim-safe
+
+
+ Casting to type '{0}' is not trim-safe, as it is a WinRT runtime class type. Add the '[DynamicWindowsRuntimeCast]' attribute to the containing method (or field), which will ensure the necessary metadata is kept. This will allow the cast to always work correctly when using trimming.
+
+
+ Casts to WinRT 'IReference`1<T>' unboxed values are not trim-safe
+
+
+ Casting to type '{0}' is not trim-safe, as it is unboxing a WinRT 'IReference`1<T>' value type. Add the '[DynamicWindowsRuntimeCast]' attribute to the containing method (or field), which will ensure the necessary metadata is kept. This will allow the cast to always work correctly when using trimming.
+
\ No newline at end of file
diff --git a/src/Authoring/WinRT.SourceGenerator/DiagnosticHelpers.cs b/src/Authoring/WinRT.SourceGenerator/DiagnosticHelpers.cs
index a8e0f112a..3854c93b8 100644
--- a/src/Authoring/WinRT.SourceGenerator/DiagnosticHelpers.cs
+++ b/src/Authoring/WinRT.SourceGenerator/DiagnosticHelpers.cs
@@ -1,412 +1,412 @@
-using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.CSharp;
-using Microsoft.CodeAnalysis.CSharp.Syntax;
-using System;
-using System.Collections.Generic;
-using System.Collections.Immutable;
-using System.Linq;
-using System.Runtime.CompilerServices;
-using WinRT.SourceGenerator;
-
-namespace Generator
-{
- public partial class WinRTComponentScanner
- {
- private void Flag() { _flag |= true; }
- /// Raise the flag so we don't make a winmd, and add a diagnostic to the sourcegenerator
- ///
- private void Report(DiagnosticDescriptor d, Location loc, params object[] args)
- {
- Flag();
- _context.ReportDiagnostic(Diagnostic.Create(d, loc, args));
- }
- private void ReportDiagnostic(Diagnostic d)
- {
- Flag();
- _context.ReportDiagnostic(d);
- }
- private SemanticModel GetModel(SyntaxTree t) { return _context.Compilation.GetSemanticModel(t); }
-
- private INamedTypeSymbol GetTypeByMetadataName(string fullyQualifiedMetadataName)
- {
- return _context.Compilation.GetTypeByMetadataName(fullyQualifiedMetadataName);
- }
-
- private bool SymEq(ISymbol sym1, ISymbol sym2) { return SymbolEqualityComparer.Default.Equals(sym1, sym2); }
-
- ///
- /// Returns true if the class represented by the symbol
- /// implements any of the interfaces defined in ProhibitedAsyncInterfaces (e.g., IAsyncAction, ...)
- /// The class/interface type symbolThe containing class/interface declaration
- /// True iff the given class implements any of the IAsync interfaces that are not valid in Windows Runtime
- private void ImplementsInvalidInterface(INamedTypeSymbol typeSymbol, TypeDeclarationSyntax typeDeclaration)
- {
- bool AsyncActionCase(INamedTypeSymbol sym)
- {
- // are we using Windows.Foundation.IAsyncAction ?
- bool isWindowsFoundation = sym.ContainingNamespace.IsGlobalNamespace ||
- string.CompareOrdinal(sym.ContainingNamespace.Name, "Windows.Foundation") == 0;
- bool isAsyncAction = string.CompareOrdinal(sym.MetadataName, "IAsyncAction") == 0;
- return isWindowsFoundation && isAsyncAction;
- }
-
- if (typeSymbol.BaseType != null && typeSymbol.BaseType.ContainingNamespace != null)
- {
- if (AsyncActionCase(typeSymbol.BaseType))
- {
- Report(WinRTRules.NonWinRTInterface, typeDeclaration.GetLocation(), typeDeclaration.Identifier, "IAsyncAction");
- }
- }
-
- foreach (var implementedInterface in typeSymbol.AllInterfaces)
- {
- if (AsyncActionCase(implementedInterface))
- {
- Report(WinRTRules.NonWinRTInterface, typeDeclaration.GetLocation(), typeDeclaration.Identifier, "IAsyncAction");
- }
- }
-
- foreach (string prohibitedInterface in nonWindowsRuntimeInterfaces)
- {
- if (ImplementsInterface(typeSymbol, prohibitedInterface))
- {
- Report(WinRTRules.NonWinRTInterface, typeDeclaration.GetLocation(), typeDeclaration.Identifier, prohibitedInterface);
- }
- }
- }
-
- ///
- /// See if this class/interfaces inherits the given type
- ///
- /// type that might inherit
- /// Inherited interface or class
- private bool ImplementsInterface(INamedTypeSymbol typeSymbol, string typeToCheck)
- {
- if (typeSymbol == null)
- {
- return false;
- }
-
- // for interface type symbols
- foreach (var implementedInterface in typeSymbol.AllInterfaces)
- {
- if (implementedInterface.MetadataName == typeToCheck)
- {
- return true;
- }
- }
-
- // class type symbols might have a BaseType, like System.Exception
- if (typeSymbol.BaseType != null)
- {
- foreach (var x in typeSymbol.BaseType.AllInterfaces)
- {
- if (x.MetadataName == typeToCheck)
- {
- return true;
- }
- }
- var typeToCheckSymbol = GetTypeByMetadataName(typeToCheck);
- if (SymEq(typeSymbol.BaseType, typeToCheckSymbol))
- {
- return true;
- }
- // Type -> Type`n
- if (typeSymbol.BaseType.MetadataName != null)
- {
- return typeSymbol.BaseType.MetadataName == typeToCheck;
- }
- }
-
- return false;
- }
-
- private bool IsPublic(MemberDeclarationSyntax member) { return member.Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword)); }
-
- #region Attributes
-
- /// Attributes can come in one list or many, e.g. [A(),B()] vs. [A()][B()]
- /// look at all possible attributes and see if any match the given string
- /// attribute names need to be fully qualified, e.g. DefaultOverload is really Windows.Foundation.Metadata.DefaultOverload
- /// all the syntax nodes that correspond to an attribute list
- /// true iff the given attribute is in the list
- private bool MatchesAnyAttribute(string attrName, SyntaxList ls)
- {
- foreach (var attrList in ls)
- {
- foreach (var attr in attrList.Attributes)
- {
- // no declared symbol for AttributeSyntax...
- if (string.CompareOrdinal(attr.Name.ToString(), attrName) == 0)
- {
- return true;
- }
- }
- }
- return false;
- }
-
- private bool MatchesAnyAttribute(string attrName, ImmutableArray attributes)
- {
- string attrClassName = attrName + "Attribute";
- return attributes.Any((attr) => attr.AttributeClass.Name == attrClassName);
- }
-
- ///
- /// Checks to see if an array parameter has been marked with both Write and Read attributes
- /// Does extra work, by catching `ref` params, done here since this code can be used by class or interface related methods
- /// Method declared
- /// true if array attributes are invalid (see summary)
- private void ParameterHasAttributeErrors(MethodDeclarationSyntax method)
- {
- // helper function, used to see if param has Ref or Out keyword
- bool HasModifier(ParameterSyntax param, SyntaxKind kind) { return param.Modifiers.Where(m => m.IsKind(kind)).Any(); }
-
- foreach (ParameterSyntax param in method.ParameterList.Parameters)
- {
- var isArrayType = param.Type.IsKind(SyntaxKind.ArrayType);
- bool hasReadOnlyArray = ParamHasReadOnlyAttribute(param);
- bool hasWriteOnlyArray = ParamHasWriteOnlyAttribute(param);
-
- // Nothing can be marked `ref`
- if (HasModifier(param, SyntaxKind.RefKeyword))
- {
- Report(WinRTRules.RefParameterFound, method.GetLocation(), method.Identifier, param.Identifier);
- }
-
- if (ParamHasInOrOutAttribute(param))
- {
- // recommend using ReadOnlyArray or WriteOnlyArray instead of In/Out
- if (isArrayType)
- {
- Report(WinRTRules.ArrayMarkedInOrOut, method.GetLocation(), method.Identifier, param.Identifier);
- }
- // if not array type, stil can't use [In] or [Out]
- else
- {
- Report(WinRTRules.NonArrayMarkedInOrOut, method.GetLocation(), method.Identifier, param.Identifier);
- }
- }
-
- if (isArrayType)
- {
- bool isOutputParam = HasModifier(param, SyntaxKind.OutKeyword);
- // can't be both ReadOnly and WriteOnly
- if (hasReadOnlyArray && hasWriteOnlyArray)
- {
- Report(WinRTRules.ArrayParamMarkedBoth, method.GetLocation(), method.Identifier, param.Identifier);
- }
- // can't be both output (writeonly) and marked read only
- else if (hasReadOnlyArray && isOutputParam)
- {
- Report(WinRTRules.ArrayOutputParamMarkedRead, method.GetLocation(), method.Identifier, param.Identifier);
- }
- // must have some indication of ReadOnly or WriteOnly
- else if (!hasWriteOnlyArray && !hasReadOnlyArray && !isOutputParam)
- {
- Report(WinRTRules.ArrayParamNotMarked, method.GetLocation(), method.Identifier, param.Identifier);
- }
- }
- // Non-array types shouldn't have attributes meant for arrays
- else if (hasWriteOnlyArray || hasReadOnlyArray)
- {
- Report(WinRTRules.NonArrayMarked, method.GetLocation(), method.Identifier, param.Identifier);
- }
- }
- }
-
- /// Looks at all possible attributes on a given parameter declaration
- /// returns true iff any are (string) equal to the given attribute name
- private bool ParamHasAttribute(ParameterSyntax param, string attrName) { return MatchesAnyAttribute(attrName, param.AttributeLists); }
-
- /// Check for qualified and unqualified [In] and [Out] attribute on the parameter
- ///
- /// True if any attribute is the In or Out attribute
- private bool ParamHasInOrOutAttribute(ParameterSyntax param) { return InAndOutAttributeNames.Where(str => ParamHasAttribute(param, str)).Any(); }
- private bool ParamHasReadOnlyAttribute(ParameterSyntax param) { return ReadOnlyArrayAttributeNames.Where(str => ParamHasAttribute(param, str)).Any(); }
- private bool ParamHasWriteOnlyAttribute(ParameterSyntax param) { return WriteOnlyArrayAttributeNames.Where(str => ParamHasAttribute(param, str)).Any(); }
-
- private ISymbol GetInterfaceMemberFromClassMember(ISymbol classMember)
- {
- var parent = classMember.ContainingType;
- foreach (var @interface in parent.AllInterfaces)
- {
- foreach (var interfaceMember in @interface.GetMembers())
- {
- if (SymbolEqualityComparer.Default.Equals(parent.FindImplementationForInterfaceMember(interfaceMember), classMember))
- {
- return interfaceMember;
- }
- }
- }
-
- return null;
- }
-
- /// Check for qualified and unqualified [DefaultOverload] attribute on the parameter<
- ///
- /// True if any attribute is the DefaultOverload attribute
- private bool HasDefaultOverloadAttribute(MethodDeclarationSyntax method)
- {
- if (OverloadAttributeNames.Where(str => MatchesAnyAttribute(str, method.AttributeLists)).Any())
- {
- return true;
- }
-
- var interfaceMember = GetInterfaceMemberFromClassMember(GetModel(method.SyntaxTree).GetDeclaredSymbol(method));
- if (interfaceMember == null)
- {
- return false;
- }
- return OverloadAttributeNames.Where(str => MatchesAnyAttribute(str, interfaceMember.GetAttributes())).Any();
- }
-
- ///
- /// Keeps track of repeated declarations of a method (overloads) and raises diagnostics according to the rule that exactly one overload should be attributed the default
- /// Look for overloads of this method, checking the attributes as well attributes for
- ///
- /// The strings are unique names for each method -- made by its name and arity
- /// Some methods may get the attribute, some may not, we keep track of this in the map.
- ///
- /// Once we have seen an overload and the method still has no attribute, we make a diagnostic for it
- /// If we see the method again but this time with the attribute, we remove it (and its diagnostic) from the map
- /// The class the method lives in -- used for creating the diagnostic
- /// True iff multiple overloads of a method are found, where more than one has been designated as the default overload
- private void CheckOverloadAttributes(MethodDeclarationSyntax method,
- Dictionary methodHasAttributeMap,
- Dictionary overloadsWithoutAttributeMap,
- SyntaxToken classId)
- {
- int methodArity = method.ParameterList.Parameters.Count;
- string methodNameWithArity = method.Identifier.Text + methodArity;
-
- // look at all the attributes on this method and see if any of them is the DefaultOverload attribute
- bool hasDefaultOverloadAttribute = HasDefaultOverloadAttribute(method);
- bool seenMethodBefore = methodHasAttributeMap.TryGetValue(methodNameWithArity, out bool methodHasAttrAlready);
-
- // Do we have an overload ?
- if (seenMethodBefore)
- {
- if (hasDefaultOverloadAttribute && !methodHasAttrAlready)
- {
- // we've seen it, but it didnt have the attribute, so mark that it has it now
- methodHasAttributeMap[methodNameWithArity] = true;
- // We finally got an attribute, so dont raise a diagnostic for this method
- overloadsWithoutAttributeMap.Remove(methodNameWithArity);
- }
- else if (hasDefaultOverloadAttribute && methodHasAttrAlready)
- {
- // Special case in that multiple instances of the DefaultAttribute being used on the method
- Report(WinRTRules.MultipleDefaultOverloadAttribute, method.GetLocation(), methodArity, method.Identifier, classId);
- }
- else if (!hasDefaultOverloadAttribute && !methodHasAttrAlready)
- {
- // we could see this method later with the attribute,
- // so hold onto the diagnostic for it until we know it doesn't have the attribute
- overloadsWithoutAttributeMap[methodNameWithArity] = Diagnostic.Create(
- WinRTRules.NeedDefaultOverloadAttribute,
- method.GetLocation(),
- methodArity,
- method.Identifier,
- classId);
- }
- }
- else
- {
- // first time we're seeing the method, add a pair in the map for its name and whether it has the attribute
- methodHasAttributeMap[methodNameWithArity] = hasDefaultOverloadAttribute;
- }
- }
-
- #endregion
-
- /// Make a suggestion for types to use instead of the given type
- /// A type that is not valid in Windows Runtime
- /// string of types that the given type implements and are valid Windows Runtime types
- private string SuggestType(string type)
- {
- switch (type)
- {
- case "System.Collections.Generic.Dictionary`2":
- return "IDictionary, IReadOnlyDictionary, IEnumerable>";
- case "System.Collections.ObjectModel.ReadOnlyDictionary`2":
- return "IReadOnlyDictionary, IEnumerable>, IDictionary";
- case "System.Collections.Generic.List`1":
- return "IList, IReadOnlyList, IEnumerable";
- case "System.Linq.Enumerable`1":
- return "IEnumerable";
- case "System.Collections.Generic.KeyValuePair":
- return "KeyValuePair";
- case "System.Array":
- return "T[]";
- default: return "No suggestions for type";
- }
- }
-
- ///
- /// the common term for the given syntax type
- private string SimplifySyntaxTypeString(string syntaxType)
- {
- switch (syntaxType)
- {
- case "EventFieldDeclarationSyntax": return "event";
- case "ConstructorDeclarationSyntax": return "constructor";
- case "DelegateDeclarationSyntax": return "delegate";
- case "IndexerDeclarationSyntax": return "indexer";
- case "MethodDeclarationSyntax": return "method";
- case "OperatorDeclarationSyntax": return "operator";
- case "PropertyDeclarationSyntax": return "property";
- default: return "unknown syntax type: " + syntaxType;
- }
- }
-
- private SpecialType[] ValidStructFieldTypes = new SpecialType[]
- {
- SpecialType.System_Boolean,
- SpecialType.System_String,
- SpecialType.System_Single,
- SpecialType.System_Double,
- SpecialType.System_UInt16,
- SpecialType.System_UInt32,
- SpecialType.System_UInt64,
- SpecialType.System_Int16,
- SpecialType.System_Int32,
- SpecialType.System_Int64,
- };
-
- private static readonly HashSet nonWindowsRuntimeInterfaces = new HashSet()
- {
- "System.Exception",
- "IAsyncActionWithProgress`1",
- "IAsyncOperation`1",
- "IAsyncOperationWithProgress`2",
- };
-
- private readonly static HashSet NotValidTypes = new HashSet()
- {
- "System.Array",
- "System.Collections.Generic.Dictionary`2",
- "System.Collections.Generic.List`1",
- "System.Collections.Generic.KeyValuePair"
- };
-
- private readonly static HashSet WIPNotValidTypes = new HashSet()
- {
- "System.Linq.Enumerable",
- "Enumerable",
- "System.Collections.ObjectModel.ReadOnlyDictionary`2",
- "ReadOnlyDictionary`2"
- };
-
- private static readonly HashSet InAndOutAttributeNames = new HashSet()
- { "In", "Out", "System.Runtime.InteropServices.In", "System.Runtime.InteropServices.Out" };
- private static readonly HashSet OverloadAttributeNames = new HashSet()
- { "DefaultOverload", "Windows.Foundation.Metadata.DefaultOverload" };
- private static readonly HashSet ReadOnlyArrayAttributeNames = new HashSet()
- { "System.Runtime.InteropServices.WindowsRuntime.ReadOnlyArray", "ReadOnlyArray" };
- private static readonly HashSet WriteOnlyArrayAttributeNames = new HashSet()
- { "System.Runtime.InteropServices.WindowsRuntime.WriteOnlyArray", "WriteOnlyArray" };
-
- private static readonly string GeneratedReturnValueName = "__retval";
- }
-}
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+using System;
+using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using WinRT.SourceGenerator;
+
+namespace Generator
+{
+ public partial class WinRTComponentScanner
+ {
+ private void Flag() { _flag |= true; }
+ /// Raise the flag so we don't make a winmd, and add a diagnostic to the sourcegenerator
+ ///
+ private void Report(DiagnosticDescriptor d, Location loc, params object[] args)
+ {
+ Flag();
+ _context.ReportDiagnostic(Diagnostic.Create(d, loc, args));
+ }
+ private void ReportDiagnostic(Diagnostic d)
+ {
+ Flag();
+ _context.ReportDiagnostic(d);
+ }
+ private SemanticModel GetModel(SyntaxTree t) { return _context.Compilation.GetSemanticModel(t); }
+
+ private INamedTypeSymbol GetTypeByMetadataName(string fullyQualifiedMetadataName)
+ {
+ return _context.Compilation.GetTypeByMetadataName(fullyQualifiedMetadataName);
+ }
+
+ private bool SymEq(ISymbol sym1, ISymbol sym2) { return SymbolEqualityComparer.Default.Equals(sym1, sym2); }
+
+ ///
+ /// Returns true if the class represented by the symbol
+ /// implements any of the interfaces defined in ProhibitedAsyncInterfaces (e.g., IAsyncAction, ...)
+ /// The class/interface type symbolThe containing class/interface declaration
+ /// True iff the given class implements any of the IAsync interfaces that are not valid in Windows Runtime
+ private void ImplementsInvalidInterface(INamedTypeSymbol typeSymbol, TypeDeclarationSyntax typeDeclaration)
+ {
+ bool AsyncActionCase(INamedTypeSymbol sym)
+ {
+ // are we using Windows.Foundation.IAsyncAction ?
+ bool isWindowsFoundation = sym.ContainingNamespace.IsGlobalNamespace ||
+ string.CompareOrdinal(sym.ContainingNamespace.Name, "Windows.Foundation") == 0;
+ bool isAsyncAction = string.CompareOrdinal(sym.MetadataName, "IAsyncAction") == 0;
+ return isWindowsFoundation && isAsyncAction;
+ }
+
+ if (typeSymbol.BaseType != null && typeSymbol.BaseType.ContainingNamespace != null)
+ {
+ if (AsyncActionCase(typeSymbol.BaseType))
+ {
+ Report(WinRTRules.NonWinRTInterface, typeDeclaration.GetLocation(), typeDeclaration.Identifier, "IAsyncAction");
+ }
+ }
+
+ foreach (var implementedInterface in typeSymbol.AllInterfaces)
+ {
+ if (AsyncActionCase(implementedInterface))
+ {
+ Report(WinRTRules.NonWinRTInterface, typeDeclaration.GetLocation(), typeDeclaration.Identifier, "IAsyncAction");
+ }
+ }
+
+ foreach (string prohibitedInterface in nonWindowsRuntimeInterfaces)
+ {
+ if (ImplementsInterface(typeSymbol, prohibitedInterface))
+ {
+ Report(WinRTRules.NonWinRTInterface, typeDeclaration.GetLocation(), typeDeclaration.Identifier, prohibitedInterface);
+ }
+ }
+ }
+
+ ///
+ /// See if this class/interfaces inherits the given type
+ ///
+ /// type that might inherit
+ /// Inherited interface or class
+ private bool ImplementsInterface(INamedTypeSymbol typeSymbol, string typeToCheck)
+ {
+ if (typeSymbol == null)
+ {
+ return false;
+ }
+
+ // for interface type symbols
+ foreach (var implementedInterface in typeSymbol.AllInterfaces)
+ {
+ if (implementedInterface.MetadataName == typeToCheck)
+ {
+ return true;
+ }
+ }
+
+ // class type symbols might have a BaseType, like System.Exception
+ if (typeSymbol.BaseType != null)
+ {
+ foreach (var x in typeSymbol.BaseType.AllInterfaces)
+ {
+ if (x.MetadataName == typeToCheck)
+ {
+ return true;
+ }
+ }
+ var typeToCheckSymbol = GetTypeByMetadataName(typeToCheck);
+ if (SymEq(typeSymbol.BaseType, typeToCheckSymbol))
+ {
+ return true;
+ }
+ // Type -> Type`n
+ if (typeSymbol.BaseType.MetadataName != null)
+ {
+ return typeSymbol.BaseType.MetadataName == typeToCheck;
+ }
+ }
+
+ return false;
+ }
+
+ private bool IsPublic(MemberDeclarationSyntax member) { return member.Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword)); }
+
+ #region Attributes
+
+ /// Attributes can come in one list or many, e.g. [A(),B()] vs. [A()][B()]
+ /// look at all possible attributes and see if any match the given string
+ /// attribute names need to be fully qualified, e.g. DefaultOverload is really Windows.Foundation.Metadata.DefaultOverload
+ /// all the syntax nodes that correspond to an attribute list
+ /// true iff the given attribute is in the list
+ private bool MatchesAnyAttribute(string attrName, SyntaxList ls)
+ {
+ foreach (var attrList in ls)
+ {
+ foreach (var attr in attrList.Attributes)
+ {
+ // no declared symbol for AttributeSyntax...
+ if (string.CompareOrdinal(attr.Name.ToString(), attrName) == 0)
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ private bool MatchesAnyAttribute(string attrName, ImmutableArray attributes)
+ {
+ string attrClassName = attrName + "Attribute";
+ return attributes.Any((attr) => attr.AttributeClass.Name == attrClassName);
+ }
+
+ ///
+ /// Checks to see if an array parameter has been marked with both Write and Read attributes
+ /// Does extra work, by catching `ref` params, done here since this code can be used by class or interface related methods
+ /// Method declared
+ /// true if array attributes are invalid (see summary)
+ private void ParameterHasAttributeErrors(MethodDeclarationSyntax method)
+ {
+ // helper function, used to see if param has Ref or Out keyword
+ bool HasModifier(ParameterSyntax param, SyntaxKind kind) { return param.Modifiers.Where(m => m.IsKind(kind)).Any(); }
+
+ foreach (ParameterSyntax param in method.ParameterList.Parameters)
+ {
+ var isArrayType = param.Type.IsKind(SyntaxKind.ArrayType);
+ bool hasReadOnlyArray = ParamHasReadOnlyAttribute(param);
+ bool hasWriteOnlyArray = ParamHasWriteOnlyAttribute(param);
+
+ // Nothing can be marked `ref`
+ if (HasModifier(param, SyntaxKind.RefKeyword))
+ {
+ Report(WinRTRules.RefParameterFound, method.GetLocation(), method.Identifier, param.Identifier);
+ }
+
+ if (ParamHasInOrOutAttribute(param))
+ {
+ // recommend using ReadOnlyArray or WriteOnlyArray instead of In/Out
+ if (isArrayType)
+ {
+ Report(WinRTRules.ArrayMarkedInOrOut, method.GetLocation(), method.Identifier, param.Identifier);
+ }
+ // if not array type, stil can't use [In] or [Out]
+ else
+ {
+ Report(WinRTRules.NonArrayMarkedInOrOut, method.GetLocation(), method.Identifier, param.Identifier);
+ }
+ }
+
+ if (isArrayType)
+ {
+ bool isOutputParam = HasModifier(param, SyntaxKind.OutKeyword);
+ // can't be both ReadOnly and WriteOnly
+ if (hasReadOnlyArray && hasWriteOnlyArray)
+ {
+ Report(WinRTRules.ArrayParamMarkedBoth, method.GetLocation(), method.Identifier, param.Identifier);
+ }
+ // can't be both output (writeonly) and marked read only
+ else if (hasReadOnlyArray && isOutputParam)
+ {
+ Report(WinRTRules.ArrayOutputParamMarkedRead, method.GetLocation(), method.Identifier, param.Identifier);
+ }
+ // must have some indication of ReadOnly or WriteOnly
+ else if (!hasWriteOnlyArray && !hasReadOnlyArray && !isOutputParam)
+ {
+ Report(WinRTRules.ArrayParamNotMarked, method.GetLocation(), method.Identifier, param.Identifier);
+ }
+ }
+ // Non-array types shouldn't have attributes meant for arrays
+ else if (hasWriteOnlyArray || hasReadOnlyArray)
+ {
+ Report(WinRTRules.NonArrayMarked, method.GetLocation(), method.Identifier, param.Identifier);
+ }
+ }
+ }
+
+ /// Looks at all possible attributes on a given parameter declaration
+ /// returns true iff any are (string) equal to the given attribute name
+ private bool ParamHasAttribute(ParameterSyntax param, string attrName) { return MatchesAnyAttribute(attrName, param.AttributeLists); }
+
+ /// Check for qualified and unqualified [In] and [Out] attribute on the parameter
+ ///
+ /// True if any attribute is the In or Out attribute
+ private bool ParamHasInOrOutAttribute(ParameterSyntax param) { return InAndOutAttributeNames.Where(str => ParamHasAttribute(param, str)).Any(); }
+ private bool ParamHasReadOnlyAttribute(ParameterSyntax param) { return ReadOnlyArrayAttributeNames.Where(str => ParamHasAttribute(param, str)).Any(); }
+ private bool ParamHasWriteOnlyAttribute(ParameterSyntax param) { return WriteOnlyArrayAttributeNames.Where(str => ParamHasAttribute(param, str)).Any(); }
+
+ private ISymbol GetInterfaceMemberFromClassMember(ISymbol classMember)
+ {
+ var parent = classMember.ContainingType;
+ foreach (var @interface in parent.AllInterfaces)
+ {
+ foreach (var interfaceMember in @interface.GetMembers())
+ {
+ if (SymbolEqualityComparer.Default.Equals(parent.FindImplementationForInterfaceMember(interfaceMember), classMember))
+ {
+ return interfaceMember;
+ }
+ }
+ }
+
+ return null;
+ }
+
+ /// Check for qualified and unqualified [DefaultOverload] attribute on the parameter<
+ ///
+ /// True if any attribute is the DefaultOverload attribute
+ private bool HasDefaultOverloadAttribute(MethodDeclarationSyntax method)
+ {
+ if (OverloadAttributeNames.Where(str => MatchesAnyAttribute(str, method.AttributeLists)).Any())
+ {
+ return true;
+ }
+
+ var interfaceMember = GetInterfaceMemberFromClassMember(GetModel(method.SyntaxTree).GetDeclaredSymbol(method));
+ if (interfaceMember == null)
+ {
+ return false;
+ }
+ return OverloadAttributeNames.Where(str => MatchesAnyAttribute(str, interfaceMember.GetAttributes())).Any();
+ }
+
+ ///
+ /// Keeps track of repeated declarations of a method (overloads) and raises diagnostics according to the rule that exactly one overload should be attributed the default
+ /// Look for overloads of this method, checking the attributes as well attributes for
+ ///
+ /// The strings are unique names for each method -- made by its name and arity
+ /// Some methods may get the attribute, some may not, we keep track of this in the map.
+ ///
+ /// Once we have seen an overload and the method still has no attribute, we make a diagnostic for it
+ /// If we see the method again but this time with the attribute, we remove it (and its diagnostic) from the map
+ /// The class the method lives in -- used for creating the diagnostic
+ /// True iff multiple overloads of a method are found, where more than one has been designated as the default overload
+ private void CheckOverloadAttributes(MethodDeclarationSyntax method,
+ Dictionary methodHasAttributeMap,
+ Dictionary overloadsWithoutAttributeMap,
+ SyntaxToken classId)
+ {
+ int methodArity = method.ParameterList.Parameters.Count;
+ string methodNameWithArity = method.Identifier.Text + methodArity;
+
+ // look at all the attributes on this method and see if any of them is the DefaultOverload attribute
+ bool hasDefaultOverloadAttribute = HasDefaultOverloadAttribute(method);
+ bool seenMethodBefore = methodHasAttributeMap.TryGetValue(methodNameWithArity, out bool methodHasAttrAlready);
+
+ // Do we have an overload ?
+ if (seenMethodBefore)
+ {
+ if (hasDefaultOverloadAttribute && !methodHasAttrAlready)
+ {
+ // we've seen it, but it didnt have the attribute, so mark that it has it now
+ methodHasAttributeMap[methodNameWithArity] = true;
+ // We finally got an attribute, so dont raise a diagnostic for this method
+ overloadsWithoutAttributeMap.Remove(methodNameWithArity);
+ }
+ else if (hasDefaultOverloadAttribute && methodHasAttrAlready)
+ {
+ // Special case in that multiple instances of the DefaultAttribute being used on the method
+ Report(WinRTRules.MultipleDefaultOverloadAttribute, method.GetLocation(), methodArity, method.Identifier, classId);
+ }
+ else if (!hasDefaultOverloadAttribute && !methodHasAttrAlready)
+ {
+ // we could see this method later with the attribute,
+ // so hold onto the diagnostic for it until we know it doesn't have the attribute
+ overloadsWithoutAttributeMap[methodNameWithArity] = Diagnostic.Create(
+ WinRTRules.NeedDefaultOverloadAttribute,
+ method.GetLocation(),
+ methodArity,
+ method.Identifier,
+ classId);
+ }
+ }
+ else
+ {
+ // first time we're seeing the method, add a pair in the map for its name and whether it has the attribute
+ methodHasAttributeMap[methodNameWithArity] = hasDefaultOverloadAttribute;
+ }
+ }
+
+ #endregion
+
+ /// Make a suggestion for types to use instead of the given type
+ /// A type that is not valid in Windows Runtime
+ /// string of types that the given type implements and are valid Windows Runtime types
+ private string SuggestType(string type)
+ {
+ switch (type)
+ {
+ case "System.Collections.Generic.Dictionary`2":
+ return "IDictionary, IReadOnlyDictionary, IEnumerable>";
+ case "System.Collections.ObjectModel.ReadOnlyDictionary`2":
+ return "IReadOnlyDictionary, IEnumerable>, IDictionary";
+ case "System.Collections.Generic.List`1":
+ return "IList, IReadOnlyList, IEnumerable";
+ case "System.Linq.Enumerable`1":
+ return "IEnumerable";
+ case "System.Collections.Generic.KeyValuePair":
+ return "KeyValuePair";
+ case "System.Array":
+ return "T[]";
+ default: return "No suggestions for type";
+ }
+ }
+
+ ///
+ /// the common term for the given syntax type
+ private string SimplifySyntaxTypeString(string syntaxType)
+ {
+ switch (syntaxType)
+ {
+ case "EventFieldDeclarationSyntax": return "event";
+ case "ConstructorDeclarationSyntax": return "constructor";
+ case "DelegateDeclarationSyntax": return "delegate";
+ case "IndexerDeclarationSyntax": return "indexer";
+ case "MethodDeclarationSyntax": return "method";
+ case "OperatorDeclarationSyntax": return "operator";
+ case "PropertyDeclarationSyntax": return "property";
+ default: return "unknown syntax type: " + syntaxType;
+ }
+ }
+
+ private SpecialType[] ValidStructFieldTypes = new SpecialType[]
+ {
+ SpecialType.System_Boolean,
+ SpecialType.System_String,
+ SpecialType.System_Single,
+ SpecialType.System_Double,
+ SpecialType.System_UInt16,
+ SpecialType.System_UInt32,
+ SpecialType.System_UInt64,
+ SpecialType.System_Int16,
+ SpecialType.System_Int32,
+ SpecialType.System_Int64,
+ };
+
+ private static readonly HashSet nonWindowsRuntimeInterfaces = new HashSet()
+ {
+ "System.Exception",
+ "IAsyncActionWithProgress`1",
+ "IAsyncOperation`1",
+ "IAsyncOperationWithProgress`2",
+ };
+
+ private static readonly HashSet NotValidTypes = new HashSet()
+ {
+ "System.Array",
+ "System.Collections.Generic.Dictionary`2",
+ "System.Collections.Generic.List`1",
+ "System.Collections.Generic.KeyValuePair"
+ };
+
+ private static readonly HashSet WIPNotValidTypes = new HashSet()
+ {
+ "System.Linq.Enumerable",
+ "Enumerable",
+ "System.Collections.ObjectModel.ReadOnlyDictionary`2",
+ "ReadOnlyDictionary`2"
+ };
+
+ private static readonly HashSet InAndOutAttributeNames = new HashSet()
+ { "In", "Out", "System.Runtime.InteropServices.In", "System.Runtime.InteropServices.Out" };
+ private static readonly HashSet OverloadAttributeNames = new HashSet()
+ { "DefaultOverload", "Windows.Foundation.Metadata.DefaultOverload" };
+ private static readonly HashSet ReadOnlyArrayAttributeNames = new HashSet()
+ { "System.Runtime.InteropServices.WindowsRuntime.ReadOnlyArray", "ReadOnlyArray" };
+ private static readonly HashSet WriteOnlyArrayAttributeNames = new HashSet()
+ { "System.Runtime.InteropServices.WindowsRuntime.WriteOnlyArray", "WriteOnlyArray" };
+
+ private static readonly string GeneratedReturnValueName = "__retval";
+ }
+}
diff --git a/src/Authoring/WinRT.SourceGenerator/DiagnosticUtils.cs b/src/Authoring/WinRT.SourceGenerator/DiagnosticUtils.cs
index ee26dec01..65ad00f6d 100644
--- a/src/Authoring/WinRT.SourceGenerator/DiagnosticUtils.cs
+++ b/src/Authoring/WinRT.SourceGenerator/DiagnosticUtils.cs
@@ -7,20 +7,22 @@
using System;
using WinRT.SourceGenerator;
-namespace Generator
+namespace Generator
{
public partial class WinRTComponentScanner
{
- public WinRTComponentScanner(GeneratorExecutionContext context, string assemblyName)
- {
+ public WinRTComponentScanner(GeneratorExecutionContext context, string assemblyName)
+ {
_assemblyName = assemblyName;
_context = context;
_flag = false;
+ _typeMapper = new TypeMapper(context.AnalyzerConfigOptions.GetCsWinRTUseWindowsUIXamlProjections());
}
private readonly string _assemblyName;
private readonly GeneratorExecutionContext _context;
private bool _flag;
+ private readonly TypeMapper _typeMapper;
public bool Found() { return _flag; }
@@ -31,70 +33,72 @@ public void FindDiagnostics()
{
WinRTSyntaxReceiver syntaxReceiver = (WinRTSyntaxReceiver)_context.SyntaxReceiver;
- if (!syntaxReceiver.Declarations.Any())
- {
+ if (!syntaxReceiver.Declarations.Any())
+ {
Report(WinRTRules.NoPublicTypesRule, null);
return;
}
- CheckNamespaces();
- CheckDeclarations();
- }
-
- private void CheckNamespaces()
- {
+ CheckNamespaces();
+ CheckDeclarations();
+ }
+
+ private void CheckNamespaces()
+ {
WinRTSyntaxReceiver syntaxReceiver = (WinRTSyntaxReceiver)_context.SyntaxReceiver;
-
- // Used to check for conflicitng namespace names
- HashSet namespaceNames = new();
-
- foreach (var @namespace in syntaxReceiver.Namespaces)
- {
- var model = _context.Compilation.GetSemanticModel(@namespace.SyntaxTree);
- var namespaceSymbol = model.GetDeclaredSymbol(@namespace);
-
- string namespaceString = namespaceSymbol.ToString();
-
- bool newNamespaceDeclaration = true;
- // Because modules could have a namespace defined in different places (i.e. defines a partial class)
- // we can't rely on `Contains` so we manually check that namespace names cannot differ by case only
- foreach (var usedNamespaceName in namespaceNames)
- {
- if (String.Equals(namespaceString, usedNamespaceName, StringComparison.OrdinalIgnoreCase) &&
- !String.Equals(namespaceString, usedNamespaceName, StringComparison.Ordinal))
- {
- newNamespaceDeclaration = false;
- Report(WinRTRules.NamespacesDifferByCase, namespaceSymbol.Locations.First(), namespaceString);
- }
- }
-
- if (newNamespaceDeclaration)
- {
- namespaceNames.Add(namespaceString);
- }
-
- if (IsInvalidNamespace(namespaceSymbol, _assemblyName))
- {
- Report(WinRTRules.DisjointNamespaceRule, namespaceSymbol.Locations.First(), _assemblyName, namespaceString);
- }
+
+ // Used to check for conflicting namespace names
+ HashSet namespaceNames = new();
+
+ foreach (var @namespace in syntaxReceiver.Namespaces)
+ {
+ var model = _context.Compilation.GetSemanticModel(@namespace.SyntaxTree);
+ var namespaceSymbol = model.GetDeclaredSymbol(@namespace);
+
+ string namespaceString = namespaceSymbol.ToString();
+
+ bool newNamespaceDeclaration = true;
+ // Because modules could have a namespace defined in different places (i.e. defines a partial class)
+ // we can't rely on `Contains` so we manually check that namespace names cannot differ by case only
+ foreach (var usedNamespaceName in namespaceNames)
+ {
+ if (String.Equals(namespaceString, usedNamespaceName, StringComparison.OrdinalIgnoreCase) &&
+ !String.Equals(namespaceString, usedNamespaceName, StringComparison.Ordinal))
+ {
+ newNamespaceDeclaration = false;
+ Report(WinRTRules.NamespacesDifferByCase, namespaceSymbol.Locations.First(), namespaceString);
+ }
+ }
+
+ if (newNamespaceDeclaration)
+ {
+ namespaceNames.Add(namespaceString);
+ }
+
+ if (IsInvalidNamespace(namespaceSymbol, _assemblyName))
+ {
+ Report(WinRTRules.DisjointNamespaceRule, namespaceSymbol.Locations.First(), _assemblyName, namespaceString);
+ }
}
}
- private void CheckDeclarations()
- {
- WinRTSyntaxReceiver syntaxReceiver = (WinRTSyntaxReceiver)_context.SyntaxReceiver;
-
+ private void CheckDeclarations()
+ {
+ WinRTSyntaxReceiver syntaxReceiver = (WinRTSyntaxReceiver)_context.SyntaxReceiver;
+
foreach (var declaration in syntaxReceiver.Declarations)
{
var model = _context.Compilation.GetSemanticModel(declaration.SyntaxTree);
+ var symbol = model.GetDeclaredSymbol(declaration);
// Check symbol information for whether it is public to properly detect partial types
- // which can leave out modifier.
- if (model.GetDeclaredSymbol(declaration).DeclaredAccessibility != Accessibility.Public)
- {
- continue;
- }
-
+ // which can leave out modifier. Also ignore nested types not effectively public
+ if (symbol.DeclaredAccessibility != Accessibility.Public ||
+ (symbol is ITypeSymbol typeSymbol && !typeSymbol.IsPubliclyAccessible()))
+ {
+ continue;
+ }
+
if (declaration is ClassDeclarationSyntax @class)
{
var classId = @class.Identifier;
@@ -103,7 +107,7 @@ private void CheckDeclarations()
var props = @class.DescendantNodes().OfType().Where(IsPublic);
// filter out methods and properties that will be replaced with our custom type mappings
- IgnoreCustomTypeMappings(classSymbol, ref publicMethods, ref props);
+ IgnoreCustomTypeMappings(classSymbol, _typeMapper, ref publicMethods, ref props);
if (!classSymbol.IsSealed && !classSymbol.IsStatic)
{
@@ -114,112 +118,113 @@ private void CheckDeclarations()
HasMultipleConstructorsOfSameArity(@class);
if (classSymbol.IsGenericType)
- {
+ {
Report(WinRTRules.GenericTypeRule, @class.GetLocation(), classId);
}
// check for things in nonWindowsRuntimeInterfaces
- ImplementsInvalidInterface(classSymbol, @class);
-
- CheckProperties(props, classId);
-
- // check types -- todo: check for !valid types
- CheckMethods(publicMethods, classId);
-
+ ImplementsInvalidInterface(classSymbol, @class);
+
+ CheckProperties(props, classId);
+
+ // check types -- todo: check for !valid types
+ CheckMethods(publicMethods, classId);
+
CheckInterfaces(model, @class);
}
else if (declaration is InterfaceDeclarationSyntax @interface)
{
var interfaceSym = model.GetDeclaredSymbol(@interface);
var methods = @interface.DescendantNodes().OfType();
- var props = @interface.DescendantNodes().OfType().Where(IsPublic);
-
- // filter out methods and properties that will be replaced with our custom type mappings
- IgnoreCustomTypeMappings(interfaceSym, ref methods, ref props);
-
+ var props = @interface.DescendantNodes().OfType().Where(IsPublic);
+
+ // filter out methods and properties that will be replaced with our custom type mappings
+ IgnoreCustomTypeMappings(interfaceSym, _typeMapper, ref methods, ref props);
+
if (interfaceSym.IsGenericType)
{
Report(WinRTRules.GenericTypeRule, @interface.GetLocation(), @interface.Identifier);
}
- ImplementsInvalidInterface(interfaceSym, @interface);
-
- CheckProperties(props, @interface.Identifier);
-
+ ImplementsInvalidInterface(interfaceSym, @interface);
+
+ CheckProperties(props, @interface.Identifier);
+
CheckMethods(methods, @interface.Identifier);
- }
- else if (declaration is StructDeclarationSyntax @struct)
- {
- CheckStructFields(@struct);
- }
+ }
+ else if (declaration is StructDeclarationSyntax @struct)
+ {
+ CheckStructFields(@struct);
+ }
}
- }
-
- private void CheckInterfaces(SemanticModel model, ClassDeclarationSyntax @class)
- {
- var classId = @class.Identifier;
+ }
+
+ private void CheckInterfaces(SemanticModel model, ClassDeclarationSyntax @class)
+ {
+ var classId = @class.Identifier;
var classSymbol = model.GetDeclaredSymbol(@class);
-
- var iWinRTObject = model.Compilation.GetTypeByMetadataName("WinRT.IWinRTObject");
- // validate that the class correctly implements all its interfaces
- var methods = classSymbol.GetMembers().OfType().ToList();
- foreach (var iface in classSymbol.AllInterfaces)
- {
- if (SymbolEqualityComparer.Default.Equals(iface, iWinRTObject))
- {
- continue;
- }
- foreach (var member in iface.GetMembers().OfType())
- {
- var impl = classSymbol.FindImplementationForInterfaceMember(member);
- if (impl == null)
- {
- var explicitIfaceImpl = methods.Where(m => IsMethodImpl(m, member));
- if (!explicitIfaceImpl.Any())
- {
- Report(WinRTRules.UnimplementedInterface, @class.GetLocation(), classId.Text, iface.ToDisplayString(), member.ToDisplayString());
- }
- }
- }
- }
- }
-
- private bool IsMethodImpl(IMethodSymbol m, IMethodSymbol interfaceMethod)
- {
- if (m.Name != interfaceMethod.Name)
- {
- return false;
- }
- if (!m.Parameters.SequenceEqual(interfaceMethod.Parameters))
- {
- return false;
- }
-
- // the return type can be covariant with the interface method's return type (i.e. a sub-type)
- if (SymEq(m.ReturnType, interfaceMethod.ReturnType) && !m.ReturnType.AllInterfaces.Contains(interfaceMethod.ReturnType))
- {
- return false;
- }
- return true;
- }
-
- private void IgnoreCustomTypeMappings(INamedTypeSymbol typeSymbol,
- ref IEnumerable methods,
+
+ var iWinRTObject = model.Compilation.GetTypeByMetadataName("WinRT.IWinRTObject");
+ // validate that the class correctly implements all its interfaces
+ var methods = classSymbol.GetMembers().OfType().ToList();
+ foreach (var iface in classSymbol.AllInterfaces)
+ {
+ if (SymbolEqualityComparer.Default.Equals(iface, iWinRTObject))
+ {
+ continue;
+ }
+ foreach (var member in iface.GetMembers().OfType())
+ {
+ var impl = classSymbol.FindImplementationForInterfaceMember(member);
+ if (impl == null)
+ {
+ var explicitIfaceImpl = methods.Where(m => IsMethodImpl(m, member));
+ if (!explicitIfaceImpl.Any())
+ {
+ Report(WinRTRules.UnimplementedInterface, @class.GetLocation(), classId.Text, iface.ToDisplayString(), member.ToDisplayString());
+ }
+ }
+ }
+ }
+ }
+
+ private bool IsMethodImpl(IMethodSymbol m, IMethodSymbol interfaceMethod)
+ {
+ if (m.Name != interfaceMethod.Name)
+ {
+ return false;
+ }
+ if (!m.Parameters.SequenceEqual(interfaceMethod.Parameters))
+ {
+ return false;
+ }
+
+ // the return type can be covariant with the interface method's return type (i.e. a sub-type)
+ if (SymEq(m.ReturnType, interfaceMethod.ReturnType) && !m.ReturnType.AllInterfaces.Contains(interfaceMethod.ReturnType, SymbolEqualityComparer.Default))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ private void IgnoreCustomTypeMappings(INamedTypeSymbol typeSymbol,
+ TypeMapper typeMapper,
+ ref IEnumerable methods,
ref IEnumerable properties)
{
- string QualifiedName(INamedTypeSymbol sym)
- {
- return sym.OriginalDefinition.ContainingNamespace + "." + sym.OriginalDefinition.MetadataName;
+ string QualifiedName(INamedTypeSymbol sym)
+ {
+ return sym.OriginalDefinition.ContainingNamespace + "." + sym.OriginalDefinition.MetadataName;
}
- HashSet classMethods = new();
+ HashSet classMethods = new(SymbolEqualityComparer.Default);
foreach (var @interface in typeSymbol.AllInterfaces.
- Where(symbol => WinRTTypeWriter.MappedCSharpTypes.ContainsKey(QualifiedName(symbol)) ||
+ Where(symbol => typeMapper.HasMappingForType(QualifiedName(symbol)) ||
WinRTTypeWriter.ImplementedInterfacesWithoutMapping.Contains(QualifiedName(symbol))))
{
foreach (var interfaceMember in @interface.GetMembers())
- {
+ {
classMethods.Add(typeSymbol.FindImplementationForInterfaceMember(interfaceMember));
}
}
@@ -274,9 +279,9 @@ private void HasConflictingParameterName(MethodDeclarationSyntax method)
bool IsInvalidParameterName(ParameterSyntax stx) { return stx.Identifier.Value.Equals(GeneratedReturnValueName); }
var hasInvalidParams = method.ParameterList.Parameters.Any(IsInvalidParameterName);
- if (hasInvalidParams)
- {
- Report(WinRTRules.ParameterNamedValueRule, method.GetLocation(), method.Identifier);
+ if (hasInvalidParams)
+ {
+ Report(WinRTRules.ParameterNamedValueRule, method.GetLocation(), method.Identifier);
}
}
@@ -305,7 +310,7 @@ private void CheckMethods(IEnumerable methodDeclaration
foreach (var arg in methodSym.Parameters)
{
ReportIfInvalidType(arg.Type, method.GetLocation(), method.Identifier, typeId);
- }
+ }
}
/* Finishes up the work started by `CheckOverloadAttributes` */
foreach (var thing in overloadsWithoutAttributeMap)
@@ -324,9 +329,9 @@ private void CheckProperties(IEnumerable props, Synta
var propSym = GetModel(prop.SyntaxTree).GetDeclaredSymbol(prop);
var loc = prop.GetLocation();
- if (propSym.GetMethod == null || !propSym.GetMethod.DeclaredAccessibility.Equals(Accessibility.Public))
- {
- Report(WinRTRules.PrivateGetterRule, loc, prop.Identifier);
+ if (propSym.GetMethod == null || !propSym.GetMethod.DeclaredAccessibility.Equals(Accessibility.Public))
+ {
+ Report(WinRTRules.PrivateGetterRule, loc, prop.Identifier);
}
ReportIfInvalidType(propSym.Type, loc, prop.Identifier, typeId);
@@ -343,12 +348,12 @@ private void CheckStructFields(StructDeclarationSyntax @struct)
{
// delegates not allowed
if (@struct.DescendantNodes().OfType().Any())
- {
+ {
Report(WinRTRules.StructHasInvalidFieldRule, @struct.GetLocation(), @struct.Identifier, SimplifySyntaxTypeString(typeof(DelegateDeclarationSyntax).Name));
}
// methods not allowed
if (@struct.DescendantNodes().OfType().Any())
- {
+ {
Report(WinRTRules.StructHasInvalidFieldRule, @struct.GetLocation(), @struct.Identifier, SimplifySyntaxTypeString(typeof(MethodDeclarationSyntax).Name));
}
@@ -356,16 +361,16 @@ private void CheckStructFields(StructDeclarationSyntax @struct)
// constructors not allowed
if (structSym.Constructors.Length > 1)
- {
+ {
Report(WinRTRules.StructHasInvalidFieldRule, @struct.GetLocation(), @struct.Identifier, SimplifySyntaxTypeString(typeof(ConstructorDeclarationSyntax).Name));
}
var fields = @struct.DescendantNodes().OfType();
- foreach (var field in fields)
+ foreach (var field in fields)
{
// all fields must be public
if (!IsPublic(field))
- {
+ {
Report(WinRTRules.StructHasPrivateFieldRule, field.GetLocation(), @struct.Identifier);
}
@@ -379,14 +384,14 @@ private void CheckStructFields(StructDeclarationSyntax @struct)
{
IFieldSymbol varFieldSym = (IFieldSymbol)GetModel(variable.SyntaxTree).GetDeclaredSymbol(variable);
- if (ValidStructFieldTypes.Contains(varFieldSym.Type.SpecialType) ||
+ if (ValidStructFieldTypes.Contains(varFieldSym.Type.SpecialType) ||
varFieldSym.Type.TypeKind == TypeKind.Struct ||
varFieldSym.Type.TypeKind == TypeKind.Enum)
{
break;
}
else
- {
+ {
Report(WinRTRules.StructHasInvalidFieldRule, variable.GetLocation(), @struct.Identifier, varFieldSym.Name);
}
}
@@ -404,23 +409,23 @@ private void CheckStructFields(StructDeclarationSyntax @struct)
/// the authored namespace to checkthe name of the component/winmd
/// True iff namespace is disjoint from the assembly name
private bool IsInvalidNamespace(INamespaceSymbol @namespace, string assemblyName)
- {
- if (string.CompareOrdinal(@namespace.ToString(), assemblyName) == 0)
- {
- return false;
- }
-
+ {
+ if (string.CompareOrdinal(@namespace.ToString(), assemblyName) == 0)
+ {
+ return false;
+ }
+
var topLevel = @namespace;
while (!topLevel.ContainingNamespace.IsGlobalNamespace)
- {
- if (string.CompareOrdinal(topLevel.ToString(), assemblyName) == 0)
- {
- return false;
+ {
+ if (string.CompareOrdinal(topLevel.ToString(), assemblyName) == 0)
+ {
+ return false;
}
topLevel = topLevel.ContainingNamespace;
- }
-
- return string.CompareOrdinal(topLevel.ToString(), assemblyName) != 0;
+ }
+
+ return string.CompareOrdinal(topLevel.ToString(), assemblyName) != 0;
}
///Array types can only be one dimensional and not System.Array,
@@ -428,22 +433,22 @@ private bool IsInvalidNamespace(INamespaceSymbol @namespace, string assemblyName
///The type to checkwhere the type is
///The method or property with this type in its signature
/// the type this member (method/prop) lives in
- private void ReportIfInvalidType(ITypeSymbol typeSymbol, Location loc, SyntaxToken memberId, SyntaxToken typeId)
+ private void ReportIfInvalidType(ITypeSymbol typeSymbol, Location loc, SyntaxToken memberId, SyntaxToken typeId)
{
// If it's of the form int[], it has to be one dimensional
- if (typeSymbol.TypeKind == TypeKind.Array)
+ if (typeSymbol.TypeKind == TypeKind.Array)
{
IArrayTypeSymbol arrTypeSym = (IArrayTypeSymbol)typeSymbol;
// [,,]?
- if (arrTypeSym.Rank > 1)
- {
+ if (arrTypeSym.Rank > 1)
+ {
Report(WinRTRules.MultiDimensionalArrayRule, loc, memberId, typeId);
return;
- }
- // [][]?
- if (arrTypeSym.ElementType.TypeKind == TypeKind.Array)
- {
+ }
+ // [][]?
+ if (arrTypeSym.ElementType.TypeKind == TypeKind.Array)
+ {
Report(WinRTRules.JaggedArrayRule, loc, memberId, typeId);
return;
}
@@ -451,7 +456,7 @@ private void ReportIfInvalidType(ITypeSymbol typeSymbol, Location loc, SyntaxTok
// NotValidTypes is an array of types that don't exist in Windows Runtime, so can't be passed between functions in Windows Runtime
foreach (var typeName in NotValidTypes)
- {
+ {
var notValidTypeSym = GetTypeByMetadataName(typeName);
if (SymEq(typeSymbol.OriginalDefinition, notValidTypeSym))
{
@@ -465,21 +470,21 @@ private void ReportIfInvalidType(ITypeSymbol typeSymbol, Location loc, SyntaxTok
if (typeSymbol.ContainingNamespace != null && !typeSymbol.ContainingNamespace.IsGlobalNamespace)
{
// ContainingNamespace for Enumerable is just System, but we need System.Linq which is the ContainingSymbol
- qualifiedName += typeSymbol.ContainingSymbol + ".";
+ qualifiedName += typeSymbol.ContainingSymbol + ".";
}
// instead of TypeName, TypeName`1
qualifiedName += typeSymbol.MetadataName;
// GetTypeByMetadataName fails on "System.Linq.Enumerable" & "System.Collections.ObjectModel.ReadOnlyDictionary`2"
// Would be fixed by issue #678 on the dotnet/roslyn-sdk repo
- foreach (var notValidType in WIPNotValidTypes)
+ foreach (var notValidType in WIPNotValidTypes)
{
if (qualifiedName == notValidType)
- {
+ {
Report(WinRTRules.UnsupportedTypeRule, loc, memberId, notValidType, SuggestType(notValidType));
return;
}
}
- }
+ }
}
}
diff --git a/src/Authoring/WinRT.SourceGenerator/DynamicWindowsRuntimeCastGenerator.cs b/src/Authoring/WinRT.SourceGenerator/DynamicWindowsRuntimeCastGenerator.cs
new file mode 100644
index 000000000..b25ca3c63
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator/DynamicWindowsRuntimeCastGenerator.cs
@@ -0,0 +1,109 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+#if ROSLYN_4_12_0_OR_GREATER
+
+using System;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text;
+using Microsoft.CodeAnalysis;
+
+#nullable enable
+
+namespace Generator;
+
+///
+/// A supporting generator for and .
+///
+[Generator]
+public sealed class DynamicWindowsRuntimeCastGenerator : IIncrementalGenerator
+{
+ ///
+ /// The shared writer for all discovered type names.
+ ///
+ [ThreadStatic]
+ private static ArrayBufferWriter? typeNamesWriter;
+
+ ///
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ // Gather all target type names
+ IncrementalValueProvider> typeNames = context.SyntaxProvider.ForAttributeWithMetadataName(
+ fullyQualifiedMetadataName: "WinRT.DynamicWindowsRuntimeCastAttribute",
+ predicate: static (syntaxNode, _) => true,
+ transform: static (context, token) =>
+ {
+ ArrayBufferWriter typeNamesWriter = DynamicWindowsRuntimeCastGenerator.typeNamesWriter ??= [];
+
+ typeNamesWriter.Clear();
+
+ foreach (AttributeData attribute in context.Attributes)
+ {
+ // Double check we have a target type
+ if (attribute.ConstructorArguments is not [{ Kind: TypedConstantKind.Type, IsNull: false, Value: INamedTypeSymbol typeSymbol }])
+ {
+ continue;
+ }
+
+ // Track the current type
+ typeNamesWriter.Add(typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
+ }
+
+ return typeNamesWriter.WrittenSpan.ToImmutableArray();
+ })
+ .SelectMany(static (value, token) => value)
+ .Collect()
+ .Select(static (value, token) => value.Distinct().OrderBy(static name => name).ToImmutableArray());
+
+ // Generate the [DynamicDependency] attributes
+ context.RegisterImplementationSourceOutput(typeNames, static (SourceProductionContext context, ImmutableArray value) =>
+ {
+ if (value.IsEmpty)
+ {
+ return;
+ }
+
+ StringBuilder builder = new();
+
+ builder.AppendLine("""
+ //
+ #pragma warning disable
+
+ namespace WinRT
+ {
+ using global::System.Runtime.CompilerServices;
+ using global::System.Diagnostics.CodeAnalysis;
+
+ ///
+ /// Roots projected types used in dynamic casts.
+ ///
+ internal static class DynamicWindowsRuntimeCastInitializer
+ {
+ ///
+ /// Roots all dependent RCW types.
+ ///
+ [ModuleInitializer]
+ """);
+
+ foreach (string typeName in value)
+ {
+ builder.Append(" [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicFields, typeof(");
+ builder.Append(typeName);
+ builder.AppendLine("))]");
+ }
+
+ builder.Append("""
+ public static void InitializeDynamicWindowsRuntimeCast()
+ {
+ }
+ }
+ }
+ """);
+
+ context.AddSource("DynamicWindowsRuntimeCastInitializer.g.cs", builder.ToString());
+ });
+ }
+}
+
+#endif
diff --git a/src/Authoring/WinRT.SourceGenerator/Extensions/SymbolExtensions.cs b/src/Authoring/WinRT.SourceGenerator/Extensions/SymbolExtensions.cs
new file mode 100644
index 000000000..681ff5f8f
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator/Extensions/SymbolExtensions.cs
@@ -0,0 +1,141 @@
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+using Microsoft.CodeAnalysis;
+
+#nullable enable
+
+namespace Generator;
+
+///
+/// Extensions for symbol types.
+///
+internal static class SymbolExtensions
+{
+ ///
+ /// Checks whether a given type symbol is publicly accessible (ie. it's public and not nested in any non public type).
+ ///
+ /// The type symbol to check for public accessibility.
+ /// Whether is publicly accessible.
+ public static bool IsPubliclyAccessible(this ITypeSymbol type)
+ {
+ for (ITypeSymbol? currentType = type; currentType is not null; currentType = currentType.ContainingType)
+ {
+ // If any type in the type hierarchy is not public, the type is not public.
+ // This makes sure to detect public types nested into eg. a private type.
+ if (currentType.DeclaredAccessibility is not Accessibility.Public)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// Checks whether a given symbol is an explicit interface implementation of a member of an internal interface (or more than one).
+ ///
+ /// The input member symbol to check.
+ /// Whether is an explicit interface implementation of internal interfaces.
+ public static bool IsExplicitInterfaceImplementationOfInternalInterfaces(this ISymbol symbol)
+ {
+ static bool IsAnyContainingTypePublic(IEnumerable symbols)
+ {
+ return symbols.Any(static symbol => symbol.ContainingType!.IsPubliclyAccessible());
+ }
+
+ return symbol switch
+ {
+ IMethodSymbol { ExplicitInterfaceImplementations: { Length: > 0 } methods } => !IsAnyContainingTypePublic(methods),
+ IPropertySymbol { ExplicitInterfaceImplementations: { Length: > 0 } properties } => !IsAnyContainingTypePublic(properties),
+ IEventSymbol { ExplicitInterfaceImplementations: { Length: > 0 } events } => !IsAnyContainingTypePublic(events),
+ _ => false
+ };
+ }
+
+ ///
+ /// Checks whether a type has an attribute with a specified type.
+ ///
+ /// The input instance to check.
+ /// The instance for the attribute type to look for.
+ /// Whether or not has an attribute with the specified name.
+ public static bool HasAttributeWithType(this ISymbol symbol, ITypeSymbol typeSymbol)
+ {
+ return TryGetAttributeWithType(symbol, typeSymbol, out _);
+ }
+
+ ///
+ /// Tries to get an attribute with the specified type.
+ ///
+ /// The input instance to check.
+ /// The instance for the attribute type to look for.
+ /// The resulting attribute, if it was found.
+ /// Whether or not has an attribute with the specified name.
+ public static bool TryGetAttributeWithType(this ISymbol symbol, ITypeSymbol typeSymbol, [NotNullWhen(true)] out AttributeData? attributeData)
+ {
+ foreach (AttributeData attribute in symbol.GetAttributes())
+ {
+ if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, typeSymbol))
+ {
+ attributeData = attribute;
+
+ return true;
+ }
+ }
+
+ attributeData = null;
+
+ return false;
+ }
+
+ ///
+ /// Enumerates all attributes with the specified type.
+ ///
+ /// The input instance to check.
+ /// The instance for the attribute type to look for.
+ /// The matching attributes.
+ public static IEnumerable EnumerateAttributesWithType(this ISymbol symbol, ITypeSymbol typeSymbol)
+ {
+ foreach (AttributeData attribute in symbol.GetAttributes())
+ {
+ if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, typeSymbol))
+ {
+ yield return attribute;
+ }
+ }
+ }
+
+ ///
+ /// Checks whether a given symbol is accessible from the assembly of a given compilation (including eg. through nested types).
+ ///
+ /// The input instance.
+ /// The instance currently in use.
+ /// Whether is accessible from the assembly for .
+ public static bool IsAccessibleFromCompilationAssembly(this ISymbol symbol, Compilation compilation)
+ {
+ return compilation.IsSymbolAccessibleWithin(symbol, compilation.Assembly);
+ }
+
+ ///
+ /// Checks whether or not a given inherits from a specified type.
+ ///
+ /// The target instance to check.
+ /// The instane to check for inheritance from.
+ /// Whether or not inherits from .
+ public static bool InheritsFromType(this ITypeSymbol typeSymbol, ITypeSymbol baseTypeSymbol)
+ {
+ INamedTypeSymbol? currentBaseTypeSymbol = typeSymbol.BaseType;
+
+ while (currentBaseTypeSymbol is not null)
+ {
+ if (SymbolEqualityComparer.Default.Equals(currentBaseTypeSymbol, baseTypeSymbol))
+ {
+ return true;
+ }
+
+ currentBaseTypeSymbol = currentBaseTypeSymbol.BaseType;
+ }
+
+ return false;
+ }
+}
diff --git a/src/Authoring/WinRT.SourceGenerator/Extensions/TypeSymbolExtensions.cs b/src/Authoring/WinRT.SourceGenerator/Extensions/TypeSymbolExtensions.cs
new file mode 100644
index 000000000..e07fac98b
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator/Extensions/TypeSymbolExtensions.cs
@@ -0,0 +1,90 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+// Ported from 'ITypeSymbolExtensions' in ComputeSharp (https://github.com/Sergio0694/ComputeSharp).
+// Licensed under the MIT License (MIT) (see: https://github.com/Sergio0694/ComputeSharp?tab=MIT-1-ov-file).
+// Source: https://github.com/Sergio0694/ComputeSharp/blob/main/src/ComputeSharp.SourceGeneration/Extensions/ITypeSymbolExtensions.cs.
+
+using System;
+using Microsoft.CodeAnalysis;
+
+#nullable enable
+
+namespace Generator;
+
+///
+/// Extensions for type symbols.
+///
+internal static class TypeSymbolExtensions
+{
+ ///
+ /// Thread-local writer to build metadata names.
+ ///
+ [ThreadStatic]
+ private static ArrayBufferWriter? writer;
+
+ ///
+ /// Gets the fully qualified metadata name for a given instance.
+ ///
+ /// The input instance.
+ /// The fully qualified metadata name for .
+ public static string GetFullyQualifiedMetadataName(this ITypeSymbol symbol)
+ {
+ ArrayBufferWriter writer = TypeSymbolExtensions.writer ??= [];
+
+ writer.Clear();
+
+ symbol.AppendFullyQualifiedMetadataName(writer);
+
+ return writer.WrittenSpan.ToString();
+ }
+
+ ///
+ /// Appends the fully qualified metadata name for a given symbol to a target builder.
+ ///
+ /// The input instance.
+ /// The target instance.
+ public static void AppendFullyQualifiedMetadataName(this ITypeSymbol symbol, ArrayBufferWriter builder)
+ {
+ static void BuildFrom(ISymbol? symbol, ArrayBufferWriter builder)
+ {
+ switch (symbol)
+ {
+ // Namespaces that are nested also append a leading '.'
+ case INamespaceSymbol { ContainingNamespace.IsGlobalNamespace: false }:
+ BuildFrom(symbol.ContainingNamespace, builder);
+ builder.Add('.');
+ builder.AddRange(symbol.MetadataName.AsSpan());
+ break;
+
+ // Other namespaces (ie. the one right before global) skip the leading '.'
+ case INamespaceSymbol { IsGlobalNamespace: false }:
+ builder.AddRange(symbol.MetadataName.AsSpan());
+ break;
+
+ // Types with no namespace just have their metadata name directly written
+ case ITypeSymbol { ContainingSymbol: INamespaceSymbol { IsGlobalNamespace: true } }:
+ builder.AddRange(symbol.MetadataName.AsSpan());
+ break;
+
+ // Types with a containing non-global namespace also append a leading '.'
+ case ITypeSymbol { ContainingSymbol: INamespaceSymbol namespaceSymbol }:
+ BuildFrom(namespaceSymbol, builder);
+ builder.Add('.');
+ builder.AddRange(symbol.MetadataName.AsSpan());
+ break;
+
+ // Nested types append a leading '+'
+ case ITypeSymbol { ContainingSymbol: ITypeSymbol typeSymbol }:
+ BuildFrom(typeSymbol, builder);
+ builder.Add('+');
+ builder.AddRange(symbol.MetadataName.AsSpan());
+ break;
+ default:
+ break;
+ }
+ }
+
+ BuildFrom(symbol, builder);
+ }
+}
diff --git a/src/Authoring/WinRT.SourceGenerator/Generator.cs b/src/Authoring/WinRT.SourceGenerator/Generator.cs
index 3a9da59c2..0a143971d 100644
--- a/src/Authoring/WinRT.SourceGenerator/Generator.cs
+++ b/src/Authoring/WinRT.SourceGenerator/Generator.cs
@@ -1,4 +1,4 @@
-using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Text;
@@ -6,12 +6,15 @@
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection.Metadata;
using System.Reflection.Metadata.Ecma335;
using System.Reflection.PortableExecutable;
using System.Text;
+using System.Threading;
+using WinRT.SourceGenerator;
namespace Generator
{
@@ -20,13 +23,17 @@ public class ComponentGenerator
private Logger Logger { get; }
private readonly GeneratorExecutionContext context;
private string tempFolder;
+ private readonly TypeMapper mapper;
public ComponentGenerator(GeneratorExecutionContext context)
{
this.context = context;
Logger = new Logger(context);
+ mapper = new(context.AnalyzerConfigOptions.GetCsWinRTUseWindowsUIXamlProjections());
+ // TODO-WuxMux: output a module initializer that validates the MUX/WUX projection mode to ensure that things don't get out of sync.
}
+ [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1035", Justification = "We need to do file IO to invoke the 'cswinrt' tool.")]
private string GetTempFolder(bool clearSourceFilesFromFolder = false)
{
if (string.IsNullOrEmpty(tempFolder) || !File.Exists(tempFolder))
@@ -49,6 +56,7 @@ private string GetTempFolder(bool clearSourceFilesFromFolder = false)
return tempFolder;
}
+ [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1035", Justification = "We need to do file IO to invoke the 'cswinrt' tool.")]
private void GenerateSources()
{
string cswinrtExe = context.GetCsWinRTExe();
@@ -57,14 +65,18 @@ private void GenerateSources()
string outputDir = GetTempFolder(true);
string windowsMetadata = context.GetCsWinRTWindowsMetadata();
string winmds = context.GetCsWinRTDependentMetadata();
+ string csWinRTExeTFM = context.GetCsWinRTExeTFM();
+ string overridedClassNameActivationFactory = context.GetCsWinRTGenerateOverridedClassNameActivationFactory() ? "-partial_factory" : "";
string arguments = string.Format(
- "-component -input \"{0}\" -input {1} -include {2} -output \"{3}\" -input {4} -verbose",
+ "-component -input \"{0}\" -input {1} -include {2} -output \"{3}\" -input {4} -target {5} {6} -verbose",
winmdFile,
windowsMetadata,
assemblyName,
outputDir,
- winmds);
+ winmds,
+ csWinRTExeTFM,
+ overridedClassNameActivationFactory);
Logger.Log("Running " + cswinrtExe + " " + arguments);
var processInfo = new ProcessStartInfo
@@ -82,12 +94,15 @@ private void GenerateSources()
{
using var cswinrtProcess = Process.Start(processInfo);
Logger.Log(cswinrtProcess.StandardOutput.ReadToEnd());
- Logger.Log(cswinrtProcess.StandardError.ReadToEnd());
+ var error = cswinrtProcess.StandardError.ReadToEnd();
+ Logger.Log(error);
cswinrtProcess.WaitForExit();
if (cswinrtProcess.ExitCode != 0)
{
- throw new Win32Exception(cswinrtProcess.ExitCode);
+ var diagnosticDescriptor = WinRTRules.SourceGeneratorFailed;
+ context.ReportDiagnostic(Diagnostic.Create(diagnosticDescriptor, null, error));
+ throw new Win32Exception(cswinrtProcess.ExitCode, error);
}
foreach (var file in Directory.GetFiles(outputDir, "*.cs", SearchOption.TopDirectoryOnly))
@@ -132,6 +147,7 @@ private bool CatchWinRTDiagnostics()
return winrtScanner.Found();
}
+ [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1035", Justification = "This method is only setting the exit code, not doing actual file IO.")]
public void Generate()
{
if (CatchWinRTDiagnostics())
@@ -152,7 +168,8 @@ public void Generate()
assembly,
version,
metadataBuilder,
- Logger);
+ Logger,
+ mapper);
WinRTSyntaxReceiver syntaxReceiver = (WinRTSyntaxReceiver)context.SyntaxReceiver;
Logger.Log("Found " + syntaxReceiver.Declarations.Count + " types");
@@ -167,6 +184,8 @@ public void Generate()
if (!context.ShouldGenerateWinMDOnly())
{
GenerateSources();
+ writer.GenerateWinRTExposedClassAttributes(context);
+ writer.GenerateOverridedClassNameActivationFactory(context);
}
}
catch (Exception e)
@@ -178,12 +197,318 @@ public void Generate()
}
Logger.Close();
Environment.ExitCode = -2;
- throw;
+ return;
}
Logger.Log("Done");
Logger.Close();
}
+
+ private static bool ShouldEmitCallToTryGetDependentActivationFactory(GeneratorExecutionContext context)
+ {
+ if (!context.AnalyzerConfigOptions.GetCsWinRTMergeReferencedActivationFactories())
+ {
+ return false;
+ }
+
+ foreach (MetadataReference metadataReference in context.Compilation.References)
+ {
+ if (context.Compilation.GetAssemblyOrModuleSymbol(metadataReference) is not IAssemblySymbol assemblySymbol)
+ {
+ continue;
+ }
+
+ // Check if the current assembly is a WinRT component (we just need one)
+ if (MergeReferencedActivationFactoriesGenerator.TryGetDependentAssemblyExportsTypeName(
+ assemblySymbol,
+ context.Compilation,
+ CancellationToken.None,
+ out _))
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ ///
+ /// Generates the native exports for a WinRT component.
+ ///
+ /// The value to use to produce source files.
+ public static void GenerateWinRTNativeExports(GeneratorExecutionContext context)
+ {
+ StringBuilder builder = new();
+
+ builder.AppendLine($$"""
+ //
+ #pragma warning disable
+
+ namespace WinRT
+ {
+ using global::System;
+ using global::System.Runtime.CompilerServices;
+ using global::System.Runtime.InteropServices;
+
+ ///
+ unsafe partial class Module
+ {
+ ///
+ /// Retrieves the activation factory from a DLL that contains activatable Windows Runtime classes.
+ ///
+ /// The class identifier that is associated with an activatable runtime class.
+ /// A pointer to the activation factory that corresponds with the class specified by .
+ /// The HRESULT for the operation.
+ ///
+ [UnmanagedCallersOnly(EntryPoint = nameof(DllGetActivationFactory), CallConvs = new[] { typeof(CallConvStdcall) })]
+ public static int DllGetActivationFactory(void* activatableClassId, void** factory)
+ {
+ {{GenerateNativeDllGetActivationFactoryImpl(context)}}
+ }
+
+ ///
+ /// Determines whether the DLL that implements this function is in use. If not, the caller can unload the DLL from memory.
+ ///
+ /// This method always returns S_FALSE.
+ ///
+ [UnmanagedCallersOnly(EntryPoint = nameof(DllCanUnloadNow), CallConvs = new[] { typeof(CallConvStdcall) })]
+ public static int DllCanUnloadNow()
+ {
+ const int S_FALSE = 1;
+
+ return S_FALSE;
+ }
+ }
+ }
+ """);
+
+ context.AddSource("NativeExports.g.cs", builder.ToString());
+
+ GenerateManagedDllGetActivationFactory(context);
+ }
+
+ private static string GenerateNativeDllGetActivationFactoryImpl(GeneratorExecutionContext context)
+ {
+ StringBuilder builder = new();
+
+ builder.AppendLine($$"""
+ const int E_INVALIDARG = unchecked((int)0x80070057);
+ const int CLASS_E_CLASSNOTAVAILABLE = unchecked((int)(0x80040111));
+ const int S_OK = 0;
+
+ if (activatableClassId is null || factory is null)
+ {
+ return E_INVALIDARG;
+ }
+
+ try
+ {
+ scoped ReadOnlySpan fullyQualifiedTypeName = MarshalString.FromAbiUnsafe((IntPtr)activatableClassId);
+
+ """);
+
+ // There's four possible cases for this method:
+ // 1a) The project is a standalone WinRT component
+ // 1b) The project is a WinRT component, and we're chaining other WinRT components
+ // 2) The project is an app/library, but we also want to chain other WinRT components
+ // 4) There's no possible types to activate (ie. no authored ones, no dependent ones)
+ if (context.IsCsWinRTComponent())
+ {
+ builder.AppendLine("""
+ IntPtr obj = global::WinRT.Module.GetActivationFactory(fullyQualifiedTypeName);
+ """);
+ builder.AppendLine();
+
+ // Only emit this call if we have actually generated that. We want to avoid generating
+ // that default implementation in every single assembly the generator runs on.
+ if (ShouldEmitCallToTryGetDependentActivationFactory(context))
+ {
+ builder.AppendLine("""
+ if ((void*)obj is null)
+ {
+ obj = global::WinRT.Module.TryGetDependentActivationFactory(fullyQualifiedTypeName);
+ }
+
+ """);
+ }
+ }
+ else if (ShouldEmitCallToTryGetDependentActivationFactory(context))
+ {
+ builder.AppendLine("""
+ IntPtr obj = global::WinRT.Module.TryGetDependentActivationFactory(fullyQualifiedTypeName);
+ """);
+ builder.AppendLine();
+ }
+ else
+ {
+ // This ensures we don't get build errors if someone sets 'CsWinRTMergeReferencedActivationFactories'
+ // but there's no referenced activation factories defined yet (ie. no WinRT components referenced).
+ builder.AppendLine("""
+ IntPtr obj = IntPtr.Zero;
+ """);
+ builder.AppendLine();
+ }
+
+ builder.Append("""
+ if ((void*)obj is null)
+ {
+ *factory = null;
+
+ return CLASS_E_CLASSNOTAVAILABLE;
+ }
+
+ *factory = (void*)obj;
+
+ return S_OK;
+ }
+ catch (Exception e)
+ {
+ ExceptionHelpers.SetErrorInfo(e);
+
+ return ExceptionHelpers.GetHRForException(e);
+ }
+ """);
+
+ return builder.ToString();
+ }
+
+ ///
+ /// Generates the module activation factory for scenarios where we are merging activation factories
+ /// but it isn't a cswinrt component.
+ ///
+ /// The value to use to produce source files.
+ public static void GenerateModuleActivationFactoryForMergedActivationFactories(GeneratorExecutionContext context)
+ {
+ StringBuilder builder = new();
+
+ builder.AppendLine("""
+ //
+ #pragma warning disable
+
+ namespace WinRT
+ {
+ using global::System;
+
+ ///
+ unsafe partial class Module
+ {
+ public static unsafe IntPtr GetActivationFactory(ReadOnlySpan runtimeClassId)
+ {
+ """);
+
+ if (ShouldEmitCallToTryGetDependentActivationFactory(context))
+ {
+ builder.AppendLine("return TryGetDependentActivationFactory(runtimeClassId);");
+ }
+ else
+ {
+ builder.AppendLine("return IntPtr.Zero;");
+ }
+
+ builder.AppendLine("""
+ }
+
+ public static IntPtr GetActivationFactory(string runtimeClassId)
+ {
+ return GetActivationFactory(runtimeClassId.AsSpan());
+ }
+ }
+ }
+ """);
+
+ context.AddSource("WinRTModule.g.cs", builder.ToString());
+ }
+
+ ///
+ /// Generates the native exports for a WinRT component.
+ ///
+ /// The value to use to produce source files.
+ public static void GenerateWinRTExportsType(GeneratorExecutionContext context)
+ {
+ if (context.Compilation.AssemblyName is not { Length: > 0 } assemblyName)
+ {
+ return;
+ }
+
+ // If an older CsWinRT version is referenced, it is used to generate the projection.
+ // The projection generated by it won't be marked partial to generate the attribute on it
+ // and we also don't support it with the new scenarios without updating CsWinRT package
+ // So skip generating them.
+ if (GeneratorHelper.IsOldCsWinRTExe(context))
+ {
+ return;
+ }
+
+ assemblyName = GeneratorHelper.GetEscapedAssemblyName(assemblyName);
+
+ context.AddSource("ExportsType.g.cs", $$"""
+ //
+ #pragma warning disable
+
+ [assembly: global::WinRT.WinRTAssemblyExportsType(typeof(ABI.Exports.{{assemblyName}}.Module))]
+
+ namespace ABI.Exports.{{assemblyName}}
+ {
+ using global::System;
+
+ ///
+ public static partial class Module
+ {
+ ///
+ public static IntPtr GetActivationFactory(ReadOnlySpan runtimeClassId)
+ {
+ return global::WinRT.Module.GetActivationFactory(runtimeClassId);
+ }
+ }
+ }
+ """);
+ }
+
+ ///
+ /// Generates the C# version of the native DllGetActivationFactory export.
+ ///
+ /// The value to use to produce source files.
+ public static void GenerateManagedDllGetActivationFactory(GeneratorExecutionContext context)
+ {
+ if (!context.GetCsWinRTGenerateManagedDllGetActivationFactory())
+ {
+ return;
+ }
+
+ if (context.Compilation.AssemblyName is not { Length: > 0 } assemblyName)
+ {
+ return;
+ }
+
+ assemblyName = GeneratorHelper.GetEscapedAssemblyName(assemblyName);
+
+ context.AddSource("DllGetActivationFactory.g.cs", $$"""
+ //
+ #pragma warning disable
+
+ namespace ABI.Exports.{{assemblyName}}
+ {
+ using global::System;
+ using global::WinRT;
+
+ ///
+ public static partial class Module
+ {
+ ///
+ /// Retrieves the activation factory from a DLL that contains activatable Windows Runtime classes.
+ ///
+ /// The class identifier that is associated with an activatable runtime class.
+ /// A pointer to the activation factory that corresponds with the class specified by .
+ /// The HRESULT for the operation.
+ ///
+ public unsafe static int DllGetActivationFactory(void* activatableClassId, void** factory)
+ {
+ {{GenerateNativeDllGetActivationFactoryImpl(context)}}
+ }
+ }
+ }
+ """);
+ }
}
[Generator]
@@ -193,12 +518,37 @@ public void Execute(GeneratorExecutionContext context)
{
if (!context.IsCsWinRTComponent() && !context.ShouldGenerateWinMDOnly())
{
+ // Special case for app/library projects that also want to chain referenced WinRT components.
+ // This is the case for eg. a UWP app that also has some OOP background tasks.
+ if (context.GetCsWinRTMergeReferencedActivationFactories())
+ {
+ ComponentGenerator.GenerateModuleActivationFactoryForMergedActivationFactories(context);
+ if (context.ShouldGenerateWinRTNativeExports())
+ {
+ ComponentGenerator.GenerateWinRTNativeExports(context);
+ }
+
+ return;
+ }
+
System.Diagnostics.Debug.WriteLine($"Skipping component {context.GetAssemblyName()}");
return;
}
- ComponentGenerator generator = new ComponentGenerator(context);
+ ComponentGenerator generator = new(context);
generator.Generate();
+
+ // Emit the native exports for NAOT compiled WinRT components, if needed
+ if (context.IsCsWinRTComponent() && context.ShouldGenerateWinRTNativeExports())
+ {
+ ComponentGenerator.GenerateWinRTNativeExports(context);
+ }
+
+ // Also emit the unique exported 'Module' type to avoid ambiguities in some scenarios (eg. merging)
+ if (context.IsCsWinRTComponent())
+ {
+ ComponentGenerator.GenerateWinRTExportsType(context);
+ }
}
public void Initialize(GeneratorInitializationContext context)
@@ -224,6 +574,10 @@ public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
// once from the namespace declaration and once from the member's declaration
if (syntaxNode is NamespaceDeclarationSyntax @namespace)
{
+ // We only include the namespace if it has a public type as otherwise it won't
+ // be projected. For partial types, there would be one instance that we encounter
+ // which declares the accessibility and we will use that to determine the accessibility
+ // of the type for the purpose of determining whether to include the namespace.
if (HasSomePublicTypes(syntaxNode))
{
Namespaces.Add(@namespace);
@@ -233,7 +587,7 @@ public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
return;
}
- if (syntaxNode is not MemberDeclarationSyntax declaration || !IsPublic(declaration))
+ if (syntaxNode is not MemberDeclarationSyntax declaration || !IsPublicOrPartial(declaration))
{
return;
}
@@ -249,6 +603,11 @@ syntaxNode is DelegateDeclarationSyntax ||
}
private bool IsPublic(MemberDeclarationSyntax member)
+ {
+ return member.Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword));
+ }
+
+ private bool IsPublicOrPartial(MemberDeclarationSyntax member)
{
// We detect whether partial types are public using symbol information later.
return member.Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword) || m.IsKind(SyntaxKind.PartialKeyword));
diff --git a/src/Authoring/WinRT.SourceGenerator/GenericVtableInitializerStrings.cs b/src/Authoring/WinRT.SourceGenerator/GenericVtableInitializerStrings.cs
new file mode 100644
index 000000000..d2c95eaf6
--- /dev/null
+++ b/src/Authoring/WinRT.SourceGenerator/GenericVtableInitializerStrings.cs
@@ -0,0 +1,1750 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.CodeAnalysis;
+using System.Collections.Immutable;
+using System.Linq;
+using WinRT.SourceGenerator;
+
+namespace Generator
+{
+ internal static class GenericVtableInitializerStrings
+ {
+ public static string GetInstantiation(string genericInterface, EquatableArray genericParameters)
+ {
+ if (genericInterface == "System.Collections.Generic.IEnumerable`1")
+ {
+ return GetIEnumerableInstantiation(genericParameters[0].ProjectedType, genericParameters[0].AbiType);
+ }
+ else if (genericInterface == "System.Collections.Generic.IList`1")
+ {
+ return GetIListInstantiation(
+ genericParameters[0].ProjectedType,
+ genericParameters[0].AbiType,
+ genericParameters[0].TypeKind,
+ genericParameters[0].TypeFlags);
+ }
+ else if (genericInterface == "System.Collections.Generic.IReadOnlyList`1")
+ {
+ return GetIReadOnlyListInstantiation(
+ genericParameters[0].ProjectedType,
+ genericParameters[0].AbiType,
+ genericParameters[0].TypeKind,
+ genericParameters[0].TypeFlags);
+ }
+ else if (genericInterface == "System.Collections.Generic.IDictionary`2")
+ {
+ return GetIDictionaryInstantiation(
+ genericParameters[0].ProjectedType,
+ genericParameters[0].AbiType,
+ genericParameters[0].TypeKind,
+ genericParameters[1].ProjectedType,
+ genericParameters[1].AbiType,
+ genericParameters[1].TypeKind);
+ }
+ else if (genericInterface == "System.Collections.Generic.IReadOnlyDictionary`2")
+ {
+ return GetIReadOnlyDictionaryInstantiation(
+ genericParameters[0].ProjectedType,
+ genericParameters[0].AbiType,
+ genericParameters[0].TypeKind,
+ genericParameters[1].ProjectedType,
+ genericParameters[1].AbiType,
+ genericParameters[1].TypeKind);
+ }
+ else if (genericInterface == "System.Collections.Generic.IEnumerator`1")
+ {
+ return GetIEnumeratorInstantiation(
+ genericParameters[0].ProjectedType,
+ genericParameters[0].AbiType,
+ genericParameters[0].TypeKind,
+ genericParameters[0].TypeFlags);
+ }
+ else if (genericInterface == "System.Collections.Generic.KeyValuePair`2")
+ {
+ return GetKeyValuePairInstantiation(
+ genericParameters[0].ProjectedType,
+ genericParameters[0].AbiType,
+ genericParameters[0].TypeKind,
+ genericParameters[1].ProjectedType,
+ genericParameters[1].AbiType,
+ genericParameters[1].TypeKind);
+ }
+ else if (genericInterface == "System.EventHandler`1")
+ {
+ return GetEventHandlerInstantiation(genericParameters[0].ProjectedType, genericParameters[0].AbiType, genericParameters[0].TypeKind);
+ }
+ else if (genericInterface == "Windows.Foundation.AsyncActionWithProgressCompletedHandler`1")
+ {
+ return GetCompletedHandlerInstantiation("Windows.Foundation.AsyncActionWithProgressCompletedHandler", "Windows.Foundation.IAsyncActionWithProgress", genericParameters);
+ }
+ else if (genericInterface == "Windows.Foundation.AsyncOperationCompletedHandler`1")
+ {
+ return GetCompletedHandlerInstantiation("Windows.Foundation.AsyncOperationCompletedHandler", "Windows.Foundation.IAsyncOperation", genericParameters);
+ }
+ else if (genericInterface == "Windows.Foundation.AsyncOperationWithProgressCompletedHandler`2")
+ {
+ return GetCompletedHandlerInstantiation("Windows.Foundation.AsyncOperationWithProgressCompletedHandler", "Windows.Foundation.IAsyncOperationWithProgress", genericParameters);
+ }
+ else if (genericInterface == "Windows.Foundation.Collections.MapChangedEventHandler`2")
+ {
+ string senderInterface = $"global::Windows.Foundation.Collections.IObservableMap<{genericParameters[0].ProjectedType}, {genericParameters[1].ProjectedType}>";
+ string changedEventArgsInterface = $"global::Windows.Foundation.Collections.IMapChangedEventArgs<{genericParameters[0].ProjectedType}>";
+ return GetChangedHandlerInstantiation("Windows.Foundation.Collections.MapChangedEventHandler", senderInterface, changedEventArgsInterface, genericParameters);
+ }
+ else if (genericInterface == "Windows.Foundation.Collections.VectorChangedEventHandler`1")
+ {
+ string senderInterface = $"global::Windows.Foundation.Collections.IObservableVector<{genericParameters[0].ProjectedType}>";
+ string changedEventArgsInterface = $"global::Windows.Foundation.Collections.IVectorChangedEventArgs";
+ return GetChangedHandlerInstantiation("Windows.Foundation.Collections.VectorChangedEventHandler", senderInterface, changedEventArgsInterface, genericParameters);
+ }
+ else if (genericInterface == "Windows.Foundation.AsyncActionProgressHandler`1")
+ {
+ return GetProgressHandlerInstantiation("Windows.Foundation.AsyncActionProgressHandler", "Windows.Foundation.IAsyncActionWithProgress", genericParameters);
+ }
+ else if (genericInterface == "Windows.Foundation.AsyncOperationProgressHandler`2")
+ {
+ return GetProgressHandlerInstantiation("Windows.Foundation.AsyncOperationProgressHandler", "Windows.Foundation.IAsyncOperationWithProgress", genericParameters);
+ }
+ else if (genericInterface == "Windows.Foundation.TypedEventHandler`2")
+ {
+ return GetTypedEventHandlerInstantiation(genericParameters);
+ }
+ else if (genericInterface == "Windows.Foundation.IAsyncActionWithProgress`1")
+ {
+ return GetIAsyncActionWithProgressInstantiation(genericParameters);
+ }
+ else if (genericInterface == "Windows.Foundation.IAsyncOperationWithProgress`2")
+ {
+ return GetIAsyncOperationWithProgressInstantiation(genericParameters);
+ }
+ else if (genericInterface == "Windows.Foundation.IAsyncOperation`1")
+ {
+ return GetIAsyncOperationInstantiation(genericParameters);
+ }
+ else if (genericInterface == "Windows.Foundation.Collections.IMapChangedEventArgs`1")
+ {
+ return GetIMapChangedEventArgsInstantiation(genericParameters);
+ }
+ else if (genericInterface == "Windows.Foundation.Collections.IObservableMap`2")
+ {
+ return GetIObservableMapInstantiation(genericParameters);
+ }
+ else if (genericInterface == "Windows.Foundation.Collections.IObservableVector`1")
+ {
+ return GetIObservableVectorInstantiation(genericParameters);
+ }
+
+ return "";
+ }
+
+ public static ImmutableHashSet AddDependentGenericInterfaces(ImmutableHashSet genericInterfaces)
+ {
+ var genericInterfacesList = genericInterfaces.ToList();
+
+ foreach (var genericInterface in genericInterfaces)
+ {
+ if (genericInterface.GenericDefinition == "Windows.Foundation.IAsyncActionWithProgress`1")
+ {
+ AddReplacedGenericInterface(genericInterface, "Windows.Foundation.AsyncActionProgressHandler");
+ AddReplacedGenericInterface(genericInterface, "Windows.Foundation.AsyncActionWithProgressCompletedHandler");
+ }
+ else if (genericInterface.GenericDefinition == "Windows.Foundation.IAsyncOperationWithProgress`2")
+ {
+ AddReplacedGenericInterface(genericInterface, "Windows.Foundation.AsyncOperationProgressHandler");
+ AddReplacedGenericInterface(genericInterface, "Windows.Foundation.AsyncOperationWithProgressCompletedHandler");
+ }
+ else if (genericInterface.GenericDefinition == "Windows.Foundation.IAsyncOperation`1")
+ {
+ AddReplacedGenericInterface(genericInterface, "Windows.Foundation.AsyncOperationCompletedHandler");
+ }
+ else if (genericInterface.GenericDefinition == "Windows.Foundation.Collections.IObservableMap`2")
+ {
+ AddReplacedGenericInterface(genericInterface, "Windows.Foundation.Collections.MapChangedEventHandler");
+ }
+ else if (genericInterface.GenericDefinition == "Windows.Foundation.Collections.IObservableVector`1")
+ {
+ AddReplacedGenericInterface(genericInterface, "Windows.Foundation.Collections.VectorChangedEventHandler");
+ }
+ }
+
+ return genericInterfacesList.ToImmutableHashSet();
+
+ void AddReplacedGenericInterface(GenericInterface genericInterfaceToReplace, string newInterfaceToReplaceWith)
+ {
+ // Replace the old interface name with the new one.
+ // We know this is an generic interface, so we want to replace the string before the generics (<).
+ string newQualifiedInterfaceName = newInterfaceToReplaceWith + genericInterfaceToReplace.Interface[genericInterfaceToReplace.Interface.IndexOf('<')..];
+
+ // The metadata name includes the number of generics which we get from the last character of the one we are replacing
+ // since they are the same in the scenarios we care about.
+ string newInterfaceMetadataName = newInterfaceToReplaceWith + "`" + genericInterfaceToReplace.GenericDefinition[^1];
+
+ genericInterfacesList.Add(new GenericInterface(
+ newQualifiedInterfaceName,
+ newInterfaceMetadataName,
+ genericInterfaceToReplace.GenericParameters
+ ));
+ }
+ }
+
+ public static string GetInstantiationInitFunction(string genericInterface, EquatableArray genericParameters, string escapedAssemblyName)
+ {
+ // Get the class name from a string like System.Collections.Generic.IEnumerator`1.
+ // Splitting on the dots and the generic specifier (`) will get us the class name
+ // in the 2nd last element.
+ var interfaceName = genericInterface.Split('.', '`')[^2];
+ var genericParametersStr = string.Join("_", genericParameters.Select(static genericParameter => GeneratorHelper.EscapeTypeNameForIdentifier(genericParameter.ProjectedType)));
+ return $$""" _ = global::WinRT.{{escapedAssemblyName}}GenericHelpers.{{interfaceName}}_{{genericParametersStr}}.Initialized;""";
+ }
+
+ private static string GetIEnumerableInstantiation(string genericType, string abiType)
+ {
+ string iEnumerableInstantiation = $$"""
+ internal static class IEnumerable_{{GeneratorHelper.EscapeTypeNameForIdentifier(genericType)}}
+ {
+ private static readonly bool _initialized = Init();
+ internal static bool Initialized => _initialized;
+
+ private static unsafe bool Init()
+ {
+ return global::ABI.System.Collections.Generic.IEnumerableMethods<{{genericType}}, {{abiType}}>.InitCcw(
+ &Do_Abi_First_0
+ );
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_First_0(IntPtr thisPtr, IntPtr* __return_value__)
+ {
+ *__return_value__ = default;
+ try
+ {
+ *__return_value__ = MarshalInterface>.
+ FromManaged(global::ABI.System.Collections.Generic.IEnumerableMethods<{{genericType}}>.Abi_First_0(thisPtr));
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+ }
+ """;
+ return iEnumerableInstantiation;
+ }
+
+ private static string GetIEnumeratorInstantiation(string genericType, string abiType, TypeKind typeKind, TypeFlags typeFlags)
+ {
+ string iEnumeratorInstantiation = $$"""
+ internal static class IEnumerator_{{GeneratorHelper.EscapeTypeNameForIdentifier(genericType)}}
+ {
+ private static readonly bool _initialized = Init();
+ internal static bool Initialized => _initialized;
+
+ private static unsafe bool Init()
+ {
+ return global::ABI.System.Collections.Generic.IEnumeratorMethods<{{genericType}}, {{abiType}}>.InitCcw(
+ &Do_Abi_get_Current_0,
+ &Do_Abi_get_HasCurrent_1,
+ &Do_Abi_MoveNext_2,
+ &Do_Abi_GetMany_3
+ );
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_MoveNext_2(IntPtr thisPtr, byte* __return_value__)
+ {
+ bool ____return_value__ = default;
+
+ *__return_value__ = default;
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IEnumeratorMethods<{{genericType}}>.Abi_MoveNext_2(thisPtr);
+ *__return_value__ = (byte)(____return_value__ ? 1 : 0);
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_GetMany_3(IntPtr thisPtr, int __itemsSize, IntPtr items, uint* __return_value__)
+ {
+ uint ____return_value__ = default;
+
+ *__return_value__ = default;
+ {{genericType}}[] __items = {{GeneratorHelper.GetMarshalerClass(genericType, abiType, typeKind, true)}}.FromAbiArray((__itemsSize, items));
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IEnumeratorMethods<{{genericType}}>.Abi_GetMany_3(thisPtr, ref __items);
+ {{GeneratorHelper.GetCopyManagedArrayMarshaler(genericType, abiType, typeKind, typeFlags)}}.CopyManagedArray(__items, items);
+ *__return_value__ = ____return_value__;
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_get_Current_0(IntPtr thisPtr, {{abiType}}* __return_value__)
+ {
+ {{genericType}} ____return_value__ = default;
+
+ *__return_value__ = default;
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IEnumeratorMethods<{{genericType}}>.Abi_get_Current_0(thisPtr);
+ *__return_value__ = {{GeneratorHelper.GetFromManagedMarshaler(genericType, abiType, typeKind, "____return_value__")}};
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_get_HasCurrent_1(IntPtr thisPtr, byte* __return_value__)
+ {
+ bool ____return_value__ = default;
+
+ *__return_value__ = default;
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IEnumeratorMethods<{{genericType}}>.Abi_get_HasCurrent_1(thisPtr);
+ *__return_value__ = (byte)(____return_value__ ? 1 : 0);
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+ }
+ """;
+ return iEnumeratorInstantiation;
+ }
+
+ private static string GetIListInstantiation(string genericType, string abiType, TypeKind typeKind, TypeFlags typeFlags)
+ {
+ string iListInstantiation = $$"""
+ internal static class IList_{{GeneratorHelper.EscapeTypeNameForIdentifier(genericType)}}
+ {
+ private static readonly bool _initialized = Init();
+ internal static bool Initialized => _initialized;
+
+ private static unsafe bool Init()
+ {
+ return global::ABI.System.Collections.Generic.IListMethods<{{genericType}}, {{abiType}}>.InitCcw(
+ &Do_Abi_GetAt_0,
+ &Do_Abi_get_Size_1,
+ &Do_Abi_GetView_2,
+ &Do_Abi_IndexOf_3,
+ &Do_Abi_SetAt_4,
+ &Do_Abi_InsertAt_5,
+ &Do_Abi_RemoveAt_6,
+ &Do_Abi_Append_7,
+ &Do_Abi_RemoveAtEnd_8,
+ &Do_Abi_Clear_9,
+ &Do_Abi_GetMany_10,
+ &Do_Abi_ReplaceAll_11
+ );
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_GetAt_0(IntPtr thisPtr, uint index, {{abiType}}* __return_value__)
+ {
+ {{genericType}} ____return_value__ = default;
+ *__return_value__ = default;
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IListMethods<{{genericType}}>.Abi_GetAt_0(thisPtr, index);
+ *__return_value__ = {{GeneratorHelper.GetFromManagedMarshaler(genericType, abiType, typeKind, "____return_value__")}};
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_GetView_2(IntPtr thisPtr, IntPtr* __return_value__)
+ {
+ global::System.Collections.Generic.IReadOnlyList<{{genericType}}> ____return_value__ = default;
+ *__return_value__ = default;
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IListMethods<{{genericType}}>.Abi_GetView_2(thisPtr);
+ *__return_value__ = MarshalInterface>.FromManaged(____return_value__);
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_IndexOf_3(IntPtr thisPtr, {{abiType}} value, uint* index, byte* __return_value__)
+ {
+ bool ____return_value__ = default;
+
+ *index = default;
+ *__return_value__ = default;
+ uint __index = default;
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IListMethods<{{genericType}}>.Abi_IndexOf_3(thisPtr, {{GeneratorHelper.GetFromAbiMarshaler(genericType, abiType, typeKind, "value")}}, out __index);
+ *index = __index;
+ *__return_value__ = (byte)(____return_value__ ? 1 : 0);
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_SetAt_4(IntPtr thisPtr, uint index, {{abiType}} value)
+ {
+ try
+ {
+ global::ABI.System.Collections.Generic.IListMethods<{{genericType}}>.Abi_SetAt_4(thisPtr, index, {{GeneratorHelper.GetFromAbiMarshaler(genericType, abiType, typeKind, "value")}});
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_InsertAt_5(IntPtr thisPtr, uint index, {{abiType}} value)
+ {
+ try
+ {
+ global::ABI.System.Collections.Generic.IListMethods<{{genericType}}>.Abi_InsertAt_5(thisPtr, index, {{GeneratorHelper.GetFromAbiMarshaler(genericType, abiType, typeKind, "value")}});
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_RemoveAt_6(IntPtr thisPtr, uint index)
+ {
+ try
+ {
+ global::ABI.System.Collections.Generic.IListMethods<{{genericType}}>.Abi_RemoveAt_6(thisPtr, index);
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_Append_7(IntPtr thisPtr, {{abiType}} value)
+ {
+ try
+ {
+ global::ABI.System.Collections.Generic.IListMethods<{{genericType}}>.Abi_Append_7(thisPtr, {{GeneratorHelper.GetFromAbiMarshaler(genericType, abiType, typeKind, "value")}});
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_RemoveAtEnd_8(IntPtr thisPtr)
+ {
+ try
+ {
+ global::ABI.System.Collections.Generic.IListMethods<{{genericType}}>.Abi_RemoveAtEnd_8(thisPtr);
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_Clear_9(IntPtr thisPtr)
+ {
+ try
+ {
+ global::ABI.System.Collections.Generic.IListMethods<{{genericType}}>.Abi_Clear_9(thisPtr);
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_GetMany_10(IntPtr thisPtr, uint startIndex, int __itemsSize, IntPtr items, uint* __return_value__)
+ {
+ uint ____return_value__ = default;
+
+ *__return_value__ = default;
+ {{genericType}}[] __items = {{GeneratorHelper.GetMarshalerClass(genericType, abiType, typeKind, true)}}.FromAbiArray((__itemsSize, items));
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IListMethods<{{genericType}}>.Abi_GetMany_10(thisPtr, startIndex, ref __items);
+ {{GeneratorHelper.GetCopyManagedArrayMarshaler(genericType, abiType, typeKind, typeFlags)}}.CopyManagedArray(__items, items);
+ *__return_value__ = ____return_value__;
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_ReplaceAll_11(IntPtr thisPtr, int __itemsSize, IntPtr items)
+ {
+ try
+ {
+ global::ABI.System.Collections.Generic.IListMethods<{{genericType}}>.Abi_ReplaceAll_11(thisPtr, {{GeneratorHelper.GetMarshalerClass(genericType, abiType, typeKind, true)}}.FromAbiArray((__itemsSize, items)));
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_get_Size_1(IntPtr thisPtr, uint* __return_value__)
+ {
+ uint ____return_value__ = default;
+
+ *__return_value__ = default;
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IListMethods<{{genericType}}>.Abi_get_Size_1(thisPtr);
+ *__return_value__ = ____return_value__;
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+ }
+ """;
+ return iListInstantiation;
+ }
+
+ private static string GetIReadOnlyListInstantiation(string genericType, string abiType, TypeKind typeKind, TypeFlags typeFlags)
+ {
+ string iReadOnlylistInstantiation = $$"""
+ internal static class IReadOnlyList_{{GeneratorHelper.EscapeTypeNameForIdentifier(genericType)}}
+ {
+ private static readonly bool _initialized = Init();
+ internal static bool Initialized => _initialized;
+
+ private static unsafe bool Init()
+ {
+ return global::ABI.System.Collections.Generic.IReadOnlyListMethods<{{genericType}}, {{abiType}}>.InitCcw(
+ &Do_Abi_GetAt_0,
+ &Do_Abi_get_Size_1,
+ &Do_Abi_IndexOf_2,
+ &Do_Abi_GetMany_3
+ );
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_GetAt_0(IntPtr thisPtr, uint index, {{abiType}}* __return_value__)
+ {
+ {{genericType}} ____return_value__ = default;
+ *__return_value__ = default;
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IReadOnlyListMethods<{{genericType}}>.Abi_GetAt_0(thisPtr, index);
+ *__return_value__ = {{GeneratorHelper.GetFromManagedMarshaler(genericType, abiType, typeKind, "____return_value__")}};
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_IndexOf_2(IntPtr thisPtr, {{abiType}} value, uint* index, byte* __return_value__)
+ {
+ bool ____return_value__ = default;
+
+ *index = default;
+ *__return_value__ = default;
+ uint __index = default;
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IReadOnlyListMethods<{{genericType}}>.Abi_IndexOf_2(thisPtr, {{GeneratorHelper.GetFromAbiMarshaler(genericType, abiType, typeKind, "value")}}, out __index);
+ *index = __index;
+ *__return_value__ = (byte)(____return_value__ ? 1 : 0);
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_GetMany_3(IntPtr thisPtr, uint startIndex, int __itemsSize, IntPtr items, uint* __return_value__)
+ {
+ uint ____return_value__ = default;
+
+ *__return_value__ = default;
+ {{genericType}}[] __items = {{GeneratorHelper.GetMarshalerClass(genericType, abiType, typeKind, true)}}.FromAbiArray((__itemsSize, items));
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IReadOnlyListMethods<{{genericType}}>.Abi_GetMany_3(thisPtr, startIndex, ref __items);
+ {{GeneratorHelper.GetCopyManagedArrayMarshaler(genericType, abiType, typeKind, typeFlags)}}.CopyManagedArray(__items, items);
+ *__return_value__ = ____return_value__;
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_get_Size_1(IntPtr thisPtr, uint* __return_value__)
+ {
+ uint ____return_value__ = default;
+
+ *__return_value__ = default;
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IReadOnlyListMethods<{{genericType}}>.Abi_get_Size_1(thisPtr);
+ *__return_value__ = ____return_value__;
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+ }
+ """;
+ return iReadOnlylistInstantiation;
+ }
+
+ private static string GetIDictionaryInstantiation(string genericKeyType, string abiKeyType, TypeKind keyTypeKind, string genericValueType, string abiValueType, TypeKind valueTypeKind)
+ {
+ string iDictionaryInstantiation = $$"""
+ internal static class IDictionary_{{GeneratorHelper.EscapeTypeNameForIdentifier(genericKeyType)}}_{{GeneratorHelper.EscapeTypeNameForIdentifier(genericValueType)}}
+ {
+ private static readonly bool _initialized = Init();
+ internal static bool Initialized => _initialized;
+
+ private static unsafe bool Init()
+ {
+ return global::ABI.System.Collections.Generic.IDictionaryMethods<{{genericKeyType}}, {{abiKeyType}}, {{genericValueType}}, {{abiValueType}}>.InitCcw(
+ &Do_Abi_Lookup_0,
+ &Do_Abi_get_Size_1,
+ &Do_Abi_HasKey_2,
+ &Do_Abi_GetView_3,
+ &Do_Abi_Insert_4,
+ &Do_Abi_Remove_5,
+ &Do_Abi_Clear_6
+ );
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_Lookup_0(IntPtr thisPtr, {{abiKeyType}} key, {{abiValueType}}* __return_value__)
+ {
+ {{genericValueType}} ____return_value__ = default;
+
+ *__return_value__ = default;
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IDictionaryMethods<{{genericKeyType}}, {{genericValueType}}>.Abi_Lookup_0(thisPtr, {{GeneratorHelper.GetFromAbiMarshaler(genericKeyType, abiKeyType, keyTypeKind, "key")}});
+ *__return_value__ = {{GeneratorHelper.GetFromManagedMarshaler(genericValueType, abiValueType, valueTypeKind, "____return_value__")}};
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_HasKey_2(IntPtr thisPtr, {{abiKeyType}} key, byte* __return_value__)
+ {
+ bool ____return_value__ = default;
+
+ *__return_value__ = default;
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IDictionaryMethods<{{genericKeyType}}, {{genericValueType}}>.Abi_HasKey_2(thisPtr, {{GeneratorHelper.GetFromAbiMarshaler(genericKeyType, abiKeyType, keyTypeKind, "key")}});
+ *__return_value__ = (byte)(____return_value__ ? 1 : 0);
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_GetView_3(IntPtr thisPtr, IntPtr* __return_value__)
+ {
+ global::System.Collections.Generic.IReadOnlyDictionary<{{genericKeyType}}, {{genericValueType}}> ____return_value__ = default;
+
+ *__return_value__ = default;
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IDictionaryMethods<{{genericKeyType}}, {{genericValueType}}>.Abi_GetView_3(thisPtr);
+ *__return_value__ = MarshalInterface>.FromManaged(____return_value__);
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_Insert_4(IntPtr thisPtr, {{abiKeyType}} key, {{abiValueType}} value, byte* __return_value__)
+ {
+ bool ____return_value__ = default;
+
+ *__return_value__ = default;
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IDictionaryMethods<{{genericKeyType}}, {{genericValueType}}>.Abi_Insert_4(thisPtr, {{GeneratorHelper.GetFromAbiMarshaler(genericKeyType, abiKeyType, keyTypeKind, "key")}}, {{GeneratorHelper.GetFromAbiMarshaler(genericValueType, abiValueType, valueTypeKind, "value")}});
+ *__return_value__ = (byte)(____return_value__ ? 1 : 0);
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_Remove_5(IntPtr thisPtr, {{abiKeyType}} key)
+ {
+ try
+ {
+ global::ABI.System.Collections.Generic.IDictionaryMethods<{{genericKeyType}}, {{genericValueType}}>.Abi_Remove_5(thisPtr, {{GeneratorHelper.GetFromAbiMarshaler(genericKeyType, abiKeyType, keyTypeKind, "key")}});
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_Clear_6(IntPtr thisPtr)
+ {
+ try
+ {
+ global::ABI.System.Collections.Generic.IDictionaryMethods<{{genericKeyType}}, {{genericValueType}}>.Abi_Clear_6(thisPtr);
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_get_Size_1(IntPtr thisPtr, uint* __return_value__)
+ {
+ uint ____return_value__ = default;
+
+ *__return_value__ = default;
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IDictionaryMethods<{{genericKeyType}}, {{genericValueType}}>.Abi_get_Size_1(thisPtr);
+ *__return_value__ = ____return_value__;
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+ }
+ """;
+ return iDictionaryInstantiation;
+ }
+
+ private static string GetIReadOnlyDictionaryInstantiation(string genericKeyType, string abiKeyType, TypeKind keyTypeKind, string genericValueType, string abiValueType, TypeKind valueTypeKind)
+ {
+ string iReadOnlyDictionaryInstantiation = $$"""
+ internal static class IReadOnlyDictionary_{{GeneratorHelper.EscapeTypeNameForIdentifier(genericKeyType)}}_{{GeneratorHelper.EscapeTypeNameForIdentifier(genericValueType)}}
+ {
+ private static readonly bool _initialized = Init();
+ internal static bool Initialized => _initialized;
+
+ private static unsafe bool Init()
+ {
+ return global::ABI.System.Collections.Generic.IReadOnlyDictionaryMethods<{{genericKeyType}}, {{abiKeyType}}, {{genericValueType}}, {{abiValueType}}>.InitCcw(
+ &Do_Abi_Lookup_0,
+ &Do_Abi_get_Size_1,
+ &Do_Abi_HasKey_2,
+ &Do_Abi_Split_3
+ );
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_Lookup_0(IntPtr thisPtr, {{abiKeyType}} key, {{abiValueType}}* __return_value__)
+ {
+ {{genericValueType}} ____return_value__ = default;
+
+ *__return_value__ = default;
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IReadOnlyDictionaryMethods<{{genericKeyType}}, {{genericValueType}}>.Abi_Lookup_0(thisPtr, {{GeneratorHelper.GetFromAbiMarshaler(genericKeyType, abiKeyType, keyTypeKind, "key")}});
+ *__return_value__ = {{GeneratorHelper.GetFromManagedMarshaler(genericValueType, abiValueType, valueTypeKind, "____return_value__")}};
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_HasKey_2(IntPtr thisPtr, {{abiKeyType}} key, byte* __return_value__)
+ {
+ bool ____return_value__ = default;
+
+ *__return_value__ = default;
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IReadOnlyDictionaryMethods<{{genericKeyType}}, {{genericValueType}}>.Abi_HasKey_2(thisPtr, {{GeneratorHelper.GetFromAbiMarshaler(genericKeyType, abiKeyType, keyTypeKind, "key")}});
+ *__return_value__ = (byte)(____return_value__ ? 1 : 0);
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_Split_3(IntPtr thisPtr, IntPtr* first, IntPtr* second)
+ {
+ *first = default;
+ *second = default;
+ IntPtr __first = default;
+ IntPtr __second = default;
+
+ try
+ {
+ global::ABI.System.Collections.Generic.IReadOnlyDictionaryMethods<{{genericKeyType}}, {{genericValueType}}>.Abi_Split_3(thisPtr, out __first, out __second);
+ *first = __first;
+ *second = __second;
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_get_Size_1(IntPtr thisPtr, uint* __return_value__)
+ {
+ uint ____return_value__ = default;
+
+ *__return_value__ = default;
+
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.IReadOnlyDictionaryMethods<{{genericKeyType}}, {{genericValueType}}>.Abi_get_Size_1(thisPtr);
+ *__return_value__ = ____return_value__;
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+ }
+ """;
+ return iReadOnlyDictionaryInstantiation;
+ }
+
+ private static string GetKeyValuePairInstantiation(string genericKeyType, string abiKeyType, TypeKind keyTypeKind, string genericValueType, string abiValueType, TypeKind valueTypeKind)
+ {
+ string keyValuePairInstantiation = $$"""
+ internal static class KeyValuePair_{{GeneratorHelper.EscapeTypeNameForIdentifier(genericKeyType)}}_{{GeneratorHelper.EscapeTypeNameForIdentifier(genericValueType)}}
+ {
+ private static readonly bool _initialized = Init();
+ internal static bool Initialized => _initialized;
+
+ private static unsafe bool Init()
+ {
+ return global::ABI.System.Collections.Generic.KeyValuePairMethods<{{genericKeyType}}, {{abiKeyType}}, {{genericValueType}}, {{abiValueType}}>.InitCcw(
+ &Do_Abi_get_Key_0,
+ &Do_Abi_get_Value_1
+ );
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_get_Key_0(IntPtr thisPtr, {{abiKeyType}}* __return_value__)
+ {
+ {{genericKeyType}} ____return_value__ = default;
+ *__return_value__ = default;
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.KeyValuePairMethods<{{genericKeyType}}, {{genericValueType}}>.Abi_get_Key_0(thisPtr);
+ *__return_value__ = {{GeneratorHelper.GetFromManagedMarshaler(genericKeyType, abiKeyType, keyTypeKind, "____return_value__")}};
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_get_Value_1(IntPtr thisPtr, {{abiValueType}}* __return_value__)
+ {
+ {{genericValueType}} ____return_value__ = default;
+ *__return_value__ = default;
+ try
+ {
+ ____return_value__ = global::ABI.System.Collections.Generic.KeyValuePairMethods<{{genericKeyType}}, {{genericValueType}}>.Abi_get_Value_1(thisPtr);
+ *__return_value__ = {{GeneratorHelper.GetFromManagedMarshaler(genericValueType, abiValueType, valueTypeKind, "____return_value__")}};
+ }
+ catch (global::System.Exception __exception__)
+ {
+ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__);
+ return global::WinRT.ExceptionHelpers.GetHRForException(__exception__);
+ }
+ return 0;
+ }
+ }
+ """;
+ return keyValuePairInstantiation;
+ }
+
+ // Delegates are special and initialize both the RCW and CCW.
+ private static string GetEventHandlerInstantiation(string genericType, string abiType, TypeKind typeKind)
+ {
+ string eventHandlerInstantiation = $$"""
+ internal static class EventHandler_{{GeneratorHelper.EscapeTypeNameForIdentifier(genericType)}}
+ {
+ private static readonly bool _initialized = Init();
+ internal static bool Initialized => _initialized;
+
+ private static unsafe bool Init()
+ {
+ _ = global::ABI.System.EventHandlerMethods<{{genericType}}, {{abiType}}>.InitCcw(
+ &Do_Abi_Invoke
+ );
+ _ = global::ABI.System.EventHandlerMethods<{{genericType}}, {{abiType}}>.InitRcwHelper(
+ &Invoke
+ );
+ return true;
+ }
+
+ [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]
+ private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr sender, {{abiType}} args)
+ {
+ try
+ {
+ global::ABI.System.EventHandlerMethods<{{genericType}}, {{abiType}}>.Abi_Invoke(thisPtr, MarshalInspectable