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 @@ -[![Build Status](https://dev.azure.com/microsoft/Dart/_apis/build/status/CsWinRT%20CI?branchName=master)](https://dev.azure.com/microsoft/Dart/_build/latest?definitionId=76961&branchName=master) +[![Build Status](https://dev.azure.com/microsoft/Dart/_apis/build/status%2FCsWinRT%20GitHub%20CI?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.FromAbi(sender), {{GeneratorHelper.GetFromAbiMarshaler(genericType, abiType, typeKind, "args")}}); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe void Invoke(IObjectReference objRef, object sender, {{genericType}} args) + { + IntPtr ThisPtr = objRef.ThisPtr; + ObjectReferenceValue __sender = default; + {{GeneratorHelper.GetMarshalerDeclaration(genericType, abiType, typeKind, "args")}} + try + { + __sender = MarshalInspectable.CreateMarshaler2(sender); + IntPtr abiSender = MarshalInspectable.GetAbi(__sender); + {{GeneratorHelper.GetCreateMarshaler(genericType, abiType, typeKind, "args")}} + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3](ThisPtr, abiSender, {{GeneratorHelper.GetAbiFromMarshaler(genericType, abiType, typeKind, "args")}})); + global::System.GC.KeepAlive(objRef); + } + finally + { + MarshalInspectable.DisposeMarshaler(__sender); + {{GeneratorHelper.GetDisposeMarshaler(genericType, abiType, typeKind, "args")}} + } + } + } + """; + return eventHandlerInstantiation; + } + + private static string GetTypedEventHandlerInstantiation(EquatableArray genericParameters) + { + string staticMethodsClass = $"global::ABI.Windows.Foundation.TypedEventHandlerMethods<{GetGenericParametersAsString(genericParameters, ", ", true, false)}>"; + string typedEventHandlerInstantiation = $$""" + internal static class TypedEventHandler_{{GetGenericParametersAsString(genericParameters, "_", false)}} + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + _ = {{staticMethodsClass}}.InitCcw( + &Do_Abi_Invoke + ); + _ = {{staticMethodsClass}}.InitRcwHelper( + &Invoke + ); + return true; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, {{genericParameters[0].AbiType}} sender, {{genericParameters[1].AbiType}} args) + { + try + { + {{staticMethodsClass}}.Abi_Invoke(thisPtr, {{GeneratorHelper.GetFromAbiMarshaler(genericParameters[0], "sender")}}, {{GeneratorHelper.GetFromAbiMarshaler(genericParameters[1], "args")}}); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe void Invoke(IObjectReference objRef, {{genericParameters[0].ProjectedType}} sender, {{genericParameters[1].ProjectedType}} args) + { + IntPtr ThisPtr = objRef.ThisPtr; + {{GeneratorHelper.GetMarshalerDeclaration(genericParameters[0], "sender")}} + {{GeneratorHelper.GetMarshalerDeclaration(genericParameters[1], "args")}} + try + { + {{GeneratorHelper.GetCreateMarshaler(genericParameters[0], "sender")}} + {{GeneratorHelper.GetCreateMarshaler(genericParameters[1], "args")}} + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3](ThisPtr, {{GeneratorHelper.GetAbiFromMarshaler(genericParameters[0], "sender")}}, {{GeneratorHelper.GetAbiFromMarshaler(genericParameters[1], "args")}})); + global::System.GC.KeepAlive(objRef); + } + finally + { + {{GeneratorHelper.GetDisposeMarshaler(genericParameters[0], "sender")}} + {{GeneratorHelper.GetDisposeMarshaler(genericParameters[1], "args")}} + } + } + } + """; + return typedEventHandlerInstantiation; + } + + private static string GetCompletedHandlerInstantiation(string completedHandler, string asyncInfoInterface, EquatableArray genericParameters) + { + string staticMethodsClass = $"global::ABI.{completedHandler}Methods<{GetGenericParametersAsString(genericParameters, ", ", true, false)}>"; + string interfaceWithGeneric = $"global::{asyncInfoInterface}<{GetGenericParametersAsString(genericParameters, ", ", false, false)}>"; + string completedHandlerInstantiation = $$""" + internal static class {{completedHandler.Split('.')[^1]}}_{{GetGenericParametersAsString(genericParameters, "_", false)}} + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + return {{staticMethodsClass}}.InitCcw( + &Do_Abi_Invoke + ); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr asyncInfo, global::Windows.Foundation.AsyncStatus asyncStatus) + { + try + { + {{staticMethodsClass}}.Abi_Invoke(thisPtr, MarshalInterface<{{interfaceWithGeneric}}>.FromAbi(asyncInfo), asyncStatus); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + """; + return completedHandlerInstantiation; + } + + private static string GetChangedHandlerInstantiation(string changedHandler, string senderInterface, string changedEventArgsInterface, EquatableArray genericParameters) + { + string staticMethodsClass = $"global::ABI.{changedHandler}Methods<{GetGenericParametersAsString(genericParameters, ", ", true, false)}>"; + string changedHandlerInstantiation = $$""" + internal static class {{changedHandler.Split('.')[^1]}}_{{GetGenericParametersAsString(genericParameters, "_", false)}} + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + return {{staticMethodsClass}}.InitCcw( + &Do_Abi_Invoke + ); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr sender, IntPtr @event) + { + try + { + {{staticMethodsClass}}.Abi_Invoke(thisPtr, MarshalInterface<{{senderInterface}}>.FromAbi(sender), MarshalInterface<{{changedEventArgsInterface}}>.FromAbi(@event)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + """; + return changedHandlerInstantiation; + } + + private static string GetProgressHandlerInstantiation(string progressHandler, string asyncInfoInterface, EquatableArray genericParameters) + { + string staticMethodsClass = $"global::ABI.{progressHandler}Methods<{GetGenericParametersAsString(genericParameters, ", ", true, false)}>"; + string asyncInfoInterfaceWithGeneric = $"global::{asyncInfoInterface}<{GetGenericParametersAsString(genericParameters, ", ", false, false)}>"; + string asyncInfoInterfaceWithGenericMethodsClass = $"global::ABI.{asyncInfoInterface}Methods<{GetGenericParametersAsString(genericParameters, ", ", false, false)}>"; + var progressParameter = genericParameters.Last(); + string progressHandlerInstantiation = $$""" + internal static class {{progressHandler.Split('.')[^1]}}_{{GetGenericParametersAsString(genericParameters, "_", false)}} + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + _ = {{staticMethodsClass}}.InitCcw( + &Do_Abi_Invoke + ); + _ = {{staticMethodsClass}}.InitRcwHelper( + &Invoke + ); + return true; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr asyncInfo, {{progressParameter.AbiType}} progressInfo) + { + try + { + {{staticMethodsClass}}.Abi_Invoke(thisPtr, MarshalInterface<{{asyncInfoInterfaceWithGeneric}}>.FromAbi(asyncInfo), {{GeneratorHelper.GetFromAbiMarshaler(progressParameter.ProjectedType, progressParameter.AbiType, progressParameter.TypeKind, "progressInfo")}}); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe void Invoke(IObjectReference objRef, {{asyncInfoInterfaceWithGeneric}} asyncInfo, {{progressParameter.ProjectedType}} progressInfo) + { + IntPtr ThisPtr = objRef.ThisPtr; + ObjectReferenceValue __asyncInfo = default; + {{GeneratorHelper.GetMarshalerDeclaration(progressParameter.ProjectedType, progressParameter.AbiType, progressParameter.TypeKind, "progressInfo")}} + try + { + __asyncInfo = MarshalInterface<{{asyncInfoInterfaceWithGeneric}}>.CreateMarshaler2(asyncInfo, {{asyncInfoInterfaceWithGenericMethodsClass}}.IID); + IntPtr abiAsyncInfo = MarshalInspectable.GetAbi(__asyncInfo); + {{GeneratorHelper.GetCreateMarshaler(progressParameter.ProjectedType, progressParameter.AbiType, progressParameter.TypeKind, "progressInfo")}} + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3](ThisPtr, abiAsyncInfo, {{GeneratorHelper.GetAbiFromMarshaler(progressParameter.ProjectedType, progressParameter.AbiType, progressParameter.TypeKind, "progressInfo")}})); + global::System.GC.KeepAlive(objRef); + } + finally + { + MarshalInterface<{{asyncInfoInterfaceWithGeneric}}>.DisposeMarshaler(__asyncInfo); + {{GeneratorHelper.GetDisposeMarshaler(progressParameter.ProjectedType, progressParameter.AbiType, progressParameter.TypeKind, "progressInfo")}} + } + } + } + """; + return progressHandlerInstantiation; + } + + private static string GetIAsyncActionWithProgressInstantiation(EquatableArray genericParameters) + { + string staticMethodsClass = $"global::ABI.Windows.Foundation.IAsyncActionWithProgressMethods<{GetGenericParametersAsString(genericParameters, ", ", false, false)}>"; + string abiStaticMethodsClass = $"global::ABI.Windows.Foundation.IAsyncActionWithProgressMethods<{GetGenericParametersAsString(genericParameters, ", ", true, false)}>"; + string instantiation = $$""" + internal static class IAsyncActionWithProgress_{{GetGenericParametersAsString(genericParameters, "_", false)}} + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + return {{abiStaticMethodsClass}}.InitCcw( + &Do_Abi_put_Progress_0, + &Do_Abi_get_Progress_1, + &Do_Abi_put_Completed_2, + &Do_Abi_get_Completed_3, + &Do_Abi_GetResults_4 + ); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_GetResults_4(IntPtr thisPtr) + { + try + { + {{staticMethodsClass}}.Do_Abi_GetResults_4(thisPtr); + } + catch (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_put_Progress_0(IntPtr thisPtr, IntPtr handler) + { + _ = AsyncActionProgressHandler_{{GetGenericParametersAsString(genericParameters, "_", false)}}.Initialized; + try + { + {{staticMethodsClass}}.Do_Abi_put_Progress_0(thisPtr, global::ABI.Windows.Foundation.AsyncActionProgressHandler<{{genericParameters[0].ProjectedType}}>.FromAbi(handler)); + } + catch (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_Progress_1(IntPtr thisPtr, IntPtr* __return_value__) + { + _ = AsyncActionProgressHandler_{{GetGenericParametersAsString(genericParameters, "_", false)}}.Initialized; + global::Windows.Foundation.AsyncActionProgressHandler<{{genericParameters[0].ProjectedType}}> ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = {{staticMethodsClass}}.Do_Abi_get_Progress_1(thisPtr); + *__return_value__ = global::ABI.Windows.Foundation.AsyncActionProgressHandler<{{genericParameters[0].ProjectedType}}>.FromManaged(____return_value__); + + } + catch (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_put_Completed_2(IntPtr thisPtr, IntPtr handler) + { + _ = AsyncActionWithProgressCompletedHandler_{{GetGenericParametersAsString(genericParameters, "_", false)}}.Initialized; + try + { + {{staticMethodsClass}}.Do_Abi_put_Completed_2(thisPtr, global::ABI.Windows.Foundation.AsyncActionWithProgressCompletedHandler<{{genericParameters[0].ProjectedType}}>.FromAbi(handler)); + } + catch (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_Completed_3(IntPtr thisPtr, IntPtr* __return_value__) + { + _ = AsyncActionWithProgressCompletedHandler_{{GetGenericParametersAsString(genericParameters, "_", false)}}.Initialized; + global::Windows.Foundation.AsyncActionWithProgressCompletedHandler<{{genericParameters[0].ProjectedType}}> ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = {{staticMethodsClass}}.Do_Abi_get_Completed_3(thisPtr); + *__return_value__ = global::ABI.Windows.Foundation.AsyncActionWithProgressCompletedHandler<{{genericParameters[0].ProjectedType}}>.FromManaged(____return_value__); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + """; + return instantiation; + } + + private static string GetIAsyncOperationWithProgressInstantiation(EquatableArray genericParameters) + { + string staticMethodsClass = $"global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods<{GetGenericParametersAsString(genericParameters, ", ", false, false)}>"; + string abiStaticMethodsClass = $"global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods<{GetGenericParametersAsString(genericParameters, ", ", true, false)}>"; + string instantiation = $$""" + internal static class IAsyncOperationWithProgress_{{GetGenericParametersAsString(genericParameters, "_", false)}} + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + return {{abiStaticMethodsClass}}.InitCcw( + &Do_Abi_put_Progress_0, + &Do_Abi_get_Progress_1, + &Do_Abi_put_Completed_2, + &Do_Abi_get_Completed_3, + &Do_Abi_GetResults_4 + ); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_GetResults_4(IntPtr thisPtr, {{genericParameters[0].AbiType}}* __return_value__) + { + {{genericParameters[0].ProjectedType}} ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = {{staticMethodsClass}}.Do_Abi_GetResults_4(thisPtr); + *__return_value__ = {{GeneratorHelper.GetFromManagedMarshaler(genericParameters[0], "____return_value__")}}; + } + catch (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_put_Progress_0(IntPtr thisPtr, IntPtr handler) + { + _ = AsyncOperationProgressHandler_{{GetGenericParametersAsString(genericParameters, "_", false)}}.Initialized; + try + { + {{staticMethodsClass}}.Do_Abi_put_Progress_0(thisPtr, global::ABI.Windows.Foundation.AsyncOperationProgressHandler<{{genericParameters[0].ProjectedType}}, {{genericParameters[1].ProjectedType}}>.FromAbi(handler)); + } + catch (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_Progress_1(IntPtr thisPtr, IntPtr* __return_value__) + { + _ = AsyncOperationProgressHandler_{{GetGenericParametersAsString(genericParameters, "_", false)}}.Initialized; + global::Windows.Foundation.AsyncOperationProgressHandler<{{genericParameters[0].ProjectedType}}, {{genericParameters[1].ProjectedType}}> ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = {{staticMethodsClass}}.Do_Abi_get_Progress_1(thisPtr); + *__return_value__ = global::ABI.Windows.Foundation.AsyncOperationProgressHandler<{{genericParameters[0].ProjectedType}}, {{genericParameters[1].ProjectedType}}>.FromManaged(____return_value__); + + } + catch (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_put_Completed_2(IntPtr thisPtr, IntPtr handler) + { + _ = AsyncOperationWithProgressCompletedHandler_{{GetGenericParametersAsString(genericParameters, "_", false)}}.Initialized; + try + { + {{staticMethodsClass}}.Do_Abi_put_Completed_2(thisPtr, global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandler<{{genericParameters[0].ProjectedType}}, {{genericParameters[1].ProjectedType}}>.FromAbi(handler)); + } + catch (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_Completed_3(IntPtr thisPtr, IntPtr* __return_value__) + { + _ = AsyncOperationWithProgressCompletedHandler_{{GetGenericParametersAsString(genericParameters, "_", false)}}.Initialized; + global::Windows.Foundation.AsyncOperationWithProgressCompletedHandler<{{genericParameters[0].ProjectedType}}, {{genericParameters[1].ProjectedType}}> ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = {{staticMethodsClass}}.Do_Abi_get_Completed_3(thisPtr); + *__return_value__ = global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandler<{{genericParameters[0].ProjectedType}}, {{genericParameters[1].ProjectedType}}>.FromManaged(____return_value__); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + """; + return instantiation; + } + + private static string GetIAsyncOperationInstantiation(EquatableArray genericParameters) + { + string staticMethodsClass = $"global::ABI.Windows.Foundation.IAsyncOperationMethods<{GetGenericParametersAsString(genericParameters, ", ", false, false)}>"; + string abiStaticMethodsClass = $"global::ABI.Windows.Foundation.IAsyncOperationMethods<{GetGenericParametersAsString(genericParameters, ", ", true, false)}>"; + string instantiation = $$""" + internal static class IAsyncOperation_{{GetGenericParametersAsString(genericParameters, "_", false)}} + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + return {{abiStaticMethodsClass}}.InitCcw( + &Do_Abi_put_Completed_0, + &Do_Abi_get_Completed_1, + &Do_Abi_GetResults_2 + ); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_GetResults_2(IntPtr thisPtr, {{genericParameters[0].AbiType}}* __return_value__) + { + {{genericParameters[0].ProjectedType}} ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = {{staticMethodsClass}}.Do_Abi_GetResults_2(thisPtr); + *__return_value__ = {{GeneratorHelper.GetFromManagedMarshaler(genericParameters[0], "____return_value__")}}; + } + catch (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_put_Completed_0(IntPtr thisPtr, IntPtr handler) + { + _ = AsyncOperationCompletedHandler_{{GetGenericParametersAsString(genericParameters, "_", false)}}.Initialized; + try + { + {{staticMethodsClass}}.Do_Abi_put_Completed_0(thisPtr, global::ABI.Windows.Foundation.AsyncOperationCompletedHandler<{{genericParameters[0].ProjectedType}}>.FromAbi(handler)); + } + catch (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_Completed_1(IntPtr thisPtr, IntPtr* __return_value__) + { + _ = AsyncOperationCompletedHandler_{{GetGenericParametersAsString(genericParameters, "_", false)}}.Initialized; + global::Windows.Foundation.AsyncOperationCompletedHandler<{{genericParameters[0].ProjectedType}}> ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = {{staticMethodsClass}}.Do_Abi_get_Completed_1(thisPtr); + *__return_value__ = global::ABI.Windows.Foundation.AsyncOperationCompletedHandler<{{genericParameters[0].ProjectedType}}>.FromManaged(____return_value__); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + """; + return instantiation; + } + + private static string GetIMapChangedEventArgsInstantiation(EquatableArray genericParameters) + { + string staticMethodsClass = $"global::ABI.Windows.Foundation.Collections.IMapChangedEventArgsMethods<{GetGenericParametersAsString(genericParameters, ", ", false, false)}>"; + string abiStaticMethodsClass = $"global::ABI.Windows.Foundation.Collections.IMapChangedEventArgsMethods<{GetGenericParametersAsString(genericParameters, ", ", true, false)}>"; + string instantiation = $$""" + internal static class IMapChangedEventArgs_{{GetGenericParametersAsString(genericParameters, "_", false)}} + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + return {{abiStaticMethodsClass}}.InitCcw( + &Do_Abi_get_CollectionChange_0, + &Do_Abi_get_Key_1 + ); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_get_CollectionChange_0(IntPtr thisPtr, global::Windows.Foundation.Collections.CollectionChange* __return_value__) + { + *__return_value__ = default; + + try + { + *__return_value__ = {{staticMethodsClass}}.Do_Abi_get_CollectionChange_0(thisPtr); + } + catch (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_Key_1(IntPtr thisPtr, {{genericParameters[0].AbiType}}* __return_value__) + { + {{genericParameters[0].ProjectedType}} ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = {{staticMethodsClass}}.Do_Abi_get_Key_1(thisPtr); + *__return_value__ = {{GeneratorHelper.GetFromManagedMarshaler(genericParameters[0], "____return_value__")}}; + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + """; + return instantiation; + } + + private static string GetIObservableMapInstantiation(EquatableArray genericParameters) + { + string staticMethodsClass = $"global::ABI.Windows.Foundation.Collections.IObservableMapMethods<{GetGenericParametersAsString(genericParameters, ", ", false, false)}>"; + string abiStaticMethodsClass = $"global::ABI.Windows.Foundation.Collections.IObservableMapMethods<{GetGenericParametersAsString(genericParameters, ", ", true, false)}>"; + string instantiation = $$""" + internal static class IObservableMap_{{GetGenericParametersAsString(genericParameters, "_", false)}} + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + return {{abiStaticMethodsClass}}.InitCcw( + &Do_Abi_add_MapChanged_0, + &Do_Abi_remove_MapChanged_1 + ); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_add_MapChanged_0(IntPtr thisPtr, IntPtr vhnd, global::WinRT.EventRegistrationToken* __return_value__) + { + *__return_value__ = default; + _ = MapChangedEventHandler_{{GetGenericParametersAsString(genericParameters, "_", false)}}.Initialized; + try + { + *__return_value__ = {{staticMethodsClass}}.Do_Abi_add_MapChanged_0(thisPtr, global::ABI.Windows.Foundation.Collections.MapChangedEventHandler<{{genericParameters[0].ProjectedType}}, {{genericParameters[1].ProjectedType}}>.FromAbi(vhnd)); + } + catch (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_MapChanged_1(IntPtr thisPtr, global::WinRT.EventRegistrationToken token) + { + try + { + {{staticMethodsClass}}.Do_Abi_remove_MapChanged_1(thisPtr, token); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + """; + return instantiation; + } + + private static string GetIObservableVectorInstantiation(EquatableArray genericParameters) + { + string staticMethodsClass = $"global::ABI.Windows.Foundation.Collections.IObservableVectorMethods<{GetGenericParametersAsString(genericParameters, ", ", false, false)}>"; + string abiStaticMethodsClass = $"global::ABI.Windows.Foundation.Collections.IObservableVectorMethods<{GetGenericParametersAsString(genericParameters, ", ", true, false)}>"; + string instantiation = $$""" + internal static class IObservableVector_{{GetGenericParametersAsString(genericParameters, "_", false)}} + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + return {{abiStaticMethodsClass}}.InitCcw( + &Do_Abi_add_VectorChanged_0, + &Do_Abi_remove_VectorChanged_1 + ); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_add_VectorChanged_0(IntPtr thisPtr, IntPtr vhnd, global::WinRT.EventRegistrationToken* __return_value__) + { + *__return_value__ = default; + _ = VectorChangedEventHandler_{{GetGenericParametersAsString(genericParameters, "_", false)}}.Initialized; + try + { + *__return_value__ = {{staticMethodsClass}}.Do_Abi_add_VectorChanged_0(thisPtr, global::ABI.Windows.Foundation.Collections.VectorChangedEventHandler<{{genericParameters[0].ProjectedType}}>.FromAbi(vhnd)); + } + catch (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_VectorChanged_1(IntPtr thisPtr, global::WinRT.EventRegistrationToken token) + { + try + { + {{staticMethodsClass}}.Do_Abi_remove_VectorChanged_1(thisPtr, token); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + """; + return instantiation; + } + + private static string GetGenericParametersAsString(EquatableArray genericParameters, string separator, bool includeAbiTypes, bool escape = true) + { + string genericParametersStr = string.Join(separator, + genericParameters.Select(genericParameter => + { + if (includeAbiTypes) + { + return string.Join( + separator, + escape ? GeneratorHelper.EscapeTypeNameForIdentifier(genericParameter.ProjectedType) : genericParameter.ProjectedType, + escape ? GeneratorHelper.EscapeTypeNameForIdentifier(genericParameter.AbiType) : genericParameter.AbiType); + } + else + { + return escape ? GeneratorHelper.EscapeTypeNameForIdentifier(genericParameter.ProjectedType) : genericParameter.ProjectedType; + } + })); + return genericParametersStr; + } + } +} diff --git a/src/Authoring/WinRT.SourceGenerator/Helper.cs b/src/Authoring/WinRT.SourceGenerator/Helper.cs index 124fe8ac8..d0c08034e 100644 --- a/src/Authoring/WinRT.SourceGenerator/Helper.cs +++ b/src/Authoring/WinRT.SourceGenerator/Helper.cs @@ -1,8 +1,18 @@ -using Microsoft.CodeAnalysis; +// 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 System; using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; using System.Linq; +using System.Text.RegularExpressions; namespace Generator { @@ -61,6 +71,7 @@ public static string GetAssemblyVersion(this GeneratorExecutionContext context) return assemblyVersion; } + [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1035", Justification = "We need to do file IO to invoke the 'cswinrt' tool.")] public static string GetGeneratedFilesDir(this GeneratorExecutionContext context) { context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWinRTGeneratedFilesDir", out var generatedFilesDir); @@ -68,6 +79,18 @@ public static string GetGeneratedFilesDir(this GeneratorExecutionContext context return generatedFilesDir; } + public static string GetCsWinRTExeTFM(this GeneratorExecutionContext context) + { + context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWinRTExeTFM", out var csWinRTExeTFM); + return csWinRTExeTFM; + } + + public static bool IsNet7OrGreater(this GeneratorExecutionContext context) + { + string tfm = context.GetCsWinRTExeTFM(); + return tfm.StartsWith("net7.0") || tfm.StartsWith("net8.0") || tfm.StartsWith("net9.0"); + } + public static bool IsCsWinRTComponent(this GeneratorExecutionContext context) { if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWinRTComponent", out var isCsWinRTComponentStr)) @@ -78,6 +101,153 @@ public static bool IsCsWinRTComponent(this GeneratorExecutionContext context) return false; } + public static bool IsCsWinRTComponent(this AnalyzerConfigOptionsProvider provider) + { + if (provider.GlobalOptions.TryGetValue("build_property.CsWinRTComponent", out var isCsWinRTComponentStr)) + { + return bool.TryParse(isCsWinRTComponentStr, out var isCsWinRTComponent) && isCsWinRTComponent; + } + + return false; + } + + public static bool IsCsWinRTAotOptimizerEnabled(this AnalyzerConfigOptionsProvider provider) + { + if (provider.GlobalOptions.TryGetValue("build_property.CsWinRTAotOptimizerEnabled", out var isCsWinRTAotOptimizerEnabledStr)) + { + return (bool.TryParse(isCsWinRTAotOptimizerEnabledStr, out var isCsWinRTAotOptimizerEnabled) && isCsWinRTAotOptimizerEnabled) || + string.Equals(isCsWinRTAotOptimizerEnabledStr, "OptIn", StringComparison.OrdinalIgnoreCase) || + string.Equals(isCsWinRTAotOptimizerEnabledStr, "Auto", StringComparison.OrdinalIgnoreCase); + } + + return false; + } + + private enum CsWinRTAotOptimizerMode + { + Disabled = 0, + OptIn = 1, + Auto = 2, + Default = 3, + } + + public static bool IsCsWinRTAotOptimizerInAutoMode(AnalyzerConfigOptionsProvider provider, Compilation compilation) + { + var mode = GetMode(provider); + + if (mode == CsWinRTAotOptimizerMode.Default) + { + // If mode is default and this is a WinUI or UWP project, which we detect by using the Button type as a marker, + // then AOT optimizer is running in auto mode because in both projects the main API boundary is WinRT. + // For CsWinRT components, we also run by default in auto mode. + return provider.IsCsWinRTComponent() || + compilation.GetTypeByMetadataName("Microsoft.UI.Xaml.Controls.Button") is not null || + compilation.GetTypeByMetadataName("Windows.UI.Xaml.Controls.Button") is not null || + // If warning level was explicitly set to 2 without a mode set, + // we don't want to change the behavior of those projects who were + // relying on it running. If they set the mode, then the mode would + // be respected. + provider.GetCsWinRTAotWarningLevel() >= 2; + } + + // If mode is not the default, check if it is set explicitly to Auto. + return mode == CsWinRTAotOptimizerMode.Auto; + + static CsWinRTAotOptimizerMode GetMode(AnalyzerConfigOptionsProvider provider) + { + if (provider.GlobalOptions.TryGetValue("build_property.CsWinRTAotOptimizerEnabled", out var isCsWinRTAotOptimizerEnabledStr)) + { + if (string.Equals(isCsWinRTAotOptimizerEnabledStr, "OptIn", StringComparison.OrdinalIgnoreCase)) + { + return CsWinRTAotOptimizerMode.OptIn; + } + + if (string.Equals(isCsWinRTAotOptimizerEnabledStr, "Auto", StringComparison.OrdinalIgnoreCase)) + { + return CsWinRTAotOptimizerMode.Auto; + } + + if (bool.TryParse(isCsWinRTAotOptimizerEnabledStr, out var isCsWinRTAotOptimizerEnabled) && isCsWinRTAotOptimizerEnabled) + { + return CsWinRTAotOptimizerMode.Default; + } + } + + return CsWinRTAotOptimizerMode.Disabled; + } + } + + public static bool GetCsWinRTRcwFactoryFallbackGeneratorForceOptIn(this AnalyzerConfigOptionsProvider provider) + { + if (provider.GlobalOptions.TryGetValue("build_property.CsWinRTRcwFactoryFallbackGeneratorForceOptIn", out var csWinRTRcwFactoryFallbackGeneratorForceOptIn)) + { + return bool.TryParse(csWinRTRcwFactoryFallbackGeneratorForceOptIn, out var isCsWinRTRcwFactoryFallbackGeneratorForceOptIn) && isCsWinRTRcwFactoryFallbackGeneratorForceOptIn; + } + + return false; + } + + public static bool GetCsWinRTMergeReferencedActivationFactories(this AnalyzerConfigOptionsProvider provider) + { + if (provider.GlobalOptions.TryGetValue("build_property.CsWinRTMergeReferencedActivationFactories", out var csWinRTMergeReferencedActivationFactories)) + { + return bool.TryParse(csWinRTMergeReferencedActivationFactories, out var isCsWinRTMergeReferencedActivationFactories) && isCsWinRTMergeReferencedActivationFactories; + } + + return false; + } + + public static bool GetCsWinRTRcwFactoryFallbackGeneratorForceOptOut(this AnalyzerConfigOptionsProvider provider) + { + if (provider.GlobalOptions.TryGetValue("build_property.CsWinRTRcwFactoryFallbackGeneratorForceOptOut", out var csWinRTRcwFactoryFallbackGeneratorForceOptOut)) + { + return bool.TryParse(csWinRTRcwFactoryFallbackGeneratorForceOptOut, out var isCsWinRTRcwFactoryFallbackGeneratorForceOptOut) && isCsWinRTRcwFactoryFallbackGeneratorForceOptOut; + } + + return false; + } + + public static bool IsCsWinRTCcwLookupTableGeneratorEnabled(this AnalyzerConfigOptionsProvider provider) + { + if (provider.GlobalOptions.TryGetValue("build_property.CsWinRTCcwLookupTableGeneratorEnabled", out var csWinRTCcwLookupTableGeneratorEnabled)) + { + return bool.TryParse(csWinRTCcwLookupTableGeneratorEnabled, out var isCsWinRTCcwLookupTableGeneratorEnabled) && isCsWinRTCcwLookupTableGeneratorEnabled; + } + + return false; + } + + public static bool GetCsWinRTUseWindowsUIXamlProjections(this AnalyzerConfigOptionsProvider provider) + { + if (provider.GlobalOptions.TryGetValue("build_property.CsWinRTUseWindowsUIXamlProjections", out var csWinRTUseWindowsUIXamlProjections)) + { + return bool.TryParse(csWinRTUseWindowsUIXamlProjections, out var isCsWinRTUseWindowsUIXamlProjectionsEnabled) && isCsWinRTUseWindowsUIXamlProjectionsEnabled; + } + + return false; + } + + public static bool GetEnableAotAnalyzer(this AnalyzerConfigOptionsProvider provider) + { + if (provider.GlobalOptions.TryGetValue("build_property.EnableAotAnalyzer", out var enableAotAnalyzer)) + { + return bool.TryParse(enableAotAnalyzer, out var isAotAnalyzerEnabled) && isAotAnalyzerEnabled; + } + + return false; + } + + public static int GetCsWinRTAotWarningLevel(this AnalyzerConfigOptionsProvider provider) + { + if (provider.GlobalOptions.TryGetValue("build_property.CsWinRTAotWarningLevel", out var csWinRTAotWarningLevelStr) && + int.TryParse(csWinRTAotWarningLevelStr, out var csWinRTAotWarningLevel)) + { + return csWinRTAotWarningLevel; + } + + return 0; + } + public static bool ShouldGenerateWinMDOnly(this GeneratorExecutionContext context) { if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWinRTGenerateWinMDOnly", out var CsWinRTGenerateWinMDOnlyStr)) @@ -88,6 +258,21 @@ public static bool ShouldGenerateWinMDOnly(this GeneratorExecutionContext contex return false; } + /// + /// Gets whether the "CsWinRTAotExportsEnabled" MSBuild property is defined. + /// + /// The input value to use. + /// Whether the "CsWinRTAotExportsEnabled" MSBuild property is defined. + public static bool ShouldGenerateWinRTNativeExports(this GeneratorExecutionContext context) + { + if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWinRTAotExportsEnabled", out var isCsWinRTAotExportsEnabledStr)) + { + return bool.TryParse(isCsWinRTAotExportsEnabledStr, out var isCsWinRTAotExportsEnabled) && isCsWinRTAotExportsEnabled; + } + + return false; + } + public static string GetCsWinRTExe(this GeneratorExecutionContext context) { context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWinRTExe", out var cswinrtExe); @@ -114,12 +299,1072 @@ public static string GetCsWinRTDependentMetadata(this GeneratorExecutionContext public static string GetWinmdOutputFile(this GeneratorExecutionContext context) { - var fileName = context.GetAssemblyName(); - if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWinRTWinMDOutputFile", out var ret)) - { - fileName = ret!; + var fileName = context.GetAssemblyName(); + if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWinRTWinMDOutputFile", out var ret)) + { + fileName = ret!; } return Path.Combine(context.GetGeneratedFilesDir(), fileName + ".winmd"); } + + public static bool GetCsWinRTMergeReferencedActivationFactories(this GeneratorExecutionContext context) + { + if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWinRTMergeReferencedActivationFactories", out var csWinRTMergeReferencedActivationFactories)) + { + return bool.TryParse(csWinRTMergeReferencedActivationFactories, out var isCsWinRTMergeReferencedActivationFactories) && isCsWinRTMergeReferencedActivationFactories; + } + + return false; + } + + public static bool GetCsWinRTGenerateOverridedClassNameActivationFactory(this GeneratorExecutionContext context) + { + if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWinRTGenerateOverridedClassNameActivationFactory", out var csWinRTGenerateOverridedClassNameActivationFactory)) + { + return bool.TryParse(csWinRTGenerateOverridedClassNameActivationFactory, out var isCsWinRTGenerateOverridedClassNameActivationFactory) && isCsWinRTGenerateOverridedClassNameActivationFactory; + } + + return false; + } + + public static bool GetCsWinRTGenerateManagedDllGetActivationFactory(this GeneratorExecutionContext context) + { + if (context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWinRTGenerateManagedDllGetActivationFactory", out var csWinRTGenerateManagedDllGetActivationFactory)) + { + return bool.TryParse(csWinRTGenerateManagedDllGetActivationFactory, out var isCsWinRTGenerateManagedDllGetActivationFactory) && isCsWinRTGenerateManagedDllGetActivationFactory; + } + + return false; + } + } + + static class GeneratorHelper + { + private static bool IsFundamentalType(ISymbol type) + { + if (type is INamedTypeSymbol namedTypeSymbol) + { + switch (namedTypeSymbol.SpecialType) + { + case SpecialType.System_Boolean: + case SpecialType.System_String: + case SpecialType.System_Single: + case SpecialType.System_Double: + case SpecialType.System_UInt16: + case SpecialType.System_UInt32: + case SpecialType.System_UInt64: + case SpecialType.System_Int16: + case SpecialType.System_Int32: + case SpecialType.System_Int64: + case SpecialType.System_Char: + case SpecialType.System_Byte: + case SpecialType.System_Object: + return true; + } + } + + return type.ToDisplayString() == "System.Guid"; + } + + /// + /// Checks whether an assembly contains old projections. + /// + /// The assembly to inspect. + /// Whether contains old projections. + public static bool IsOldProjectionAssembly(IAssemblySymbol assemblySymbol) + { + // We only care about assemblies that have some dependent assemblies + if (assemblySymbol.Modules.First() is not { ReferencedAssemblies: { Length: > 0 } dependentAssemblies }) + { + return false; + } + + // Scan all dependent assemblies to look for CsWinRT with version < 2.0.8 + foreach (AssemblyIdentity assemblyIdentity in dependentAssemblies) + { + if (assemblyIdentity.Name == "WinRT.Runtime") + { + return assemblyIdentity.Version < new Version(2, 0, 8) && + assemblyIdentity.Version != new Version(0, 0, 0, 0); + } + } + + // This assembly is not a projection assembly + return false; + } + + public static bool IsOldCsWinRTExe(GeneratorExecutionContext context) + { + string cswinrtExe = context.GetCsWinRTExe(); + var cswinrtExeVersion = new Version(FileVersionInfo.GetVersionInfo(cswinrtExe).FileVersion); + return cswinrtExeVersion < new Version(2, 1, 0) && cswinrtExeVersion != new Version(0, 0, 0, 0); + } + + public static bool AllowUnsafe(Compilation compilation) + { + return compilation is CSharpCompilation csharpCompilation && csharpCompilation.Options.AllowUnsafe; + } + + // Returns whether the class is marked with the GeneratedBindableCustomProperty attribute or not. + public static bool IsGeneratedBindableCustomPropertyClass(Compilation compilation, ISymbol type) + { + var generatedBindableCustomPropertyAttribute = compilation.GetTypeByMetadataName("WinRT.GeneratedBindableCustomPropertyAttribute"); + return HasAttributeWithType(type, generatedBindableCustomPropertyAttribute); + } + + // Returns whether it is a WinRT class or interface. + // If the bool parameter is true, then custom mapped interfaces are also considered. + // This function is similar to whether it is a WinRT type, but custom type mapped + // classes are excluded given those are C# implemented classes such as string. + public static Func IsWinRTClassOrInterface(Compilation compilation, Func isWinRTType, TypeMapper mapper) + { + var winrtRuntimeTypeAttribute = compilation.GetTypeByMetadataName("WinRT.WindowsRuntimeTypeAttribute"); + return IsWinRTClassOrInterfaceHelper; + + bool IsWinRTClassOrInterfaceHelper(ISymbol type, bool includeMappedInterfaces) + { + if (type is ITypeSymbol typeSymbol && typeSymbol.TypeKind == TypeKind.Interface) + { + return isWinRTType(type, mapper); + } + + return HasAttributeWithType(type, winrtRuntimeTypeAttribute); + } + } + + public static bool IsWinRTType(ISymbol type, TypeMapper mapper) + { + return IsWinRTType(type, null, mapper); + } + + public static bool IsWinRTType(ISymbol type, Func isAuthoringWinRTType, TypeMapper mapper) + { + bool isProjectedType = type.GetAttributes(). + Any(static attribute => string.CompareOrdinal(attribute.AttributeClass.Name, "WindowsRuntimeTypeAttribute") == 0) || + IsFundamentalType(type); + + if (!isProjectedType & type.ContainingNamespace != null) + { + string qualifiedTypeName = string.Join(".", type.ContainingNamespace.ToDisplayString(), type.MetadataName); + isProjectedType = mapper.HasMappingForType(qualifiedTypeName); + + // Check if CsWinRT component projected type from another project. + if (!isProjectedType) + { + isProjectedType = type.ContainingAssembly.GetTypeByMetadataName(GetAuthoringMetadataTypeName(qualifiedTypeName)) != null; + } + } + + // Ensure all generic parameters are WinRT types. + if (isProjectedType && type is INamedTypeSymbol namedType && namedType.IsGenericType && !namedType.IsDefinition) + { + isProjectedType = namedType.TypeArguments.All(t => + IsWinRTType(t, isAuthoringWinRTType, mapper) || + (isAuthoringWinRTType != null && isAuthoringWinRTType(t, mapper))); + } + + return isProjectedType; + } + + public static bool IsWinRTType(ISymbol type, ITypeSymbol winrtRuntimeTypeAttribute, TypeMapper mapper, bool isComponentProject, IAssemblySymbol currentAssembly) + { + if (IsFundamentalType(type)) + { + return true; + } + + if (isComponentProject && + // Make sure type is in component project. + SymbolEqualityComparer.Default.Equals(type.ContainingAssembly, currentAssembly) && + type.DeclaredAccessibility == Accessibility.Public) + { + // Authoring diagnostics will make sure all public types are valid WinRT types. + return true; + } + + bool isProjectedType = HasAttributeWithType(type, winrtRuntimeTypeAttribute); + if (!isProjectedType & type.ContainingNamespace != null) + { + string qualifiedTypeName = string.Join(".", type.ContainingNamespace.ToDisplayString(), type.MetadataName); + isProjectedType = mapper.HasMappingForType(qualifiedTypeName); + + // Check if CsWinRT component projected type from another project. + if (!isProjectedType) + { + isProjectedType = type.ContainingAssembly.GetTypeByMetadataName(GetAuthoringMetadataTypeName(qualifiedTypeName)) != null; + } + } + + // Ensure all generic parameters are WinRT types. + if (isProjectedType && + type is INamedTypeSymbol namedType && + namedType.IsGenericType && + !namedType.IsDefinition) + { + isProjectedType = namedType.TypeArguments.All(t => IsWinRTType(t, winrtRuntimeTypeAttribute, mapper, isComponentProject, currentAssembly)); + } + + return isProjectedType; + } + + public static bool IsWinRTTypeOrImplementsWinRTType(ISymbol type, ITypeSymbol winrtRuntimeTypeAttribute, TypeMapper mapper, bool isComponentProject, IAssemblySymbol currentAssembly) + { + if (IsWinRTType(type, winrtRuntimeTypeAttribute, mapper, isComponentProject, currentAssembly)) + { + return true; + } + + if (type is INamedTypeSymbol namedType && + namedType.AllInterfaces.Any(iface => IsWinRTType(iface, winrtRuntimeTypeAttribute, mapper, isComponentProject, currentAssembly))) + { + return true; + } + + return false; + } + + // Assuming a type is a WinRT type, this determines whether it is a WinRT type from custom type mappings. + // i.e Whether it is a built-in type that is also a WinRT type. + public static bool IsCustomMappedType(ISymbol type, TypeMapper mapper) + { + if (IsFundamentalType(type)) + { + return true; + } + + bool isCustomMappedType = false; + if (type.ContainingNamespace != null) + { + isCustomMappedType = mapper.HasMappingForType(string.Join(".", type.ContainingNamespace.ToDisplayString(), type.MetadataName)); + } + + // Ensure all generic parameters are WinRT types. + if (isCustomMappedType && + type is INamedTypeSymbol namedType && + namedType.IsGenericType && + !namedType.IsDefinition) + { + isCustomMappedType = namedType.TypeArguments.All(t => IsCustomMappedType(t, mapper)); + } + + return isCustomMappedType; + } + + // Checks if the interface references any internal types (either the interface itself or within its generic types). + public static bool IsInternalInterfaceFromReferences(INamedTypeSymbol iface, IAssemblySymbol currentAssembly) + { + if (iface.DeclaredAccessibility == Accessibility.Internal && + !SymbolEqualityComparer.Default.Equals(iface.ContainingAssembly, currentAssembly)) + { + return true; + } + + if (iface.IsGenericType) + { + // Making use of HashSet to avoid checking multiple times for same type and to avoid doing recursive calls. + HashSet genericArgumentsToProcess = new(iface.TypeArguments, SymbolEqualityComparer.Default); + HashSet visitedTypes = new(SymbolEqualityComparer.Default); + while (genericArgumentsToProcess.Count != 0) + { + var currentType = genericArgumentsToProcess.First(); + visitedTypes.Add(currentType); + genericArgumentsToProcess.Remove(currentType); + + if (currentType.DeclaredAccessibility == Accessibility.Internal && + !SymbolEqualityComparer.Default.Equals(currentType.ContainingAssembly, currentAssembly)) + { + return true; + } + + if (currentType is INamedTypeSymbol currentNamedTypeSymbol) + { + if (currentNamedTypeSymbol.IsGenericType) + { + foreach (var typeArgument in currentNamedTypeSymbol.TypeArguments) + { + if (!visitedTypes.Contains(typeArgument)) + { + genericArgumentsToProcess.Add(typeArgument); + } + } + } + } + } + } + + return false; + } + + // Checks whether the symbol references any generic that hasn't been instantiated + // and is used by a WinRT interface. For instance, List where T is a generic. + // If the generic isn't used by any WinRT interface, this returns false as for + // instance, we can still generate the vtable attribute for it. + public static bool HasNonInstantiatedWinRTGeneric(ITypeSymbol symbol, TypeMapper mapper) + { + return symbol is INamedTypeSymbol namedType && + (IsArgumentTypeParameter(namedType) || + (namedType.TypeArguments.Any(IsArgumentTypeParameter) && + namedType.AllInterfaces.Any(iface => iface.TypeArguments.Any(IsArgumentTypeParameter) && + // Checks if without the non-instantiated generic, whether it would be a WinRT type. + IsWinRTType(iface.OriginalDefinition, null, mapper)))); + + static bool IsArgumentTypeParameter(ITypeSymbol argument) + { + return argument.TypeKind == TypeKind.TypeParameter; + } + } + + public static bool IsPartial(INamedTypeSymbol symbol) + { + bool isPartial = true; + for (ITypeSymbol parent = symbol; parent is not null; parent = parent.ContainingType) + { + isPartial &= parent.DeclaringSyntaxReferences.Any( + static syntax => syntax.GetSyntax() is BaseTypeDeclarationSyntax declaration && + declaration.Modifiers.Any(SyntaxKind.PartialKeyword)); + } + return isPartial; + } + + public static bool IsPartial(TypeDeclarationSyntax node) + { + bool isPartial = true; + for (TypeDeclarationSyntax parent = node; parent is not null; parent = parent.Parent as TypeDeclarationSyntax) + { + isPartial &= parent.Modifiers.Any(static m => m.IsKind(SyntaxKind.PartialKeyword)); + } + return isPartial; + } + + public static bool HasPrivateclass(ITypeSymbol symbol) + { + return symbol is INamedTypeSymbol namedType && + (namedType.DeclaredAccessibility == Accessibility.Private || + namedType.TypeArguments.Any(static argument => argument.DeclaredAccessibility == Accessibility.Private)); + } + + public static bool HasWinRTExposedTypeAttribute(ISymbol type) + { + return type.GetAttributes(). + Any(static attribute => string.CompareOrdinal(attribute.AttributeClass.Name, "WinRTExposedTypeAttribute") == 0); + } + + public static bool HasWinRTRuntimeClassNameAttribute(ISymbol type, Compilation compilation) + { + var winrtRuntimeClassNameAttribute = compilation.GetTypeByMetadataName("WinRT.WinRTRuntimeClassNameAttribute"); + if (winrtRuntimeClassNameAttribute is null) + { + return false; + } + + return HasAttributeWithType(type, winrtRuntimeClassNameAttribute); + } + + public static bool IsWinRTType(MemberDeclarationSyntax node) + { + bool isProjectedType = node.AttributeLists.SelectMany(static list => list.Attributes). + Any(static attribute => string.CompareOrdinal(attribute.Name.NormalizeWhitespace().ToFullString(), "global::WinRT.WindowsRuntimeType") == 0); + return isProjectedType; + } + + public static bool HasBindableCustomPropertyAttribute(MemberDeclarationSyntax node) + { + return node.AttributeLists.SelectMany(static list => list.Attributes).Any(IsBindableCustomPropertyAttribute); + + // Check based on identifier name if this is the GeneratedBindableCustomProperty attribute. + // Technically this can be a different namespace, but we will confirm later once + // we have access to the semantic model. + static bool IsBindableCustomPropertyAttribute(AttributeSyntax attribute) + { + var nameSyntax = attribute.Name; + if (nameSyntax is QualifiedNameSyntax qualifiedName) + { + // Right would have the attribute while left is the namespace. + nameSyntax = qualifiedName.Right; + } + + return nameSyntax is IdentifierNameSyntax name && + (name.Identifier.ValueText == "GeneratedBindableCustomProperty" || + name.Identifier.ValueText == "GeneratedBindableCustomPropertyAttribute"); + } + } + + /// + /// Checks whether or not a given symbol has an attribute with the 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 type. + public static bool HasAttributeWithType(ISymbol symbol, ITypeSymbol attributeTypeSymbol) + { + foreach (AttributeData attribute in symbol.GetAttributes()) + { + if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, attributeTypeSymbol)) + { + return true; + } + } + + return false; + } + + /// + /// Checks whether or not a given symbol has an attribute with any types in a given sequence. + /// + /// The input instance to check. + /// The instances for the attributes type to look for. + /// Whether or not has an attribute with any of the specified types. + public static bool HasAttributeWithAnyType(ISymbol symbol, ImmutableArray attributeTypeSymbols) + { + foreach (AttributeData attribute in symbol.GetAttributes()) + { + if (attributeTypeSymbols.Contains(attribute.AttributeClass, SymbolEqualityComparer.Default)) + { + return true; + } + } + + return false; + } + + /// + /// Checks whether a symbol is annotated with [WinRTExposedType(typeof(WinRTManagedOnlyTypeDetails))]. + /// + public static bool IsManagedOnlyType(ISymbol symbol, ITypeSymbol winrtExposedTypeAttribute, ITypeSymbol winrtManagedOnlyTypeDetails) + { + foreach (AttributeData attribute in symbol.GetAttributes()) + { + if (SymbolEqualityComparer.Default.Equals(attribute.AttributeClass, winrtExposedTypeAttribute)) + { + if (attribute.ConstructorArguments is [{ Kind: TypedConstantKind.Type, Type: ITypeSymbol exposedTypeDetails }] && + SymbolEqualityComparer.Default.Equals(exposedTypeDetails, winrtManagedOnlyTypeDetails)) + { + return true; + } + + // A type can have just one [WinRTExposedType] attribute. If the details are not WinRTManagedOnlyTypeDetails, + // we can immediatley stop here and avoid checking all remaining attributes, as we couldn't possibly match. + return false; + } + } + + return false; + } + + public static Func IsWinRTType(Compilation compilation, bool isComponentProject) + { + var winrtTypeAttribute = compilation.GetTypeByMetadataName("WinRT.WindowsRuntimeTypeAttribute"); + return IsWinRTTypeHelper; + + bool IsWinRTTypeHelper(ISymbol type, TypeMapper typeMapper) + { + return IsWinRTType(type, winrtTypeAttribute, typeMapper, isComponentProject, compilation.Assembly); + } + } + + public static Func IsManagedOnlyType(Compilation compilation) + { + var winrtExposedTypeAttribute = compilation.GetTypeByMetadataName("WinRT.WinRTExposedTypeAttribute"); + var winrtManagedOnlyTypeDetails = compilation.GetTypeByMetadataName("WinRT.WinRTManagedOnlyTypeDetails"); + + return IsManagedOnlyTypeHelper; + + bool IsManagedOnlyTypeHelper(ISymbol type) + { + return IsManagedOnlyType(type, winrtExposedTypeAttribute, winrtManagedOnlyTypeDetails); + } + } + + private static string GetAbiTypeForFundamentalType(ISymbol type) + { + if (type is INamedTypeSymbol namedTypeSymbol) + { + switch (namedTypeSymbol.SpecialType) + { + case SpecialType.System_Boolean: + return "byte"; + case SpecialType.System_String: + return "IntPtr"; + case SpecialType.System_Char: + return "ushort"; + case SpecialType.System_Object: + return "IntPtr"; + case SpecialType.System_Single: + case SpecialType.System_Double: + case SpecialType.System_UInt16: + case SpecialType.System_UInt32: + case SpecialType.System_UInt64: + case SpecialType.System_Int16: + case SpecialType.System_Int32: + case SpecialType.System_Int64: + case SpecialType.System_Byte: + return type.ToDisplayString(); + } + } + + return type.ToDisplayString(); + } + + public static bool IsBlittableValueType(ITypeSymbol type, TypeMapper typeMapper) + { + if (!type.IsValueType) + { + return false; + } + + if (type.SpecialType != SpecialType.None) + { + switch (type.SpecialType) + { + case SpecialType.System_Single: + case SpecialType.System_Double: + case SpecialType.System_UInt16: + case SpecialType.System_UInt32: + case SpecialType.System_UInt64: + case SpecialType.System_Int16: + case SpecialType.System_Int32: + case SpecialType.System_Int64: + case SpecialType.System_Byte: + case SpecialType.System_SByte: + case SpecialType.System_IntPtr: + case SpecialType.System_UIntPtr: + return true; + default: + return false; + } + } + + if (type.ContainingNamespace != null) + { + string customTypeMapKey = string.Join(".", type.ContainingNamespace.ToDisplayString(), type.MetadataName); + if (typeMapper.HasMappingForType(customTypeMapKey)) + { + return typeMapper.GetMappedType(customTypeMapKey).IsBlittable(); + } + } + + if (type.TypeKind == TypeKind.Enum) + { + return true; + } + + if (type.TypeKind == TypeKind.Struct) + { + foreach (var typeMember in type.GetMembers()) + { + if (typeMember is IFieldSymbol field && + !field.IsStatic && + !IsBlittableValueType(field.Type, typeMapper)) + { + return false; + } + } + } + return true; + } + + public static string GetAbiType(ITypeSymbol type, TypeMapper mapper) + { + if (IsFundamentalType(type)) + { + return GetAbiTypeForFundamentalType(type); + } + + var typeStr = type.ToDisplayString(); + if (typeStr == "System.Type") + { + return "ABI.System.Type"; + } + else if (typeStr.StartsWith("System.Collections.Generic.KeyValuePair<")) + { + return "IntPtr"; + } + else if (typeStr == "System.Exception") + { + return "ABI.System.Exception"; + } + + if (type.IsValueType && !type.NullableAnnotation.HasFlag(NullableAnnotation.Annotated)) + { + string customTypeMapKey = string.Join(".", type.ContainingNamespace.ToDisplayString(), type.MetadataName); + if (mapper.HasMappingForType(customTypeMapKey)) + { + string prefix = mapper.GetMappedType(customTypeMapKey).IsBlittable() ? "" : "ABI."; + return prefix + typeStr; + } + + if (!IsBlittableValueType(type, mapper)) + { + var winrtHelperAttribute = type.GetAttributes(). + Where(static attribute => string.CompareOrdinal(attribute.AttributeClass.Name, "WindowsRuntimeHelperTypeAttribute") == 0). + FirstOrDefault(); + if (winrtHelperAttribute != null && + winrtHelperAttribute.ConstructorArguments.Any()) + { + return winrtHelperAttribute.ConstructorArguments[0].Value.ToString(); + } + // Handling authoring scenario where Impl type has the attributes and + // if the current component is the one being authored, it may not be + // generated yet to check given it is the same compilation. + else + { + return "ABI." + typeStr; + } + } + else + { + return typeStr; + } + } + + return "IntPtr"; + } + + public static string GetMarshalerClass(string type, string abiType, TypeKind kind, bool isArray, bool useGenericMarshaler = false) + { + if (type == "System.String" || type == "string") + { + return "MarshalString"; + } + else if (type == "System.Type" || type == "Type") + { + if (isArray) + { + return "MarshalNonBlittable"; + } + else + { + return "global::ABI.System.Type"; + } + } + else if (type == "System.Exception" || type == "Exception") + { + if (isArray) + { + return "MarshalNonBlittable"; + } + else + { + return "global::ABI.System.Exception"; + } + } + else if (type == "System.Object" || type == "object") + { + return "MarshalInspectable"; + } + else if (type.StartsWith("System.Collections.Generic.KeyValuePair<")) + { + return $$"""MarshalInterface<{{type}}>"""; + } + else if (kind == TypeKind.Enum) + { + if (isArray) + { + return $$"""MarshalBlittable<{{type}}>"""; + } + else + { + return ""; + } + } + else if (kind == TypeKind.Struct) + { + if (type == abiType) + { + if (isArray) + { + return $$"""MarshalBlittable<{{type}}>"""; + } + else + { + return ""; + } + } + else + { + if (isArray) + { + return $$"""MarshalNonBlittable<{{type}}>"""; + } + else + { + return "global::ABI." + type; + } + } + } + else if (kind == TypeKind.Interface) + { + return $$"""MarshalInterface<{{type}}>"""; + } + else if (kind == TypeKind.Class || kind == TypeKind.Delegate) + { + return useGenericMarshaler ? "MarshalInspectable" : "global::ABI." + type; + } + + throw new ArgumentException(); + } + + public static string GetFromAbiMarshaler(GenericParameter genericParameter, string arg) + { + return GetFromAbiMarshaler( + genericParameter.ProjectedType, + genericParameter.AbiType, + genericParameter.TypeKind, + arg); + } + + public static string GetFromAbiMarshaler(string type, string abiType, TypeKind kind, string arg) + { + string marshalerType = GetMarshalerClass(type, abiType, kind, false); + if (kind == TypeKind.Enum || (kind == TypeKind.Struct && type == abiType)) + { + return arg; + } + else if (type == "bool") + { + return $$"""({{arg}} != 0)"""; + } + else if (type == "char") + { + return $$"""(char){{arg}}"""; + } + else + { + return $$"""{{marshalerType}}.FromAbi({{arg}})"""; + } + } + + public static string GetFromManagedMarshaler(GenericParameter genericParameter, string arg) + { + return GetFromManagedMarshaler( + genericParameter.ProjectedType, + genericParameter.AbiType, + genericParameter.TypeKind, + arg); + } + + public static string GetFromManagedMarshaler(string type, string abiType, TypeKind kind, string arg) + { + string marshalerType = GetMarshalerClass(type, abiType, kind, false); + if (kind == TypeKind.Enum || (kind == TypeKind.Struct && type == abiType)) + { + return arg; + } + else if (type == "bool") + { + return $$"""(byte)({{arg}} ? 1 : 0)"""; + } + else if (type == "char") + { + return $$"""(ushort){{arg}}"""; + } + else + { + return $$"""{{marshalerType}}.FromManaged({{arg}})"""; + } + } + + public static string GetCopyManagedArrayMarshaler(string type, string abiType, TypeKind kind, TypeFlags typeFlags) + { + if (kind == TypeKind.Class || kind == TypeKind.Delegate) + { + if (type is "System.String" || type is "string") return "global::WinRT.MarshalString"; + if (type is "System.Type") return "global::ABI.System.Type"; + if (type is "System.Object" || type is "object") return "global::WinRT.MarshalInspectable"; + + if (typeFlags.HasFlag(TypeFlags.Exception)) + { + return "global::WinRT.MarshalNonBlittable"; + } + + // Handles all other classes and delegate types + return $"global::WinRT.MarshalGenericHelper<{type}>"; + } + else + { + return GetMarshalerClass(type, abiType, kind, true); + } + } + + public static string GetCreateMarshaler(GenericParameter genericParameter, string arg) + { + return GetCreateMarshaler( + genericParameter.ProjectedType, + genericParameter.AbiType, + genericParameter.TypeKind, + arg); + } + + public static string GetCreateMarshaler(string type, string abiType, TypeKind kind, string arg) + { + if (kind == TypeKind.Enum || (kind == TypeKind.Struct && type == abiType) || + type == "bool" || + type == "char") + { + return ""; + } + else if (type == "System.String" || type == "string") + { + // TODO: Consider switching to pinning + return $$"""__{{arg}} = MarshalString.CreateMarshaler({{arg}});"""; + } + else if (kind == TypeKind.Struct) + { + string marshalerClass = GetMarshalerClass(type, abiType, kind, false); + return $$"""__{{arg}} = {{marshalerClass}}.CreateMarshaler({{arg}});"""; + } + else + { + string marshalerClass = GetMarshalerClass(type, abiType, kind, false); + return $$"""__{{arg}} = {{marshalerClass}}.CreateMarshaler2({{arg}});"""; + } + } + + public static string GetDisposeMarshaler(GenericParameter genericParameter, string arg) + { + return GetDisposeMarshaler( + genericParameter.ProjectedType, + genericParameter.AbiType, + genericParameter.TypeKind, + arg); + } + + public static string GetDisposeMarshaler(string type, string abiType, TypeKind kind, string arg) + { + if (kind == TypeKind.Enum || (kind == TypeKind.Struct && type == abiType) || + type == "bool" || + type == "char") + { + return ""; + } + else + { + string marshalerClass = GetMarshalerClass(type, abiType, kind, false, true); + return $$"""{{marshalerClass}}.DisposeMarshaler(__{{arg}});"""; + } + } + + public static string GetAbiFromMarshaler(GenericParameter genericParameter, string arg) + { + return GetAbiFromMarshaler( + genericParameter.ProjectedType, + genericParameter.AbiType, + genericParameter.TypeKind, + arg); + } + + public static string GetAbiFromMarshaler(string type, string abiType, TypeKind kind, string arg) + { + if (kind == TypeKind.Enum || (kind == TypeKind.Struct && type == abiType)) + { + return arg; + } + else if (type == "bool") + { + return $"(byte){arg}"; + } + else if (type == "char") + { + return $"(ushort){arg}"; + } + else + { + string marshalerClass = GetMarshalerClass(type, abiType, kind, false, true); + return $"{marshalerClass}.GetAbi(__{arg})"; + } + } + + public static string GetMarshalerDeclaration(GenericParameter genericParameter, string arg) + { + return GetMarshalerDeclaration( + genericParameter.ProjectedType, + genericParameter.AbiType, + genericParameter.TypeKind, + arg); + } + + public static string GetMarshalerDeclaration(string type, string abiType, TypeKind kind, string arg) + { + if (kind == TypeKind.Enum || (kind == TypeKind.Struct && type == abiType) || + type == "bool" || + type == "char") + { + return ""; + } + else if (kind == TypeKind.Struct) + { + return $"{GetAbiMarshalerType(type, abiType, kind, false)}.Marshaler __{arg} = default;"; + } + else + { + return $"{GetAbiMarshalerType(type, abiType, kind, false)} __{arg} = default;"; + } + } + + public static string GetAbiMarshalerType(string type, string abiType, TypeKind kind, bool isArray) + { + if (type == "System.String" || type == "string") + { + return isArray ? "MarshalString.MarshalerArray" : "MarshalString"; + } + else if (type == "System.Type" || type == "Type") + { + if (isArray) + { + return "MarshalNonBlittable.MarshalerArray"; + } + else + { + return "global::ABI.System.Type.Marshaler"; + } + } + else if (type.StartsWith("System.Collections.Generic.KeyValuePair<")) + { + return isArray ? $$"""MarshalInterfaceHelper<{{type}}>.MarshalerArray""" : "ObjectReferenceValue"; + } + else if (kind == TypeKind.Enum) + { + return isArray ? $$"""MarshalBlittable<{{type}}>.MarshalerArray""" : type; + } + else if (kind == TypeKind.Struct) + { + if (type == abiType) + { + return isArray ? $$"""MarshalBlittable<{{type}}>.MarshalerArray""" : type; + } + else + { + return isArray ? $$"""MarshalNonBlittable<{{type}}>.MarshalerArray""" : "ABI." + type; + } + } + else if (type == "System.Object" || type == "object" || kind == TypeKind.Class || kind == TypeKind.Interface || kind == TypeKind.Delegate) + { + return isArray ? $$"""MarshalInterfaceHelper<{{type}}>.MarshalerArray""" : "ObjectReferenceValue"; + } + + throw new ArgumentException(); + } + + public static string EscapeAssemblyNameForIdentifier(string typeName) + { + if (string.IsNullOrEmpty(typeName)) + { + return typeName; + } + + var prefixedTypeName = char.IsDigit(typeName[0]) ? "_" + typeName : typeName; + return Regex.Replace(prefixedTypeName, @"[^a-zA-Z0-9_]", "_"); + } + + public static string EscapeTypeNameForIdentifier(string typeName) + { + return Regex.Replace(typeName, """[(\ |:<>,\.\-@;+'^!`)]""", "_"); + } + + public readonly struct MappedType + { + private readonly string @namespace; + private readonly string name; + private readonly string assembly; + private readonly bool isSystemType; + private readonly bool isValueType; + private readonly bool isBlittable; + private readonly Func multipleMappingFunc; + + public MappedType(string @namespace, string name, string assembly, bool isValueType = false, bool isBlittable = false) + { + this.@namespace = @namespace; + this.name = name; + this.assembly = assembly; + isSystemType = string.CompareOrdinal(this.assembly, "mscorlib") == 0; + this.isValueType = isValueType; + this.isBlittable = isBlittable; + multipleMappingFunc = null; + } + + public MappedType(Func multipleMappingFunc) + { + @namespace = null; + name = null; + assembly = null; + isSystemType = false; + isValueType = false; + this.multipleMappingFunc = multipleMappingFunc; + } + + public (string, string, string, bool, bool) GetMapping(ISymbol containingType = null) + { + return multipleMappingFunc != null ? + multipleMappingFunc(containingType) : (@namespace, name, assembly, isSystemType, isValueType); + } + + public bool IsBlittable() + { + return isValueType && isBlittable; + } + } + + private static readonly Dictionary AsyncMethodToTaskAdapter = new() + { + // AsAsyncOperation is an extension method, due to that using the format of ReducedFrom. + { "System.WindowsRuntimeSystemExtensions.AsAsyncOperation(System.Threading.Tasks.Task)", "System.Threading.Tasks.TaskToAsyncOperationAdapter`1" }, + { "System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.Run(System.Func>)", "System.Threading.Tasks.TaskToAsyncOperationAdapter`1"}, + { "System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.FromResult(TResult)", "System.Threading.Tasks.TaskToAsyncOperationAdapter`1" }, + { "System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.FromException(System.Exception)", "System.Threading.Tasks.TaskToAsyncOperationAdapter`1" }, + { "System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.CanceledOperation()", "System.Threading.Tasks.TaskToAsyncOperationAdapter`1" }, + { "System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.Run(System.Func, System.Threading.Tasks.Task>)", "System.Threading.Tasks.TaskToAsyncOperationWithProgressAdapter`2" }, + { "System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.FromResultWithProgress(TResult)", "System.Threading.Tasks.TaskToAsyncOperationWithProgressAdapter`2" }, + { "System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.FromExceptionWithProgress(System.Exception)", "System.Threading.Tasks.TaskToAsyncOperationWithProgressAdapter`2" }, + { "System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.CanceledOperationWithProgress()", "System.Threading.Tasks.TaskToAsyncOperationWithProgressAdapter`2" }, + { "System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.Run(System.Func, System.Threading.Tasks.Task>)", "System.Threading.Tasks.TaskToAsyncActionWithProgressAdapter`1" }, + { "System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.CompletedActionWithProgress()", "System.Threading.Tasks.TaskToAsyncActionWithProgressAdapter`1" }, + { "System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.FromExceptionWithProgress(System.Exception)", "System.Threading.Tasks.TaskToAsyncActionWithProgressAdapter`1" }, + { "System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.CanceledActionWithProgress()", "System.Threading.Tasks.TaskToAsyncActionWithProgressAdapter`1" } + }; + + public static string GetTaskAdapterIfAsyncMethod(IMethodSymbol symbol) + { + var symbolStr = symbol.IsExtensionMethod ? symbol.ReducedFrom?.ToDisplayString() : symbol.OriginalDefinition?.ToDisplayString(); + if (!string.IsNullOrEmpty(symbolStr)) + { + if (AsyncMethodToTaskAdapter.TryGetValue(symbolStr, out var adapterTypeStr)) + { + return adapterTypeStr; + } + } + + return null; + } + + public static string TrimGlobalFromTypeName(string typeName) + { + return typeName.StartsWith("global::") ? typeName[8..] : typeName; + } + + public static string GetEscapedAssemblyName(string assemblyName) + { + // Make sure to escape invalid characters for namespace names. + // See ECMA 335, II.6.2 and II.5.2/3. + if (assemblyName.AsSpan().IndexOfAny("$@`?".AsSpan()) != -1) + { + char[] buffer = new char[assemblyName.Length]; + + for (int i = 0; i < assemblyName.Length; i++) + { + buffer[i] = assemblyName[i] is '$' or '@' or '`' or '?' + ? '_' + : assemblyName[i]; + } + + assemblyName = new string(buffer); + } + + return assemblyName; + } + + public static string GetAuthoringMetadataTypeName(string authoringTypeName) + { + return $"ABI.Impl.{authoringTypeName}"; + } } -} +} \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/Helpers/ArrayBufferWriter{T}.cs b/src/Authoring/WinRT.SourceGenerator/Helpers/ArrayBufferWriter{T}.cs new file mode 100644 index 000000000..d5b4e7844 --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator/Helpers/ArrayBufferWriter{T}.cs @@ -0,0 +1,246 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// Ported from 'ImmutableArrayBuilder' 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/Helpers/ImmutableArrayBuilder%7BT%7D.cs. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Runtime.CompilerServices; + +#nullable enable + +namespace Generator; + +/// +/// A helper type to build sequences of values with pooled buffers. +/// +/// The type of items to create sequences for. +internal sealed class ArrayBufferWriter : IList, IReadOnlyList +{ + /// + /// The underlying array. + /// + private T[] _array; + + /// + /// The starting offset within . + /// + private int _index; + + /// + /// Creates a new instance with the specified parameters. + /// + public ArrayBufferWriter() + { + if (typeof(T) == typeof(char)) + { + _array = new T[1024]; + } + else + { + _array = new T[8]; + } + + _index = 0; + } + + /// + public int Count => _index; + + /// + /// Gets the data written to the underlying buffer so far, as a . + /// + public ReadOnlySpan WrittenSpan + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new(_array, 0, _index); + } + + /// + bool ICollection.IsReadOnly => true; + + /// + T IReadOnlyList.this[int index] => WrittenSpan[index]; + + /// + T IList.this[int index] + { + get => WrittenSpan[index]; + set => throw new NotSupportedException(); + } + + /// + /// Advances the current writer and gets a to the requested memory area. + /// + /// The requested size to advance by. + /// A to the requested memory area. + /// + /// No other data should be written to the builder while the returned + /// is in use, as it could invalidate the memory area wrapped by it, if resizing occurs. + /// + public Span Advance(int requestedSize) + { + EnsureCapacity(requestedSize); + + Span span = _array.AsSpan(_index, requestedSize); + + _index += requestedSize; + + return span; + } + + /// + public void Add(T value) + { + EnsureCapacity(1); + + _array[_index++] = value; + } + + // + /// Adds the specified items to the end of the array. + /// + /// The items to add at the end of the array. + public void AddRange(ReadOnlySpan items) + { + EnsureCapacity(items.Length); + + items.CopyTo(_array.AsSpan(_index)); + + _index += items.Length; + } + + /// + /// Inserts an item to the builder at the specified index. + /// + /// The zero-based index at which should be inserted. + /// The object to insert into the current instance. + public void Insert(int index, T item) + { + if (index < 0 || index > _index) + { + ImmutableArrayBuilder.ThrowArgumentOutOfRangeExceptionForIndex(); + } + + EnsureCapacity(1); + + if (index < _index) + { + Array.Copy(_array, index, _array, index + 1, _index - index); + } + + _array[index] = item; + _index++; + } + + /// + /// Clears the items in the current writer. + /// + public void Clear() + { + if (typeof(T) != typeof(byte) && + typeof(T) != typeof(char) && + typeof(T) != typeof(int)) + { + _array.AsSpan(0, _index).Clear(); + } + + _index = 0; + } + + /// + /// Ensures that has enough free space to contain a given number of new items. + /// + /// The minimum number of items to ensure space for in . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void EnsureCapacity(int requestedSize) + { + if (requestedSize > _array.Length - _index) + { + ResizeBuffer(requestedSize); + } + } + + /// + /// Resizes to ensure it can fit the specified number of new items. + /// + /// The minimum number of items to ensure space for in . + [MethodImpl(MethodImplOptions.NoInlining)] + private void ResizeBuffer(int sizeHint) + { + int minimumSize = _index + sizeHint; + int requestedSize = Math.Max(_array.Length * 2, minimumSize); + + T[] newArray = new T[requestedSize]; + + Array.Copy(_array, newArray, _index); + + _array = newArray; + } + + /// + int IList.IndexOf(T item) + { + return Array.IndexOf(_array, item, 0, _index); + } + + /// + void IList.RemoveAt(int index) + { + throw new NotSupportedException(); + } + + /// + bool ICollection.Contains(T item) + { + return Array.IndexOf(_array, item, 0, _index) >= 0; + } + + /// + void ICollection.CopyTo(T[] array, int arrayIndex) + { + Array.Copy(_array, 0, array, arrayIndex, _index); + } + + /// + bool ICollection.Remove(T item) + { + throw new NotSupportedException(); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + T?[] array = _array!; + int length = _index; + + for (int i = 0; i < length; i++) + { + yield return array[i]!; + } + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)this).GetEnumerator(); + } +} + +/// +/// Private helpers for the type. +/// +file static class ImmutableArrayBuilder +{ + /// + /// Throws an for "index". + /// + public static void ThrowArgumentOutOfRangeExceptionForIndex() + { + throw new ArgumentOutOfRangeException("index"); + } +} \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/ImmutableEquatableArray.cs b/src/Authoring/WinRT.SourceGenerator/ImmutableEquatableArray.cs new file mode 100644 index 000000000..b3bbaa84d --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator/ImmutableEquatableArray.cs @@ -0,0 +1,189 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Runtime.CompilerServices; + +#nullable enable + +namespace WinRT.SourceGenerator +{ + + // Adapted from https://github.com/CommunityToolkit/dotnet/blob/main/src/CommunityToolkit.Mvvm.SourceGenerators/Helpers/EquatableArray%7BT%7D.cs + /// + /// An imutable, equatable array. This is equivalent to but with value equality support. + /// + /// The type of values in the array. + internal readonly struct EquatableArray : IEquatable>, IEnumerable + where T : IEquatable + { + /// + /// The underlying array. + /// + private readonly T[]? array; + + /// + /// Creates a new instance. + /// + /// The input to wrap. + public EquatableArray(ImmutableArray array) + { + this.array = Unsafe.As, T[]?>(ref array); + } + + /// + /// Gets a reference to an item at a specified position within the array. + /// + /// The index of the item to retrieve a reference to. + /// A reference to an item at a specified position within the array. + public ref readonly T this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => ref AsImmutableArray().ItemRef(index); + } + + /// + /// Gets a value indicating whether the current array is empty. + /// + public bool IsEmpty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => AsImmutableArray().IsEmpty; + } + + /// + public bool Equals(EquatableArray array) + { + return AsSpan().SequenceEqual(array.AsSpan()); + } + + /// + public override bool Equals([NotNullWhen(true)] object? obj) + { + return obj is EquatableArray array && Equals(this, array); + } + + /// + public override int GetHashCode() + { + if (this.array is not T[] array) + { + return 0; + } + + int hashCode = 0; + + foreach (T item in array) + { + hashCode = unchecked((hashCode * (int)0xA5555529) + item.GetHashCode()); + } + + return hashCode; + } + + /// + /// Gets an instance from the current . + /// + /// The from the current . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ImmutableArray AsImmutableArray() + { + return Unsafe.As>(ref Unsafe.AsRef(in this.array)); + } + + /// + /// Creates an instance from a given . + /// + /// The input instance. + /// An instance from a given . + public static EquatableArray FromImmutableArray(ImmutableArray array) + { + return new(array); + } + + /// + /// Returns a wrapping the current items. + /// + /// A wrapping the current items. + public ReadOnlySpan AsSpan() + { + return AsImmutableArray().AsSpan(); + } + + /// + /// Copies the contents of this instance. to a mutable array. + /// + /// The newly instantiated array. + public T[] ToArray() + { + return AsImmutableArray().ToArray(); + } + + /// + /// Gets an value to traverse items in the current array. + /// + /// An value to traverse items in the current array. + public ImmutableArray.Enumerator GetEnumerator() + { + return AsImmutableArray().GetEnumerator(); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)AsImmutableArray()).GetEnumerator(); + } + + /// + IEnumerator IEnumerable.GetEnumerator() + { + return ((IEnumerable)AsImmutableArray()).GetEnumerator(); + } + + /// + /// Implicitly converts an to . + /// + /// An instance from a given . + public static implicit operator EquatableArray(ImmutableArray array) + { + return FromImmutableArray(array); + } + + /// + /// Implicitly converts an to . + /// + /// An instance from a given . + public static implicit operator ImmutableArray(EquatableArray array) + { + return array.AsImmutableArray(); + } + + /// + /// Checks whether two values are the same. + /// + /// The first value. + /// The second value. + /// Whether and are equal. + public static bool operator ==(EquatableArray left, EquatableArray right) + { + return left.Equals(right); + } + + /// + /// Checks whether two values are not the same. + /// + /// The first value. + /// The second value. + /// Whether and are not equal. + public static bool operator !=(EquatableArray left, EquatableArray right) + { + return !left.Equals(right); + } + } +} diff --git a/src/Authoring/WinRT.SourceGenerator/Logger.cs b/src/Authoring/WinRT.SourceGenerator/Logger.cs index 579e1d8ab..c76e20ae8 100644 --- a/src/Authoring/WinRT.SourceGenerator/Logger.cs +++ b/src/Authoring/WinRT.SourceGenerator/Logger.cs @@ -1,10 +1,12 @@ using Microsoft.CodeAnalysis; +using System.Diagnostics.CodeAnalysis; using System.IO; namespace Generator { class Logger { + [SuppressMessage("MicrosoftCodeAnalysisCorrectness", "RS1035", Justification = "We need to do file IO to save the 'cswinrt' log file.")] public Logger(GeneratorExecutionContext context) { context.AnalyzerConfigOptions.GlobalOptions.TryGetValue("build_property.CsWinRTEnableLogging", out var enableLoggingStr); diff --git a/src/Authoring/WinRT.SourceGenerator/MergeReferencedActivationFactoriesGenerator.cs b/src/Authoring/WinRT.SourceGenerator/MergeReferencedActivationFactoriesGenerator.cs new file mode 100644 index 000000000..154057f0e --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator/MergeReferencedActivationFactoriesGenerator.cs @@ -0,0 +1,176 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Immutable; +using System.Diagnostics.CodeAnalysis; +using System.Linq; +using System.Text; +using System.Threading; +using Microsoft.CodeAnalysis; +using WinRT.SourceGenerator; + +#nullable enable + +namespace Generator; + +[Generator] +public sealed class MergeReferencedActivationFactoriesGenerator : IIncrementalGenerator +{ + /// + public void Initialize(IncrementalGeneratorInitializationContext context) + { + // Get whether the generator is enabled + IncrementalValueProvider isGeneratorEnabled = context.AnalyzerConfigOptionsProvider.Select(static (options, token) => + { + return options.GetCsWinRTMergeReferencedActivationFactories(); + }); + + // Get the fully qualified type names of all assembly exports types to merge + IncrementalValueProvider> assemblyExportsTypeNames = + context.CompilationProvider + .Combine(isGeneratorEnabled) + .Select(static (item, token) => + { + // Immediately bail if the generator is disabled + if (!item.Right) + { + return new EquatableArray(ImmutableArray.Empty); + } + + ImmutableArray.Builder builder = ImmutableArray.CreateBuilder(); + + foreach (MetadataReference metadataReference in item.Left.References) + { + token.ThrowIfCancellationRequested(); + + if (item.Left.GetAssemblyOrModuleSymbol(metadataReference) is not IAssemblySymbol assemblySymbol) + { + continue; + } + + token.ThrowIfCancellationRequested(); + + // Add the type name if the assembly is a WinRT component + if (TryGetDependentAssemblyExportsTypeName( + assemblySymbol, + item.Left, + token, + out string? name)) + { + builder.Add(name); + } + } + + token.ThrowIfCancellationRequested(); + + return new EquatableArray(builder.ToImmutable()); + }); + + // Generate the chaining helper + context.RegisterImplementationSourceOutput(assemblyExportsTypeNames, static (context, assemblyExportsTypeNames) => + { + if (assemblyExportsTypeNames.IsEmpty) + { + return; + } + + StringBuilder builder = new(); + + builder.AppendLine(""" + // + #pragma warning disable + + namespace WinRT + { + using global::System; + + /// + partial class Module + { + /// + /// Tries to retrieve the activation factory from all dependent WinRT components. + /// + /// The marshalled fully qualified type name of the activation factory to retrieve. + /// The pointer to the activation factory that corresponds with the class specified by . + internal static unsafe IntPtr TryGetDependentActivationFactory(ReadOnlySpan fullyQualifiedTypeName) + { + IntPtr obj; + + """); + + foreach (string assemblyExportsTypeName in assemblyExportsTypeNames) + { + builder.AppendLine($$""" + obj = global::{{assemblyExportsTypeName}}.GetActivationFactory(fullyQualifiedTypeName); + + if ((void*)obj is not null) + { + return obj; + } + + """); + } + + builder.AppendLine(""" + return default; + } + } + } + """); + + context.AddSource("ChainedExports.g.cs", builder.ToString()); + }); + } + + /// + /// Tries to get the name of a dependent WinRT component from a given assembly. + /// + /// The assembly symbol to analyze. + /// The instance to use. + /// The instance to use. + /// The resulting type name, if found. + /// Whether a type name was found. + internal static bool TryGetDependentAssemblyExportsTypeName( + IAssemblySymbol assemblySymbol, + Compilation compilation, + CancellationToken token, + [NotNullWhen(true)] out string? name) + { + // Get the attribute to lookup to find the target type to use + INamedTypeSymbol winRTAssemblyExportsTypeAttributeSymbol = compilation.GetTypeByMetadataName("WinRT.WinRTAssemblyExportsTypeAttribute")!; + + // Make sure the assembly does have the attribute on it + if (!assemblySymbol.TryGetAttributeWithType(winRTAssemblyExportsTypeAttributeSymbol, out AttributeData? attributeData)) + { + name = null; + + return false; + } + + token.ThrowIfCancellationRequested(); + + // Sanity check: we should have a valid type in the annotation + if (attributeData.ConstructorArguments is not [{ Kind: TypedConstantKind.Type, Value: INamedTypeSymbol assemblyExportsTypeSymbol }]) + { + name = null; + + return false; + } + + token.ThrowIfCancellationRequested(); + + // Other sanity check: this type should be accessible from this compilation + if (!assemblyExportsTypeSymbol.IsAccessibleFromCompilationAssembly(compilation)) + { + name = null; + + return false; + } + + token.ThrowIfCancellationRequested(); + + name = assemblyExportsTypeSymbol.ToDisplayString(); + + return true; + } +} diff --git a/src/Authoring/WinRT.SourceGenerator/Properties/launchSettings.json b/src/Authoring/WinRT.SourceGenerator/Properties/launchSettings.json new file mode 100644 index 000000000..ef608ed3b --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "WinRT.SourceGenerator": { + "commandName": "DebugRoslynComponent", + "targetProject": "..\\..\\Projections\\Windows\\Windows.csproj" + } + } +} \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/RcwReflectionFallbackGenerator.cs b/src/Authoring/WinRT.SourceGenerator/RcwReflectionFallbackGenerator.cs new file mode 100644 index 000000000..b481ca841 --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator/RcwReflectionFallbackGenerator.cs @@ -0,0 +1,339 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Text; +using Microsoft.CodeAnalysis; +using WinRT.SourceGenerator; + +#nullable enable + +namespace Generator; + +[Generator] +public sealed class RcwReflectionFallbackGenerator : IIncrementalGenerator +{ + /// + public void Initialize(IncrementalGeneratorInitializationContext context) + { + // Gather all PE references from the current compilation + IncrementalValuesProvider executableReferences = + context.CompilationProvider + .SelectMany(static (compilation, token) => + { + var executableReferences = ImmutableArray.CreateBuilder(); + + foreach (MetadataReference metadataReference in compilation.References) + { + // We are only interested in PE references (not project references) + if (metadataReference is not PortableExecutableReference executableReference) + { + continue; + } + + executableReferences.Add(new EquatablePortableExecutableReference(executableReference, compilation)); + } + + return executableReferences.ToImmutable(); + }); + + // Get whether the current project is an .exe + IncrementalValueProvider isOutputTypeExe = context.CompilationProvider.Select(static (compilation, token) => + { + return compilation.Options.OutputKind is OutputKind.ConsoleApplication or OutputKind.WindowsApplication or OutputKind.WindowsRuntimeApplication; + }); + + // Get whether the generator is explicitly set as opt-in + IncrementalValueProvider isGeneratorForceOptIn = context.AnalyzerConfigOptionsProvider.Select(static (options, token) => + { + return options.GetCsWinRTRcwFactoryFallbackGeneratorForceOptIn(); + }); + + // Get whether the generator is explicitly set as opt-out + IncrementalValueProvider isGeneratorForceOptOut = context.AnalyzerConfigOptionsProvider.Select(static (options, token) => + { + return options.GetCsWinRTRcwFactoryFallbackGeneratorForceOptOut(); + }); + + IncrementalValueProvider csWinRTAotWarningEnabled = context.AnalyzerConfigOptionsProvider.Select(static (options, token) => + { + return options.GetCsWinRTAotWarningLevel() >= 1; + }); + + // Get whether the generator should actually run or not + IncrementalValueProvider isGeneratorEnabled = + isOutputTypeExe + .Combine(isGeneratorForceOptIn) + .Combine(isGeneratorForceOptOut) + .Select(static (flags, token) => (flags.Left.Left || flags.Left.Right) && !flags.Right); + + // Bypass all items if the flag is not set + IncrementalValuesProvider<(EquatablePortableExecutableReference Value, bool)> enabledExecutableReferences = + executableReferences + .Combine(isGeneratorEnabled) + .Where(static item => item.Right); + + // Get all the names of the projected types to root + IncrementalValuesProvider> executableTypeNames = enabledExecutableReferences.Select(static (executableReference, token) => + { + Compilation compilation = executableReference.Value.GetCompilationUnsafe(); + + // We only care about resolved assembly symbols (this should always be the case anyway) + if (compilation.GetAssemblyOrModuleSymbol(executableReference.Value.Reference) is not IAssemblySymbol assemblySymbol) + { + return EquatableArray.FromImmutableArray(ImmutableArray.Empty); + } + + // If the assembly is not an old projections assembly, we have nothing to do + if (!GeneratorHelper.IsOldProjectionAssembly(assemblySymbol)) + { + return EquatableArray.FromImmutableArray(ImmutableArray.Empty); + } + + token.ThrowIfCancellationRequested(); + + ITypeSymbol attributeSymbol = compilation.GetTypeByMetadataName("System.Attribute")!; + ITypeSymbol windowsRuntimeTypeAttributeSymbol = compilation.GetTypeByMetadataName("WinRT.WindowsRuntimeTypeAttribute")!; + + ImmutableArray.Builder executableTypeNames = ImmutableArray.CreateBuilder(); + + // Process all type symbols in the current assembly + foreach (INamedTypeSymbol typeSymbol in VisitNamedTypeSymbolsExceptABI(assemblySymbol)) + { + token.ThrowIfCancellationRequested(); + + // We only care about public or internal classes + if (typeSymbol is not { TypeKind: TypeKind.Class, DeclaredAccessibility: Accessibility.Public or Accessibility.Internal }) + { + continue; + } + + // Ignore static types (we only care about actual RCW types we can instantiate) + if (typeSymbol.IsStatic) + { + continue; + } + + // Ignore attribute types (they're never instantiated like normal RCWs) + if (IsDerivedFromType(typeSymbol, attributeSymbol)) + { + continue; + } + + // If the type is not a generated projected type, do nothing + if (!GeneratorHelper.HasAttributeWithType(typeSymbol, windowsRuntimeTypeAttributeSymbol)) + { + continue; + } + + // Double check we can in fact access this type (or we can't reference it) + if (!compilation.IsSymbolAccessibleWithin(typeSymbol, compilation.Assembly)) + { + continue; + } + + var typeName = typeSymbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat); + + // These types are in the existing WinUI projection, but have been moved to the Windows SDK projection. + // So if we see those, we want to ignore them. + if (typeName == "global::Windows.UI.Text.ContentLinkInfo" || + typeName == "global::Windows.UI.Text.RichEditTextDocument" || + typeName == "global::Windows.UI.Text.RichEditTextRange") + { + continue; + } + + // Check if we are able to resolve the type using GetTypeByMetadataName. If not, + // it indicates there are multiple definitions of this type in the references + // and us emitting a dependency on this type would cause compiler error. So emit + // a warning instead. + bool hasMultipleDefinitions = compilation.GetTypeByMetadataName(GeneratorHelper.TrimGlobalFromTypeName(typeName)) is null; + executableTypeNames.Add(new RcwReflectionFallbackType(typeName, hasMultipleDefinitions)); + } + + token.ThrowIfCancellationRequested(); + + return EquatableArray.FromImmutableArray(executableTypeNames.ToImmutable()); + }); + + // Combine all names into a single sequence + IncrementalValueProvider<(ImmutableArray, bool)> projectedTypeNamesAndAotWarningEnabled = + executableTypeNames + .Where(static names => !names.IsEmpty) + .SelectMany(static (executableTypeNames, token) => executableTypeNames.AsImmutableArray()) + .Collect() + .Combine(csWinRTAotWarningEnabled); + + // Generate the [DynamicDependency] attributes + context.RegisterImplementationSourceOutput(projectedTypeNamesAndAotWarningEnabled, static (SourceProductionContext context, (ImmutableArray projectedTypeNames, bool csWinRTAotWarningEnabled) value) => + { + if (value.projectedTypeNames.IsEmpty) + { + return; + } + + StringBuilder builder = new(); + + builder.AppendLine(""" + // + #pragma warning disable + + namespace WinRT + { + using global::System.Runtime.CompilerServices; + using global::System.Diagnostics.CodeAnalysis; + + /// + /// Roots RCW types for assemblies referencing old projections. + /// It is recommended to update those, to get binary size savings. + /// + internal static class RcwFallbackInitializer + { + /// + /// Roots all dependent RCW types. + /// + [ModuleInitializer] + """); + + bool emittedDynamicDependency = false; + foreach (RcwReflectionFallbackType projectedTypeName in value.projectedTypeNames) + { + // If there are multiple definitions of the type, emitting a dependency would result in a compiler error. + // So instead, emit a diagnostic for it. + if (projectedTypeName.HasMultipleDefinitions) + { + var diagnosticDescriptor = value.csWinRTAotWarningEnabled ? + WinRTRules.ClassNotAotCompatibleOldProjectionMultipleInstancesWarning : WinRTRules.ClassNotAotCompatibleOldProjectionMultipleInstancesInfo; + // We have no location to emit the diagnostic as this is just a reference we detect. + context.ReportDiagnostic(Diagnostic.Create(diagnosticDescriptor, null, GeneratorHelper.TrimGlobalFromTypeName(projectedTypeName.TypeName))); + } + else + { + emittedDynamicDependency = true; + builder.Append(" [DynamicDependency(DynamicallyAccessedMemberTypes.NonPublicConstructors, typeof("); + builder.Append(projectedTypeName.TypeName); + builder.AppendLine("))]"); + } + } + + builder.Append(""" + public static void InitializeRcwFallback() + { + } + } + } + """); + + if (emittedDynamicDependency) + { + context.AddSource("RcwFallbackInitializer.g.cs", builder.ToString()); + } + }); + } + + /// + /// Visits all named type symbols in a given assembly, except for ABI types. + /// + /// The assembly to inspect. + /// All named type symbols in , except for ABI types. + private static IEnumerable VisitNamedTypeSymbolsExceptABI(IAssemblySymbol assemblySymbol) + { + static IEnumerable Visit(INamespaceOrTypeSymbol symbol) + { + foreach (ISymbol memberSymbol in symbol.GetMembers()) + { + // Visit the current symbol if it's a type symbol + if (memberSymbol is INamedTypeSymbol typeSymbol) + { + yield return typeSymbol; + } + else if (memberSymbol is INamespaceSymbol { Name: not ("ABI" or "WinRT") } namespaceSymbol) + { + // If the symbol is a namespace, also recurse (ignore the ABI namespaces) + foreach (INamedTypeSymbol nestedTypeSymbol in Visit(namespaceSymbol)) + { + yield return nestedTypeSymbol; + } + } + } + } + + return Visit(assemblySymbol.GlobalNamespace); + } + + /// + /// Checks whether a given type is derived from a specified type. + /// + /// The input instance to check. + /// The base type to look for. + /// Whether derives from . + private static bool IsDerivedFromType(ITypeSymbol typeSymbol, ITypeSymbol baseTypeSymbol) + { + for (ITypeSymbol? currentSymbol = typeSymbol.BaseType; + currentSymbol is { SpecialType: not SpecialType.System_Object }; + currentSymbol = currentSymbol.BaseType) + { + if (SymbolEqualityComparer.Default.Equals(currentSymbol, baseTypeSymbol)) + { + return true; + } + } + + return false; + } + + /// + /// An equatable type that weakly references a object. + /// + /// The object to wrap. + /// The instance where comes from. + public sealed class EquatablePortableExecutableReference( + PortableExecutableReference executableReference, + Compilation compilation) : IEquatable + { + /// + /// A weak reference to the object owning . + /// + private readonly WeakReference Compilation = new(compilation); + + /// + /// Gets the object for this instance. + /// + public PortableExecutableReference Reference { get; } = executableReference; + + /// + /// Gets the object for . + /// + /// The object for . + /// Thrown if the object has been collected. + /// + /// This method should only be used from incremental steps immediately following a change in the metadata reference + /// being used, as that would guarantee that that object would be alive. + /// + public Compilation GetCompilationUnsafe() + { + if (Compilation.TryGetTarget(out Compilation? compilation)) + { + return compilation; + } + + throw new InvalidOperationException("No compilation object is available."); + } + + /// + public bool Equals(EquatablePortableExecutableReference other) + { + if (other is null) + { + return false; + } + + return other.Reference.GetMetadataId() == Reference.GetMetadataId(); + } + } + + internal readonly record struct RcwReflectionFallbackType(string TypeName, bool HasMultipleDefinitions); +} diff --git a/src/Authoring/WinRT.SourceGenerator/ResX/de-DE/CsWinRTDiagnosticStrings.resx b/src/Authoring/WinRT.SourceGenerator/ResX/de-DE/CsWinRTDiagnosticStrings.resx index 9172e31f8..bf5675041 100644 --- a/src/Authoring/WinRT.SourceGenerator/ResX/de-DE/CsWinRTDiagnosticStrings.resx +++ b/src/Authoring/WinRT.SourceGenerator/ResX/de-DE/CsWinRTDiagnosticStrings.resx @@ -1,271 +1,271 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - Als InAttribute oder OutAttribute markierter Array-Parameter - - - Die Methode '{0}' hat einen Parameter '{1}', der ein Array ist und entweder ein System.Runtime.InteropServices.InAttribute oder ein System.Runtime.InteropServices.OutAttribute aufweist. - - - In Windows-Runtime müssen Arrayparameter entweder ReadOnlyArray oder WriteOnlyArray aufweisen. - - - Entfernen Sie diese Attribute, oder ersetzen Sie sie bei Bedarf durch das entsprechende Windows-Runtime-Attribut. - - - Array-Parameter mit „out“ und ReadOnlyArray gekennzeichnet - - - Die Methode '{0}' hat einen Ausgabeparameter '{1}', der ein Array ist, das jedoch das Attribut ReadOnlyArray aufweist. - - - In der Windows-Runtime sind die Inhalte von Ausgabearrays beschreibbar. Entfernen Sie das Attribut aus '{1}'. - - - Array-Parameter, der sowohl mit ReadOnlyArray als auch mit WriteOnlyArray gekennzeichnet ist - - - Die Methode '{0}' hat einen Parameter '{1}', der ein Array ist und sowohl ReadOnlyArray als auch WriteOnlyArray aufweist. - - - In der Windows-Runtime müssen die Parameter des Inhaltsarrays entweder lesbar oder beschreibbar sein. Entfernen Sie eines der Attribute aus '{1}'. - - - Arrayparameter nicht als ReadOnlyArray- oder WriteOnlyArray-Methode markiert - - - Die Methode '{0}' hat einen Parameter '{1}', bei dem es sich um ein Array handelt. - - - In Windows-Runtime muss der Inhalt von Array-Parametern entweder lesbar oder schreibbar sein; bitte wenden Sie entweder „ReadOnlyArray“ oder „WriteOnlyArray“ auf „{1}“ an. - - - Klassenkonstruktorregel - - - Klassen dürfen nicht mehrere Konstruktoren derselben Stelligkeit in der Windows-Runtime aufweisen. Die Klasse {0} hat mehrere {1}-Stelligkeitskonstruktoren. - - - Der Namespace ist vom Hauptnamespace (winmd) getrennt. - - - Ein öffentlicher Typ hat einen Namespace ('{1}'), der kein gemeinsames Präfix mit anderen Namespaces ('{0}') gemeinsam hat. - {1} and {0} will be some user-defined keyword - - - Alle Typen in einer Windows-Metadatendatei müssen in einem Unter-Namensraum des Namensraums vorhanden sein, der durch den Dateinamen impliziert wird. - "sub namespace" means a namespace defined within another namespace - - - Klasse (oder Schnittstelle) ist generisch - - - Typ {0} ist generisch, Windows-Runtime-Typen dürfen nicht generisch sein. - {0} will be some user-defined keyword - - - Es wurde eine Arraysignatur mit gezacktem Array gefunden. Dies ist kein gültiger WinRT-Typ. - - - Die Methode{0} hat ein verschachteltes Array vom Typ{1} in ihrer Signatur. Arrays in der Windows-Runtime-Methodensignatur können nicht geschachtelt werden. - - - Array-Signatur mit mehrdimensionalem Array gefunden, das kein gültiger Windows-Runtime-Typ ist - - - Die Methode „{0}“ weist ein mehrdimensionales Array vom Typ „{1}“ in ihrer Signatur auf. Arrays in Windows-Runtime-Methodensignaturen müssen eindimensional sein. - - - Es sollte nur eine Überladung als Standard festgelegt werden. - - - In der Klasse {2}: Mehrere {0}-parameter-Überladungen von „{1}“ sind mit Windows.Foundation.Metadata.DefaultOverloadAttribute versehen. - - - Das Attribut kann nur auf eine Überladung der Methode angewendet werden. - - - Namespace-Namen dürfen sich nicht nur durch Groß- und Kleinschreibung unterscheiden. - - - Es wurden mehrere Namespaces mit dem Namen "'{0}';" gefunden. Namespacenamen dürfen sich nicht nur in der Windows-Runtime - {0} will be some user-defined keyword - - - Mehrere Überladungen ohne DefaultOverload-Attribut gesehen - - - In Klasse {2}: Für die {0}-parameter-Überladungen von {1} muss genau eine Methode als Standardüberladung angegeben sein, indem sie mit Windows.Foundation.Metadata.DefaultOverloadAttribute decodiert wird. - - - Parameter (kein Arraytyp) mit InAttribute oder OutAttribute markiert - - - Die Methode „{0}“ hat den Parameter „{1}“ mit einem System.Runtime.InteropServices.InAttribute oder System.Runtime.InteropServices.OutAttribute.Windows-Runtime unterstützt das Markieren von Parametern mit System.Runtime.InteropServices.InAttribute oder System.Runtime.InteropServices.OutAttribute nicht. - - - Entfernen Sie ggf. System.Runtime.InteropServices.InAttribute, und ersetzen Sie stattdessen System.Runtime.InteropServices.OutAttribute durch den out-Modifizierer. - - - Nicht-Arrayparameter, der mit ReadOnlyArray oder WriteOnlyArray markiert ist - - - Die Methode '{0}' hat einen Parameter '{1}', der kein Array ist und entweder ein ReadOnlyArray-Attribut oder ein WriteOnlyArray-Attribut aufweist. - - - Windows-Runtime unterstützt nicht das Markieren von Nicht-Arrayparametern mit ReadOnlyArray oder WriteOnlyArray. - - - Ungültige übernommene Schnittstelle - - - Windows-Runtime Komponententyp kann {0} die Schnittstelle nicht {1} implementieren, da die Schnittstelle keine gültige Windows-Runtime Schnittstelle ist. - - - Es wurden keine öffentlichen Typen definiert. - - - Komponenten für Windows-Runtime müssen mindestens einen öffentlichen Typ aufweisen. - - - Operatorüberladung verfügbar gemacht - - - {0} ist eine Operatorüberladung, verwaltete Typen können keine Operatorüberladungen in Windows-Runtime bereitstellen. - - - Parameter-benannte Werte-Regel - - - Der Parametername{1} in der Methode{0} ist derselbe wie der Parametername des Rückgabewerts, der in der generierten C#/WinRT-Interop verwendet wird; verwenden Sie einen anderen Parameternamen. - - - Die Eigenschaft muss über einen öffentlichen Getter verfügen. - - - Die Eigenschaft „{0}“ verfügt über keine öffentliche Getter-Methode. Windows-Runtime unterstützt keine reinen Setter-Eigenschaften. - {0} will be some user-defined keyword - - - Als Verweis übergebener Parameter - - - Die Methode '{0}' hat einen Parameter, '{1}' als "ref" markiert ist; Verweisparameter sind in Windows-Runtime nicht zulässig. - - - Const-Feld in struct - - - Die Struktur {0} weist ein konstantes Feld auf. Konstanten können nur für Windows-Runtime Enumerationen verwendet werden. - - - Ungültiges Feld in der Struktur - - - Die Struktur {0} hat ein Feld vom Typ {1}; {1} ist kein gültiger Windows-Runtime Feldtyp. - - - Jedes Feld in einer Windows-Runtime-Struktur kann nur UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Boolean, String, Enum oder selbst eine Struktur sein. - - - Privates Feld in der Struktur - - - Die Struktur {0} weist ein nicht öffentliches Feld auf. Alle Felder müssen für Windows-Runtime-Strukturen öffentlich sein. - - - Leere Strukturregel - - - Struktur {0} enthält keine öffentlichen Felder. Windows-Runtime-Strukturen müssen mindestens ein öffentliches Feld enthalten. - - - Die Klasse implementiert eine Schnittstelle falsch. - - - Die '{0}' implementiert die Schnittstelle '{1}' nicht ordnungsgemäß, da '{2}' fehlt oder nicht öffentlich ist. - - - Die Klasse ist nicht versiegelt. - - - Das Exportieren nicht versiegelter Typen wird in CsWinRT nicht unterstützt. Markieren Sie den Typ {0} als versiegelt. - {0} will be some user-defined keyword - - - Verfügbarmachen eines nicht unterstützten Typs - - - Der Member '{0}' weist den Typ '{1}' in der Signatur auf. - {0} and {1} will be user-defined keywords - - - Der Typ „{1}“ ist kein gültiger Windows-Runtime-Typ. - {1} will be some user-defined keyword - - - Der Typ (oder seine generischen Parameter) implementiert jedoch Schnittstellen, die gültige Windows-Runtime-Typen sind. - - - Erwägen Sie, den Typ „{1}“ in der Member-Signatur in einen der folgenden Typen aus System.Collections.Generic zu ändern: {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 + + + Als InAttribute oder OutAttribute markierter Array-Parameter + + + Die Methode '{0}' hat einen Parameter '{1}', der ein Array ist und entweder ein System.Runtime.InteropServices.InAttribute oder ein System.Runtime.InteropServices.OutAttribute aufweist. + + + In Windows-Runtime müssen Arrayparameter entweder ReadOnlyArray oder WriteOnlyArray aufweisen. + + + Entfernen Sie diese Attribute, oder ersetzen Sie sie bei Bedarf durch das entsprechende Windows-Runtime-Attribut. + + + Array-Parameter mit „out“ und ReadOnlyArray gekennzeichnet + + + Die Methode '{0}' hat einen Ausgabeparameter '{1}', der ein Array ist, das jedoch das Attribut ReadOnlyArray aufweist. + + + In der Windows-Runtime sind die Inhalte von Ausgabearrays beschreibbar. Entfernen Sie das Attribut aus '{1}'. + + + Array-Parameter, der sowohl mit ReadOnlyArray als auch mit WriteOnlyArray gekennzeichnet ist + + + Die Methode '{0}' hat einen Parameter '{1}', der ein Array ist und sowohl ReadOnlyArray als auch WriteOnlyArray aufweist. + + + In der Windows-Runtime müssen die Parameter des Inhaltsarrays entweder lesbar oder beschreibbar sein. Entfernen Sie eines der Attribute aus '{1}'. + + + Arrayparameter nicht als ReadOnlyArray- oder WriteOnlyArray-Methode markiert + + + Die Methode '{0}' hat einen Parameter '{1}', bei dem es sich um ein Array handelt. + + + In Windows-Runtime muss der Inhalt von Array-Parametern entweder lesbar oder schreibbar sein; bitte wenden Sie entweder „ReadOnlyArray“ oder „WriteOnlyArray“ auf „{1}“ an. + + + Klassenkonstruktorregel + + + Klassen dürfen nicht mehrere Konstruktoren derselben Stelligkeit in der Windows-Runtime aufweisen. Die Klasse {0} hat mehrere {1}-Stelligkeitskonstruktoren. + + + Der Namespace ist vom Hauptnamespace (winmd) getrennt. + + + Ein öffentlicher Typ hat einen Namespace ('{1}'), der kein gemeinsames Präfix mit anderen Namespaces ('{0}') gemeinsam hat. + {1} and {0} will be some user-defined keyword + + + Alle Typen in einer Windows-Metadatendatei müssen in einem Unter-Namensraum des Namensraums vorhanden sein, der durch den Dateinamen impliziert wird. + "sub namespace" means a namespace defined within another namespace + + + Klasse (oder Schnittstelle) ist generisch + + + Typ {0} ist generisch, Windows-Runtime-Typen dürfen nicht generisch sein. + {0} will be some user-defined keyword + + + Es wurde eine Arraysignatur mit gezacktem Array gefunden. Dies ist kein gültiger WinRT-Typ. + + + Die Methode{0} hat ein verschachteltes Array vom Typ{1} in ihrer Signatur. Arrays in der Windows-Runtime-Methodensignatur können nicht geschachtelt werden. + + + Array-Signatur mit mehrdimensionalem Array gefunden, das kein gültiger Windows-Runtime-Typ ist + + + Die Methode „{0}“ weist ein mehrdimensionales Array vom Typ „{1}“ in ihrer Signatur auf. Arrays in Windows-Runtime-Methodensignaturen müssen eindimensional sein. + + + Es sollte nur eine Überladung als Standard festgelegt werden. + + + In der Klasse {2}: Mehrere {0}-parameter-Überladungen von „{1}“ sind mit Windows.Foundation.Metadata.DefaultOverloadAttribute versehen. + + + Das Attribut kann nur auf eine Überladung der Methode angewendet werden. + + + Namespace-Namen dürfen sich nicht nur durch Groß- und Kleinschreibung unterscheiden. + + + Es wurden mehrere Namespaces mit dem Namen "'{0}';" gefunden. Namespacenamen dürfen sich nicht nur in der Windows-Runtime + {0} will be some user-defined keyword + + + Mehrere Überladungen ohne DefaultOverload-Attribut gesehen + + + In Klasse {2}: Für die {0}-parameter-Überladungen von {1} muss genau eine Methode als Standardüberladung angegeben sein, indem sie mit Windows.Foundation.Metadata.DefaultOverloadAttribute decodiert wird. + + + Parameter (kein Arraytyp) mit InAttribute oder OutAttribute markiert + + + Die Methode „{0}“ hat den Parameter „{1}“ mit einem System.Runtime.InteropServices.InAttribute oder System.Runtime.InteropServices.OutAttribute.Windows-Runtime unterstützt das Markieren von Parametern mit System.Runtime.InteropServices.InAttribute oder System.Runtime.InteropServices.OutAttribute nicht. + + + Entfernen Sie ggf. System.Runtime.InteropServices.InAttribute, und ersetzen Sie stattdessen System.Runtime.InteropServices.OutAttribute durch den out-Modifizierer. + + + Nicht-Arrayparameter, der mit ReadOnlyArray oder WriteOnlyArray markiert ist + + + Die Methode '{0}' hat einen Parameter '{1}', der kein Array ist und entweder ein ReadOnlyArray-Attribut oder ein WriteOnlyArray-Attribut aufweist. + + + Windows-Runtime unterstützt nicht das Markieren von Nicht-Arrayparametern mit ReadOnlyArray oder WriteOnlyArray. + + + Ungültige übernommene Schnittstelle + + + Windows-Runtime Komponententyp kann {0} die Schnittstelle nicht {1} implementieren, da die Schnittstelle keine gültige Windows-Runtime Schnittstelle ist. + + + Es wurden keine öffentlichen Typen definiert. + + + Komponenten für Windows-Runtime müssen mindestens einen öffentlichen Typ aufweisen. + + + Operatorüberladung verfügbar gemacht + + + {0} ist eine Operatorüberladung, verwaltete Typen können keine Operatorüberladungen in Windows-Runtime bereitstellen. + + + Parameter-benannte Werte-Regel + + + Der Parametername{1} in der Methode{0} ist derselbe wie der Parametername des Rückgabewerts, der in der generierten C#/WinRT-Interop verwendet wird; verwenden Sie einen anderen Parameternamen. + + + Die Eigenschaft muss über einen öffentlichen Getter verfügen. + + + Die Eigenschaft „{0}“ verfügt über keine öffentliche Getter-Methode. Windows-Runtime unterstützt keine reinen Setter-Eigenschaften. + {0} will be some user-defined keyword + + + Als Verweis übergebener Parameter + + + Die Methode '{0}' hat einen Parameter, '{1}' als "ref" markiert ist; Verweisparameter sind in Windows-Runtime nicht zulässig. + + + Const-Feld in struct + + + Die Struktur {0} weist ein konstantes Feld auf. Konstanten können nur für Windows-Runtime Enumerationen verwendet werden. + + + Ungültiges Feld in der Struktur + + + Die Struktur {0} hat ein Feld vom Typ {1}; {1} ist kein gültiger Windows-Runtime Feldtyp. + + + Jedes Feld in einer Windows-Runtime-Struktur kann nur UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Boolean, String, Enum oder selbst eine Struktur sein. + + + Privates Feld in der Struktur + + + Die Struktur {0} weist ein nicht öffentliches Feld auf. Alle Felder müssen für Windows-Runtime-Strukturen öffentlich sein. + + + Leere Strukturregel + + + Struktur {0} enthält keine öffentlichen Felder. Windows-Runtime-Strukturen müssen mindestens ein öffentliches Feld enthalten. + + + Die Klasse implementiert eine Schnittstelle falsch. + + + Die '{0}' implementiert die Schnittstelle '{1}' nicht ordnungsgemäß, da '{2}' fehlt oder nicht öffentlich ist. + + + Die Klasse ist nicht versiegelt. + + + Das Exportieren nicht versiegelter Typen wird in CsWinRT nicht unterstützt. Markieren Sie den Typ {0} als versiegelt. + {0} will be some user-defined keyword + + + Verfügbarmachen eines nicht unterstützten Typs + + + Der Member '{0}' weist den Typ '{1}' in der Signatur auf. + {0} and {1} will be user-defined keywords + + + Der Typ „{1}“ ist kein gültiger Windows-Runtime-Typ. + {1} will be some user-defined keyword + + + Der Typ (oder seine generischen Parameter) implementiert jedoch Schnittstellen, die gültige Windows-Runtime-Typen sind. + + + Erwägen Sie, den Typ „{1}“ in der Member-Signatur in einen der folgenden Typen aus System.Collections.Generic zu ändern: {2}. + {1} and {2} will be keywords (types) from DotNet + \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/ResX/es-ES/CsWinRTDiagnosticStrings.resx b/src/Authoring/WinRT.SourceGenerator/ResX/es-ES/CsWinRTDiagnosticStrings.resx index e6f3106f7..54e82524c 100644 --- a/src/Authoring/WinRT.SourceGenerator/ResX/es-ES/CsWinRTDiagnosticStrings.resx +++ b/src/Authoring/WinRT.SourceGenerator/ResX/es-ES/CsWinRTDiagnosticStrings.resx @@ -1,271 +1,271 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - Parámetro de matriz marcado como InAttribute o OutAttribute - - - El método '{0}' tiene un parámetro '{1}' que es una matriz y que tiene System.Runtime.InteropServices.InAttribute o System.Runtime.InteropServices.OutAttribute. - - - En el Windows Runtime, los parámetros de matriz deben tener ReadOnlyArray o WriteOnlyArray. - - - Quite estos atributos o reemplácelos por el atributo de Windows Runtime adecuado si es necesario. - - - Parámetro de matriz marcado como `out` y ReadOnlyArray - - - El método '{0}' tiene un parámetro de salida '{1}' que es una matriz, pero que tiene el atributo ReadOnlyArray. - - - En el Windows Runtime, se puede escribir en el contenido de las matrices de salida. Quite el atributo de '{1}'. - - - Parámetro de matriz marcado como ReadOnlyArray y WriteOnlyArray - - - El método '{0}' tiene un parámetro '{1}' que es una matriz y que tiene ReadOnlyArray y WriteOnlyArray. - - - En el Windows Runtime, los parámetros de la matriz de contenido deben ser legibles o grabables. Quite uno de los atributos de '{1}'. - - - Parámetro de matriz no marcado como ReadOnlyArray o WriteOnlyArray - - - El método '{0}' tiene un parámetro '{1}' que es una matriz. - - - En el Windows Runtime, el contenido de los parámetros de matriz debe ser legible o grabable; aplique ReadOnlyArray o WriteOnlyArray a '{1}'. - - - Regla de constructor de clase - - - Las clases no pueden tener varios constructores de la misma aridad en el Windows Runtime, la clase {0} tiene varios constructores de aridad {1} - - - El espacio de nombres es independiente del espacio de nombres principal (winmd) - - - Un tipo público tiene un espacio de nombres ('{1}') que no comparte ningún prefijo común con otros espacios de nombres ('{0}'). - {1} and {0} will be some user-defined keyword - - - Todos los tipos de un archivo de metadatos de Windows deben existir en un subes namespace del espacio de nombres que implica el nombre de archivo. - "sub namespace" means a namespace defined within another namespace - - - La clase (o interfaz) es genérica - - - El tipo {0} es genérico, los tipos de Windows Runtime no pueden ser genéricos - {0} will be some user-defined keyword - - - Se encontró una firma de matriz con matriz escalonada, que no es un tipo WinRT válido - - - El método {0} tiene una matriz anidada de tipo {1} en su signatura; las matrices de Windows Runtime signatura de método no se pueden anidar - - - Se encontró una firma de matriz con una matriz multidimensional, que no es un tipo de Windows Runtime válido - - - El método '{0}' tiene una matriz multidimensional de tipo '{1}" en su firma; las matrices de métodos de firma de Windows Runtime deben ser unidimensionales - - - Solo se debe designar una sobrecarga como predeterminada - - - En la clase {2}: varias sobrecargas -parameter {0} de '{1}' se decoran con Windows.Foundation.Metadata.DefaultOverloadAttribute. - - - El atributo solo se puede aplicar a una sobrecarga del método. - - - Los nombres de espacio de nombres no pueden diferir solo en mayúsculas y minúsculas - - - Se encontraron varios espacios de nombres con el nombre '{0}'; los nombres de espacio de nombres no pueden diferir solo por mayúsculas y minúsculas en el Windows Runtime - {0} will be some user-defined keyword - - - Se han detectado varias sobrecargas sin el atributo DefaultOverload - - - En la clase {2}: las sobrecargas -parameter {0} de {1} deben tener exactamente un método especificado como sobrecarga predeterminada decorando con Windows.Foundation.Metadata.DefaultOverloadAttribute - - - Parámetro (no tipo de matriz) marcado como InAttribute o OutAttribute - - - El método '{0}' tiene un parámetro '{1}' con System.Runtime.InteropServices.InAttribute o System.Runtime.InteropServices.OutAttribute.Windows Runtime no admite marcar parámetros con System.Runtime.InteropServices.InAttribute o System.Runtime.InteropServices.OutAttribute. - - - Considere la posibilidad de quitar System.Runtime.InteropServices.InAttribute y reemplace System.Runtime.InteropServices.OutAttribute por el modificador 'out'. - - - Parámetro que no es de matriz marcado con ReadOnlyArray o WriteOnlyArray - - - El método '{0}' tiene un parámetro '{1}' que no es una matriz y que tiene un atributo ReadOnlyArray o un atributo WriteOnlyArray. - - - Windows Runtime no admite el marcado de parámetros que no son de matriz con ReadOnlyArray o WriteOnlyArray. - - - Interfaz heredada no válida - - - Windows Runtime tipo de componente {0} no puede implementar {1} de interfaz, ya que la interfaz no es una interfaz Windows Runtime válida - - - No hay tipos públicos definidos - - - Windows Runtime componentes deben tener al menos un tipo público - - - Sobrecarga de operador expuesta - - - {0} es una sobrecarga de operador, los tipos administrados no pueden exponer sobrecargas de operador en el Windows Runtime - - - Regla de valor con nombre de parámetro - - - El nombre de parámetro {1} en el método {0} es el mismo que el nombre del parámetro de valor devuelto usado en la interoperabilidad C#/WinRT generada; usar un nombre de parámetro diferente - - - La propiedad debe tener captador público - - - La propiedad '{0}' no tiene un método getter público. Windows Runtime no admite propiedades de solo establecedor. - {0} will be some user-defined keyword - - - Parámetro pasado por referencia - - - El método '{0}' tiene el parámetro '{1}' marcado como 'ref'; no se permiten parámetros de referencia en Windows Runtime - - - Campo Const en struct - - - La estructura {0} tiene un campo const: las constantes solo pueden aparecer en enumeraciones de Windows Runtime. - - - Campo no válido en struct - - - La estructura {0} tiene un campo de tipo {1}; {1} no es un tipo de campo Windows Runtime válido. - - - Cada campo de una estructura de Windows Runtime solo puede ser UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Boolean, String, Enum o, a su vez, una estructura. - - - Campo privado en struct - - - La estructura {0} tiene un campo no público. Todos los campos deben ser públicos para las estructuras Windows Runtime. - - - Regla de estructura vacía - - - La estructura {0} no contiene campos públicos. Windows Runtime estructuras deben contener al menos un campo público. - - - La clase implementa incorrectamente una interfaz - - - La clase '{0}' no implementa correctamente la interfaz '{1}' porque falta el miembro '{2}' o no es público - - - La clase no está cerrada - - - No se admite la exportación de tipos no cerrados en CsWinRT. Marque el tipo {0} como cerrado - {0} will be some user-defined keyword - - - Exponiendo el tipo no admitido - - - El miembro '{0}' tiene el tipo '{1}' en su signatura. - {0} and {1} will be user-defined keywords - - - El tipo '{1}' no es un tipo Windows Runtime válido. - {1} will be some user-defined keyword - - - Sin embargo, el tipo (o sus parámetros genéricos) implementan interfaces que son tipos de Windows Runtime válidos. - - - Considere la posibilidad de cambiar el tipo '{1} de la firma de miembro a uno de los siguientes tipos de 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 + + + Parámetro de matriz marcado como InAttribute o OutAttribute + + + El método '{0}' tiene un parámetro '{1}' que es una matriz y que tiene System.Runtime.InteropServices.InAttribute o System.Runtime.InteropServices.OutAttribute. + + + En el Windows Runtime, los parámetros de matriz deben tener ReadOnlyArray o WriteOnlyArray. + + + Quite estos atributos o reemplácelos por el atributo de Windows Runtime adecuado si es necesario. + + + Parámetro de matriz marcado como `out` y ReadOnlyArray + + + El método '{0}' tiene un parámetro de salida '{1}' que es una matriz, pero que tiene el atributo ReadOnlyArray. + + + En el Windows Runtime, se puede escribir en el contenido de las matrices de salida. Quite el atributo de '{1}'. + + + Parámetro de matriz marcado como ReadOnlyArray y WriteOnlyArray + + + El método '{0}' tiene un parámetro '{1}' que es una matriz y que tiene ReadOnlyArray y WriteOnlyArray. + + + En el Windows Runtime, los parámetros de la matriz de contenido deben ser legibles o grabables. Quite uno de los atributos de '{1}'. + + + Parámetro de matriz no marcado como ReadOnlyArray o WriteOnlyArray + + + El método '{0}' tiene un parámetro '{1}' que es una matriz. + + + En el Windows Runtime, el contenido de los parámetros de matriz debe ser legible o grabable; aplique ReadOnlyArray o WriteOnlyArray a '{1}'. + + + Regla de constructor de clase + + + Las clases no pueden tener varios constructores de la misma aridad en el Windows Runtime, la clase {0} tiene varios constructores de aridad {1} + + + El espacio de nombres es independiente del espacio de nombres principal (winmd) + + + Un tipo público tiene un espacio de nombres ('{1}') que no comparte ningún prefijo común con otros espacios de nombres ('{0}'). + {1} and {0} will be some user-defined keyword + + + Todos los tipos de un archivo de metadatos de Windows deben existir en un subes namespace del espacio de nombres que implica el nombre de archivo. + "sub namespace" means a namespace defined within another namespace + + + La clase (o interfaz) es genérica + + + El tipo {0} es genérico, los tipos de Windows Runtime no pueden ser genéricos + {0} will be some user-defined keyword + + + Se encontró una firma de matriz con matriz escalonada, que no es un tipo WinRT válido + + + El método {0} tiene una matriz anidada de tipo {1} en su signatura; las matrices de Windows Runtime signatura de método no se pueden anidar + + + Se encontró una firma de matriz con una matriz multidimensional, que no es un tipo de Windows Runtime válido + + + El método '{0}' tiene una matriz multidimensional de tipo '{1}" en su firma; las matrices de métodos de firma de Windows Runtime deben ser unidimensionales + + + Solo se debe designar una sobrecarga como predeterminada + + + En la clase {2}: varias sobrecargas -parameter {0} de '{1}' se decoran con Windows.Foundation.Metadata.DefaultOverloadAttribute. + + + El atributo solo se puede aplicar a una sobrecarga del método. + + + Los nombres de espacio de nombres no pueden diferir solo en mayúsculas y minúsculas + + + Se encontraron varios espacios de nombres con el nombre '{0}'; los nombres de espacio de nombres no pueden diferir solo por mayúsculas y minúsculas en el Windows Runtime + {0} will be some user-defined keyword + + + Se han detectado varias sobrecargas sin el atributo DefaultOverload + + + En la clase {2}: las sobrecargas -parameter {0} de {1} deben tener exactamente un método especificado como sobrecarga predeterminada decorando con Windows.Foundation.Metadata.DefaultOverloadAttribute + + + Parámetro (no tipo de matriz) marcado como InAttribute o OutAttribute + + + El método '{0}' tiene un parámetro '{1}' con System.Runtime.InteropServices.InAttribute o System.Runtime.InteropServices.OutAttribute.Windows Runtime no admite marcar parámetros con System.Runtime.InteropServices.InAttribute o System.Runtime.InteropServices.OutAttribute. + + + Considere la posibilidad de quitar System.Runtime.InteropServices.InAttribute y reemplace System.Runtime.InteropServices.OutAttribute por el modificador 'out'. + + + Parámetro que no es de matriz marcado con ReadOnlyArray o WriteOnlyArray + + + El método '{0}' tiene un parámetro '{1}' que no es una matriz y que tiene un atributo ReadOnlyArray o un atributo WriteOnlyArray. + + + Windows Runtime no admite el marcado de parámetros que no son de matriz con ReadOnlyArray o WriteOnlyArray. + + + Interfaz heredada no válida + + + Windows Runtime tipo de componente {0} no puede implementar {1} de interfaz, ya que la interfaz no es una interfaz Windows Runtime válida + + + No hay tipos públicos definidos + + + Windows Runtime componentes deben tener al menos un tipo público + + + Sobrecarga de operador expuesta + + + {0} es una sobrecarga de operador, los tipos administrados no pueden exponer sobrecargas de operador en el Windows Runtime + + + Regla de valor con nombre de parámetro + + + El nombre de parámetro {1} en el método {0} es el mismo que el nombre del parámetro de valor devuelto usado en la interoperabilidad C#/WinRT generada; usar un nombre de parámetro diferente + + + La propiedad debe tener captador público + + + La propiedad '{0}' no tiene un método getter público. Windows Runtime no admite propiedades de solo establecedor. + {0} will be some user-defined keyword + + + Parámetro pasado por referencia + + + El método '{0}' tiene el parámetro '{1}' marcado como 'ref'; no se permiten parámetros de referencia en Windows Runtime + + + Campo Const en struct + + + La estructura {0} tiene un campo const: las constantes solo pueden aparecer en enumeraciones de Windows Runtime. + + + Campo no válido en struct + + + La estructura {0} tiene un campo de tipo {1}; {1} no es un tipo de campo Windows Runtime válido. + + + Cada campo de una estructura de Windows Runtime solo puede ser UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Boolean, String, Enum o, a su vez, una estructura. + + + Campo privado en struct + + + La estructura {0} tiene un campo no público. Todos los campos deben ser públicos para las estructuras Windows Runtime. + + + Regla de estructura vacía + + + La estructura {0} no contiene campos públicos. Windows Runtime estructuras deben contener al menos un campo público. + + + La clase implementa incorrectamente una interfaz + + + La clase '{0}' no implementa correctamente la interfaz '{1}' porque falta el miembro '{2}' o no es público + + + La clase no está cerrada + + + No se admite la exportación de tipos no cerrados en CsWinRT. Marque el tipo {0} como cerrado + {0} will be some user-defined keyword + + + Exponiendo el tipo no admitido + + + El miembro '{0}' tiene el tipo '{1}' en su signatura. + {0} and {1} will be user-defined keywords + + + El tipo '{1}' no es un tipo Windows Runtime válido. + {1} will be some user-defined keyword + + + Sin embargo, el tipo (o sus parámetros genéricos) implementan interfaces que son tipos de Windows Runtime válidos. + + + Considere la posibilidad de cambiar el tipo '{1} de la firma de miembro a uno de los siguientes tipos de System.Collections.Generic: {2}. + {1} and {2} will be keywords (types) from DotNet + \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/ResX/fr-FR/CsWinRTDiagnosticStrings.resx b/src/Authoring/WinRT.SourceGenerator/ResX/fr-FR/CsWinRTDiagnosticStrings.resx index cae843779..9b830d509 100644 --- a/src/Authoring/WinRT.SourceGenerator/ResX/fr-FR/CsWinRTDiagnosticStrings.resx +++ b/src/Authoring/WinRT.SourceGenerator/ResX/fr-FR/CsWinRTDiagnosticStrings.resx @@ -1,271 +1,271 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - Paramètre de tableau marqué InAttribute ou OutAttribute - - - La méthode '{0}' a un paramètre '{1}' qui est un tableau et qui a un System.Runtime.InteropServices.InAttribute ou system.Runtime.InteropServices.OutAttribute. - - - Dans le Windows Runtime, les paramètres de tableau doivent avoir ReadOnlyArray ou WriteOnlyArray. - - - Supprimez ces attributs ou remplacez-les par l’attribut Windows Runtime approprié si nécessaire. - - - Paramètre de tableau marqué `out` et ReadOnlyArray - - - La méthode '{0}' a un paramètre de sortie '{1}' qui est un tableau, mais qui a l’attribut ReadOnlyArray. - - - Dans le Windows Runtime, le contenu des tableaux de sortie est accessible en écriture. Supprimez l’attribut de '{1}'. - - - Paramètre de tableau marqué à la fois comme ReadOnlyArray et WriteOnlyArray - - - La méthode '{0}' a un paramètre '{1}' qui est un tableau et qui a ReadOnlyArray et WriteOnlyArray. - - - Dans le Windows Runtime, les paramètres du tableau de contenu doivent être lisibles ou accessibles en écriture. Supprimez l’un des attributs de '{1}'. - - - Paramètre de tableau non marqué avec ReadOnlyArray ou WriteOnlyArray - - - La méthode '{0}' a un paramètre '{1}' qui est un tableau. - - - Dans le Windows Runtime, le contenu des paramètres de tableau doit être lisible ou accessible en écriture ; appliquez ReadOnlyArray ou WriteOnlyArray à '{1}'. - - - Règle de constructeur de classe - - - Les classes ne peuvent pas avoir plusieurs constructeurs de la même arité dans le Windows Runtime, la classe {0} a plusieurs constructeurs d’arité {1} - - - L’espace de noms est disjoint de l’espace de noms principal (winmd) - - - Un type public a un espace de noms ('{1}') qui ne partage aucun préfixe commun avec d’autres espaces de noms ('{0}'). - {1} and {0} will be some user-defined keyword - - - Tous les types d’un fichier de métadonnées Windows doivent exister dans un sous-espace de noms de l’espace de noms qui est impliqué par le nom de fichier. - "sub namespace" means a namespace defined within another namespace - - - La classe (ou l’interface) est générique - - - Le type {0} est générique, les types Windows Runtime ne peuvent pas être génériques - {0} will be some user-defined keyword - - - Signature de tableau trouvée avec un tableau en escalier, qui n’est pas un type WinRT valide - - - La méthode {0} a un tableau imbriqué de type {1} dans sa signature ; les tableaux dans Windows Runtime signature de méthode ne peuvent pas être imbriqués - - - Signature de tableau trouvée avec un tableau multidimensionnel, qui n’est pas un type Windows Runtime valide - - - La méthode « {0} » possède un tableau multidimensionnel de type « {1} » dans sa signature ; les tableaux dans les signatures de méthode Windows Runtime doivent être unidimensionnels - - - Une seule surcharge doit être désignée par défaut - - - Dans la classe {2} : plusieurs surcharges de {0}-paramètre de « {1} » sont décorées avec Windows.Foundation.Metadata.DefaultOverloadAttribute. - - - L’attribut ne peut être appliqué qu’à une seule surcharge de la méthode. - - - Les noms d’espaces de noms ne peuvent pas différer uniquement par la casse - - - Plusieurs espaces de noms portant le nom '{0}'; les noms d’espace de noms ne peuvent pas différer uniquement par la casse dans le Windows Runtime - {0} will be some user-defined keyword - - - Surcharges multiples rencontrées sans attribut DefaultOverload - - - Dans la classe {2} : les surcharges de {0}-paramètre de {1} doivent avoir exactement une méthode spécifiée comme surcharge par défaut en la décorant avec Windows.Foundation.Metadata.DefaultOverloadAttribute - - - Paramètre (non de type de tableau) marqué InAttribute ou OutAttribute - - - La méthode « {0} » a un paramètre « {1} » avec System.Runtime.InteropServices.InAttribute ou System.Runtime.InteropServices.OutAttribute.Windows Runtime ne prend pas en charge le marquage de paramètres avec System.Runtime.InteropServices.InAttribute ou System.Runtime.InteropServices.OutAttribute. - - - Supprimez System.Runtime.InteropServices.InAttribute et remplacez System.Runtime.InteropServices.OutAttribute par le modificateur 'out' à la place. - - - Paramètre autre qu’un tableau marqué avec ReadOnlyArray ou WriteOnlyArray - - - La méthode '{0}' a un paramètre '{1}' qui n’est pas un tableau et qui possède un attribut ReadOnlyArray ou WriteOnlyArray. - - - Windows Runtime ne prend pas en charge le marquage de paramètres autres que tableau avec ReadOnlyArray ou WriteOnlyArray. - - - Interface non valide héritée - - - Le type de composant Windows Runtime {0} ne peut pas implémenter l’interface {1}, car l’interface n’est pas une interface Windows Runtime valide - - - Aucun type public défini - - - Les composants Windows Runtime doivent avoir au moins un type public - - - Surcharge d’opérateur exposée - - - {0} est une surcharge d’opérateur, les types managés ne peuvent pas exposer de surcharges d’opérateur dans le Windows Runtime - - - Règle de valeur nommée du paramètre - - - Le nom de paramètre {1} dans la méthode {0} est identique au nom du paramètre de valeur de retour utilisé dans l’interopérabilité C#/WinRT générée ; utiliser un autre nom de paramètre - - - La propriété doit avoir une méthode getter publique - - - La propriété '{0}' n’a pas de méthode getter publique. Windows Runtime ne prend pas en charge les propriétés setter uniquement. - {0} will be some user-defined keyword - - - Paramètre passé par référence - - - La méthode '{0}' a un paramètre '{1}' marqué 'ref'; les paramètres de référence ne sont pas autorisés dans Windows Runtime - - - Champ Const dans struct - - - Le {0} de structure a un champ const - les constantes ne peuvent apparaître que sur Windows Runtime énumérations. - - - Champ non valide dans struct - - - Le {0} de structure a un champ de type {1}; {1} n’est pas un type de champ Windows Runtime valide. - - - Chaque champ d’une structure Windows Runtime peut uniquement être UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Boolean, String, Enum ou lui-même une structure. - - - Champ privé dans struct - - - Le {0} de structure a un champ non public. Tous les champs doivent être publics pour les structures Windows Runtime. - - - Règle de struct vide - - - La structure {0} ne contient aucun champ public. Windows Runtime structures doivent contenir au moins un champ public. - - - La classe implémente de manière incorrecte une interface - - - Le '{0}' de classe n’implémente pas correctement les '{1}' d’interface, car le '{2}' membre est manquant ou n’est pas public - - - La classe n’est pas scellée - - - L’exportation de types non scellés n’est pas prise en charge dans CsWinRT. Marquez le type {0} comme scellé - {0} will be some user-defined keyword - - - Exposition du type non prise en charge - - - Le membre '{0}' a le type '{1}' dans sa signature. - {0} and {1} will be user-defined keywords - - - Le type '{1}' n’est pas un type Windows Runtime valide. - {1} will be some user-defined keyword - - - Cependant, le type (ou ses paramètres génériques) implémente des interfaces qui sont des types Windows Runtime valides. - - - Changez le type « {1} » dans la signature de membre en l’un des types suivants à partir de 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 + + + Paramètre de tableau marqué InAttribute ou OutAttribute + + + La méthode '{0}' a un paramètre '{1}' qui est un tableau et qui a un System.Runtime.InteropServices.InAttribute ou system.Runtime.InteropServices.OutAttribute. + + + Dans le Windows Runtime, les paramètres de tableau doivent avoir ReadOnlyArray ou WriteOnlyArray. + + + Supprimez ces attributs ou remplacez-les par l’attribut Windows Runtime approprié si nécessaire. + + + Paramètre de tableau marqué `out` et ReadOnlyArray + + + La méthode '{0}' a un paramètre de sortie '{1}' qui est un tableau, mais qui a l’attribut ReadOnlyArray. + + + Dans le Windows Runtime, le contenu des tableaux de sortie est accessible en écriture. Supprimez l’attribut de '{1}'. + + + Paramètre de tableau marqué à la fois comme ReadOnlyArray et WriteOnlyArray + + + La méthode '{0}' a un paramètre '{1}' qui est un tableau et qui a ReadOnlyArray et WriteOnlyArray. + + + Dans le Windows Runtime, les paramètres du tableau de contenu doivent être lisibles ou accessibles en écriture. Supprimez l’un des attributs de '{1}'. + + + Paramètre de tableau non marqué avec ReadOnlyArray ou WriteOnlyArray + + + La méthode '{0}' a un paramètre '{1}' qui est un tableau. + + + Dans le Windows Runtime, le contenu des paramètres de tableau doit être lisible ou accessible en écriture ; appliquez ReadOnlyArray ou WriteOnlyArray à '{1}'. + + + Règle de constructeur de classe + + + Les classes ne peuvent pas avoir plusieurs constructeurs de la même arité dans le Windows Runtime, la classe {0} a plusieurs constructeurs d’arité {1} + + + L’espace de noms est disjoint de l’espace de noms principal (winmd) + + + Un type public a un espace de noms ('{1}') qui ne partage aucun préfixe commun avec d’autres espaces de noms ('{0}'). + {1} and {0} will be some user-defined keyword + + + Tous les types d’un fichier de métadonnées Windows doivent exister dans un sous-espace de noms de l’espace de noms qui est impliqué par le nom de fichier. + "sub namespace" means a namespace defined within another namespace + + + La classe (ou l’interface) est générique + + + Le type {0} est générique, les types Windows Runtime ne peuvent pas être génériques + {0} will be some user-defined keyword + + + Signature de tableau trouvée avec un tableau en escalier, qui n’est pas un type WinRT valide + + + La méthode {0} a un tableau imbriqué de type {1} dans sa signature ; les tableaux dans Windows Runtime signature de méthode ne peuvent pas être imbriqués + + + Signature de tableau trouvée avec un tableau multidimensionnel, qui n’est pas un type Windows Runtime valide + + + La méthode « {0} » possède un tableau multidimensionnel de type « {1} » dans sa signature ; les tableaux dans les signatures de méthode Windows Runtime doivent être unidimensionnels + + + Une seule surcharge doit être désignée par défaut + + + Dans la classe {2} : plusieurs surcharges de {0}-paramètre de « {1} » sont décorées avec Windows.Foundation.Metadata.DefaultOverloadAttribute. + + + L’attribut ne peut être appliqué qu’à une seule surcharge de la méthode. + + + Les noms d’espaces de noms ne peuvent pas différer uniquement par la casse + + + Plusieurs espaces de noms portant le nom '{0}'; les noms d’espace de noms ne peuvent pas différer uniquement par la casse dans le Windows Runtime + {0} will be some user-defined keyword + + + Surcharges multiples rencontrées sans attribut DefaultOverload + + + Dans la classe {2} : les surcharges de {0}-paramètre de {1} doivent avoir exactement une méthode spécifiée comme surcharge par défaut en la décorant avec Windows.Foundation.Metadata.DefaultOverloadAttribute + + + Paramètre (non de type de tableau) marqué InAttribute ou OutAttribute + + + La méthode « {0} » a un paramètre « {1} » avec System.Runtime.InteropServices.InAttribute ou System.Runtime.InteropServices.OutAttribute.Windows Runtime ne prend pas en charge le marquage de paramètres avec System.Runtime.InteropServices.InAttribute ou System.Runtime.InteropServices.OutAttribute. + + + Supprimez System.Runtime.InteropServices.InAttribute et remplacez System.Runtime.InteropServices.OutAttribute par le modificateur 'out' à la place. + + + Paramètre autre qu’un tableau marqué avec ReadOnlyArray ou WriteOnlyArray + + + La méthode '{0}' a un paramètre '{1}' qui n’est pas un tableau et qui possède un attribut ReadOnlyArray ou WriteOnlyArray. + + + Windows Runtime ne prend pas en charge le marquage de paramètres autres que tableau avec ReadOnlyArray ou WriteOnlyArray. + + + Interface non valide héritée + + + Le type de composant Windows Runtime {0} ne peut pas implémenter l’interface {1}, car l’interface n’est pas une interface Windows Runtime valide + + + Aucun type public défini + + + Les composants Windows Runtime doivent avoir au moins un type public + + + Surcharge d’opérateur exposée + + + {0} est une surcharge d’opérateur, les types managés ne peuvent pas exposer de surcharges d’opérateur dans le Windows Runtime + + + Règle de valeur nommée du paramètre + + + Le nom de paramètre {1} dans la méthode {0} est identique au nom du paramètre de valeur de retour utilisé dans l’interopérabilité C#/WinRT générée ; utiliser un autre nom de paramètre + + + La propriété doit avoir une méthode getter publique + + + La propriété '{0}' n’a pas de méthode getter publique. Windows Runtime ne prend pas en charge les propriétés setter uniquement. + {0} will be some user-defined keyword + + + Paramètre passé par référence + + + La méthode '{0}' a un paramètre '{1}' marqué 'ref'; les paramètres de référence ne sont pas autorisés dans Windows Runtime + + + Champ Const dans struct + + + Le {0} de structure a un champ const - les constantes ne peuvent apparaître que sur Windows Runtime énumérations. + + + Champ non valide dans struct + + + Le {0} de structure a un champ de type {1}; {1} n’est pas un type de champ Windows Runtime valide. + + + Chaque champ d’une structure Windows Runtime peut uniquement être UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Boolean, String, Enum ou lui-même une structure. + + + Champ privé dans struct + + + Le {0} de structure a un champ non public. Tous les champs doivent être publics pour les structures Windows Runtime. + + + Règle de struct vide + + + La structure {0} ne contient aucun champ public. Windows Runtime structures doivent contenir au moins un champ public. + + + La classe implémente de manière incorrecte une interface + + + Le '{0}' de classe n’implémente pas correctement les '{1}' d’interface, car le '{2}' membre est manquant ou n’est pas public + + + La classe n’est pas scellée + + + L’exportation de types non scellés n’est pas prise en charge dans CsWinRT. Marquez le type {0} comme scellé + {0} will be some user-defined keyword + + + Exposition du type non prise en charge + + + Le membre '{0}' a le type '{1}' dans sa signature. + {0} and {1} will be user-defined keywords + + + Le type '{1}' n’est pas un type Windows Runtime valide. + {1} will be some user-defined keyword + + + Cependant, le type (ou ses paramètres génériques) implémente des interfaces qui sont des types Windows Runtime valides. + + + Changez le type « {1} » dans la signature de membre en l’un des types suivants à partir de System.Collections.Generic : {2}. + {1} and {2} will be keywords (types) from DotNet + \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/ResX/it-IT/CsWinRTDiagnosticStrings.resx b/src/Authoring/WinRT.SourceGenerator/ResX/it-IT/CsWinRTDiagnosticStrings.resx index 4cf988ab9..84e2e9140 100644 --- a/src/Authoring/WinRT.SourceGenerator/ResX/it-IT/CsWinRTDiagnosticStrings.resx +++ b/src/Authoring/WinRT.SourceGenerator/ResX/it-IT/CsWinRTDiagnosticStrings.resx @@ -1,271 +1,271 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - Parametro di matrice contrassegnato come InAttribute o OutAttribute - - - Il metodo '{0}' ha un parametro '{1}' che è una matrice e che contiene System.Runtime.InteropServices.InAttribute o System.Runtime.InteropServices.OutAttribute. - - - Nel Windows Runtime i parametri di matrice devono avere ReadOnlyArray o WriteOnlyArray. - - - Rimuovere questi attributi o sostituirli con l'attributo Windows Runtime appropriato, se necessario. - - - Parametro di matrice contrassegnato come 'out' e ReadOnlyArray - - - Il metodo '{0}' dispone di un parametro di output '{1}' che è una matrice, ma che ha l'attributo ReadOnlyArray. - - - Nel Windows Runtime, il contenuto delle matrici di output è scrivibile. Rimuovere l'attributo dal '{1}'. - - - Parametro di matrice contrassegnato sia come ReadOnlyArray che come WriteOnlyArray - - - Il metodo '{0}' ha un parametro '{1}' che è una matrice e che contiene sia ReadOnlyArray che WriteOnlyArray. - - - Nel Windows Runtime i parametri della matrice del contenuto devono essere leggibili o scrivibili. Rimuovere uno degli attributi dal '{1}'. - - - Parametro di matrice non contrassegnato come ReadOnlyArray o WriteOnlyArray - - - Il metodo '{0}' contiene un '{1}' di parametro che è una matrice. - - - Nel Windows Runtime, il contenuto dei parametri della matrice deve essere leggibile o scrivibile; applicare ReadOnlyArray o WriteOnlyArray a '{1}'. - - - Regola costruttore classe - - - Le classi non possono avere più costruttori dello stesso grado nel Windows Runtime. La classe {0} ha più costruttori {1}-arity - - - Lo spazio dei nomi è disgiunto dallo spazio dei nomi principale (winmd) - - - Uno spazio dei nomi ('{1}') di un tipo pubblico non condivide alcun prefisso comune con altri spazi dei nomi ('{0}'). - {1} and {0} will be some user-defined keyword - - - Tutti i tipi all'interno di un file di metadati Windows devono essere presenti in uno spazio dei nomi secondario dello spazio dei nomi implicito nel nome del file. - "sub namespace" means a namespace defined within another namespace - - - La classe (o l'interfaccia) è generica - - - Il tipo {0} è generico, Windows Runtime tipi non possono essere generici - {0} will be some user-defined keyword - - - È stata trovata una firma della matrice con matrice di matrici, che non è un tipo WinRT valido - - - Nella firma del metodo {0} è presente una matrice annidata di tipo {1}; non è possibile annidare matrici nella firma del metodo Windows Runtime - - - Trovata firma di matrice con matrice multidimensionale, che non è un tipo di Windows Runtime valido - - - Nella firma del metodo '{0}' è presente una matrice multidimensionale di tipo '{1}'. le matrici nelle firme del metodo Windows Runtime devono essere unidimensionali - - - È necessario designare come predefinito un solo overload - - - Nella classe {2}: più overload di parametri {0} di '{1}' sono decorati con Windows.Foundation.Metadata.DefaultOverloadAttribute. - - - L'attributo può essere applicato solo a un overload del metodo. - - - I nomi degli spazi dei nomi non possono differire solo per maiuscole e minuscole - - - Sono stati trovati più spazi dei nomi con il nome '{0}'; i nomi degli spazi dei nomi non possono differire solo per la distinzione tra maiuscole e minuscole nel Windows Runtime - {0} will be some user-defined keyword - - - Più overload rilevati senza attributo DefaultOverload - - - Nella classe {2}: per gli overload del parametro {0} di {1} deve essere specificato esattamente un metodo come overload predefinito decorandolo con Windows.Foundation.Metadata.DefaultOverloadAttribute - - - Parametro (non tipo matrice) contrassegnato come InAttribute o OutAttribute - - - Il metodo '{0}' ha un parametro '{1}' con System.Runtime.InteropServices.InAttribute o System.Runtime.InteropServices.OutAttribute.Windows Runtime non supporta il contrassegno di parametri con System.Runtime.InteropServices.InAttribute o System.Runtime.InteropServices.OutAttribute. - - - Provare a rimuovere System.Runtime.InteropServices.InAttribute e sostituire System.Runtime.InteropServices.OutAttribute con il modificatore 'out'. - - - Parametro non di matrice contrassegnato con ReadOnlyArray o WriteOnlyArray - - - Il metodo '{0}' ha un parametro '{1}' che non è una matrice e che ha un attributo ReadOnlyArray o WriteOnlyArray. - - - Windows Runtime non supporta il contrassegno di parametri non di matrice con ReadOnlyArray o WriteOnlyArray. - - - Interfaccia ereditata non valida - - - Windows Runtime tipo di componente {0} non può implementare l'interfaccia {1}. L'interfaccia non è un'interfaccia Windows Runtime valida - - - Nessun tipo pubblico definito - - - I componenti Windows Runtime devono avere almeno un tipo pubblico - - - Overload dell'operatore esposto - - - {0} è un overload dell'operatore. I tipi gestiti non possono esporre overload di operatori nel Windows Runtime - - - Regola del valore denominato del parametro - - - Il nome del parametro {1} nel metodo {0} è uguale al nome del parametro del valore restituito utilizzato nell'interoperabilità C#/WinRT generata; usa un nome di parametro diverso - - - La proprietà deve avere un getter pubblico - - - La proprietà '{0}' non dispone di un metodo getter pubblico. Windows Runtime non supporta proprietà solo setter. - {0} will be some user-defined keyword - - - Parametro passato per riferimento - - - Il metodo '{0}' ha un parametro '{1}' contrassegnato come 'ref'; parametri di riferimento non consentiti in Windows Runtime - - - Campo costante nello struct - - - La struttura {0} contiene un campo costante. Le costanti possono essere visualizzate solo nelle enumerazioni Windows Runtime. - - - Campo non valido nello struct - - - La struttura {0} ha un campo di tipo {1}; {1} non è un tipo di campo Windows Runtime valido. - - - Ogni campo di una struttura Windows Runtime può essere solo UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Boolean, String, Enum o una struttura stessa. - - - Campo privato nello struct - - - La struttura {0} contiene un campo non pubblico. Tutti i campi devono essere pubblici per le strutture Windows Runtime. - - - Regola di struct vuota - - - La struttura {0} non contiene campi pubblici. Windows Runtime strutture devono contenere almeno un campo pubblico. - - - La classe implementa in modo errato un'interfaccia - - - La classe '{0}' non implementa correttamente l'interfaccia '{1}' perché il '{2}' membro è mancante o non pubblico - - - Classe non eseguito - - - L'esportazione di tipi non sealed non è supportata in CsWinRT. Contrassegnare il tipo {0} come sealed - {0} will be some user-defined keyword - - - Esposizione del tipo non supportato - - - Il '{0}' membro ha il tipo '{1}' nella relativa firma. - {0} and {1} will be user-defined keywords - - - Il tipo '{1}' non è un tipo di Windows Runtime valido. - {1} will be some user-defined keyword - - - Tuttavia, il tipo (o i relativi parametri generici) implementa interfacce valide Windows Runtime tipi. - - - Provare a modificare il tipo '{1} nella firma del membro in uno dei tipi seguenti da 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 + + + Parametro di matrice contrassegnato come InAttribute o OutAttribute + + + Il metodo '{0}' ha un parametro '{1}' che è una matrice e che contiene System.Runtime.InteropServices.InAttribute o System.Runtime.InteropServices.OutAttribute. + + + Nel Windows Runtime i parametri di matrice devono avere ReadOnlyArray o WriteOnlyArray. + + + Rimuovere questi attributi o sostituirli con l'attributo Windows Runtime appropriato, se necessario. + + + Parametro di matrice contrassegnato come 'out' e ReadOnlyArray + + + Il metodo '{0}' dispone di un parametro di output '{1}' che è una matrice, ma che ha l'attributo ReadOnlyArray. + + + Nel Windows Runtime, il contenuto delle matrici di output è scrivibile. Rimuovere l'attributo dal '{1}'. + + + Parametro di matrice contrassegnato sia come ReadOnlyArray che come WriteOnlyArray + + + Il metodo '{0}' ha un parametro '{1}' che è una matrice e che contiene sia ReadOnlyArray che WriteOnlyArray. + + + Nel Windows Runtime i parametri della matrice del contenuto devono essere leggibili o scrivibili. Rimuovere uno degli attributi dal '{1}'. + + + Parametro di matrice non contrassegnato come ReadOnlyArray o WriteOnlyArray + + + Il metodo '{0}' contiene un '{1}' di parametro che è una matrice. + + + Nel Windows Runtime, il contenuto dei parametri della matrice deve essere leggibile o scrivibile; applicare ReadOnlyArray o WriteOnlyArray a '{1}'. + + + Regola costruttore classe + + + Le classi non possono avere più costruttori dello stesso grado nel Windows Runtime. La classe {0} ha più costruttori {1}-arity + + + Lo spazio dei nomi è disgiunto dallo spazio dei nomi principale (winmd) + + + Uno spazio dei nomi ('{1}') di un tipo pubblico non condivide alcun prefisso comune con altri spazi dei nomi ('{0}'). + {1} and {0} will be some user-defined keyword + + + Tutti i tipi all'interno di un file di metadati Windows devono essere presenti in uno spazio dei nomi secondario dello spazio dei nomi implicito nel nome del file. + "sub namespace" means a namespace defined within another namespace + + + La classe (o l'interfaccia) è generica + + + Il tipo {0} è generico, Windows Runtime tipi non possono essere generici + {0} will be some user-defined keyword + + + È stata trovata una firma della matrice con matrice di matrici, che non è un tipo WinRT valido + + + Nella firma del metodo {0} è presente una matrice annidata di tipo {1}; non è possibile annidare matrici nella firma del metodo Windows Runtime + + + Trovata firma di matrice con matrice multidimensionale, che non è un tipo di Windows Runtime valido + + + Nella firma del metodo '{0}' è presente una matrice multidimensionale di tipo '{1}'. le matrici nelle firme del metodo Windows Runtime devono essere unidimensionali + + + È necessario designare come predefinito un solo overload + + + Nella classe {2}: più overload di parametri {0} di '{1}' sono decorati con Windows.Foundation.Metadata.DefaultOverloadAttribute. + + + L'attributo può essere applicato solo a un overload del metodo. + + + I nomi degli spazi dei nomi non possono differire solo per maiuscole e minuscole + + + Sono stati trovati più spazi dei nomi con il nome '{0}'; i nomi degli spazi dei nomi non possono differire solo per la distinzione tra maiuscole e minuscole nel Windows Runtime + {0} will be some user-defined keyword + + + Più overload rilevati senza attributo DefaultOverload + + + Nella classe {2}: per gli overload del parametro {0} di {1} deve essere specificato esattamente un metodo come overload predefinito decorandolo con Windows.Foundation.Metadata.DefaultOverloadAttribute + + + Parametro (non tipo matrice) contrassegnato come InAttribute o OutAttribute + + + Il metodo '{0}' ha un parametro '{1}' con System.Runtime.InteropServices.InAttribute o System.Runtime.InteropServices.OutAttribute.Windows Runtime non supporta il contrassegno di parametri con System.Runtime.InteropServices.InAttribute o System.Runtime.InteropServices.OutAttribute. + + + Provare a rimuovere System.Runtime.InteropServices.InAttribute e sostituire System.Runtime.InteropServices.OutAttribute con il modificatore 'out'. + + + Parametro non di matrice contrassegnato con ReadOnlyArray o WriteOnlyArray + + + Il metodo '{0}' ha un parametro '{1}' che non è una matrice e che ha un attributo ReadOnlyArray o WriteOnlyArray. + + + Windows Runtime non supporta il contrassegno di parametri non di matrice con ReadOnlyArray o WriteOnlyArray. + + + Interfaccia ereditata non valida + + + Windows Runtime tipo di componente {0} non può implementare l'interfaccia {1}. L'interfaccia non è un'interfaccia Windows Runtime valida + + + Nessun tipo pubblico definito + + + I componenti Windows Runtime devono avere almeno un tipo pubblico + + + Overload dell'operatore esposto + + + {0} è un overload dell'operatore. I tipi gestiti non possono esporre overload di operatori nel Windows Runtime + + + Regola del valore denominato del parametro + + + Il nome del parametro {1} nel metodo {0} è uguale al nome del parametro del valore restituito utilizzato nell'interoperabilità C#/WinRT generata; usa un nome di parametro diverso + + + La proprietà deve avere un getter pubblico + + + La proprietà '{0}' non dispone di un metodo getter pubblico. Windows Runtime non supporta proprietà solo setter. + {0} will be some user-defined keyword + + + Parametro passato per riferimento + + + Il metodo '{0}' ha un parametro '{1}' contrassegnato come 'ref'; parametri di riferimento non consentiti in Windows Runtime + + + Campo costante nello struct + + + La struttura {0} contiene un campo costante. Le costanti possono essere visualizzate solo nelle enumerazioni Windows Runtime. + + + Campo non valido nello struct + + + La struttura {0} ha un campo di tipo {1}; {1} non è un tipo di campo Windows Runtime valido. + + + Ogni campo di una struttura Windows Runtime può essere solo UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Boolean, String, Enum o una struttura stessa. + + + Campo privato nello struct + + + La struttura {0} contiene un campo non pubblico. Tutti i campi devono essere pubblici per le strutture Windows Runtime. + + + Regola di struct vuota + + + La struttura {0} non contiene campi pubblici. Windows Runtime strutture devono contenere almeno un campo pubblico. + + + La classe implementa in modo errato un'interfaccia + + + La classe '{0}' non implementa correttamente l'interfaccia '{1}' perché il '{2}' membro è mancante o non pubblico + + + Classe non eseguito + + + L'esportazione di tipi non sealed non è supportata in CsWinRT. Contrassegnare il tipo {0} come sealed + {0} will be some user-defined keyword + + + Esposizione del tipo non supportato + + + Il '{0}' membro ha il tipo '{1}' nella relativa firma. + {0} and {1} will be user-defined keywords + + + Il tipo '{1}' non è un tipo di Windows Runtime valido. + {1} will be some user-defined keyword + + + Tuttavia, il tipo (o i relativi parametri generici) implementa interfacce valide Windows Runtime tipi. + + + Provare a modificare il tipo '{1} nella firma del membro in uno dei tipi seguenti da System.Collections.Generic: {2}. + {1} and {2} will be keywords (types) from DotNet + \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/ResX/ja-JP/CsWinRTDiagnosticStrings.resx b/src/Authoring/WinRT.SourceGenerator/ResX/ja-JP/CsWinRTDiagnosticStrings.resx index 94cdc1a1f..84489f511 100644 --- a/src/Authoring/WinRT.SourceGenerator/ResX/ja-JP/CsWinRTDiagnosticStrings.resx +++ b/src/Authoring/WinRT.SourceGenerator/ResX/ja-JP/CsWinRTDiagnosticStrings.resx @@ -1,271 +1,271 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - 配列パラメーターが InAttribute または OutAttribute に設定されています - - - メソッド '{0}'には、配列であるパラメーター '{1}'があり、System.Runtime.InteropServices.InAttribute または System.Runtime.InteropServices.OutAttribute を持っています。 - - - Windows ランタイムでは、配列パラメーターには ReadOnlyArray または WriteOnlyArray が必要です。 - - - これらの属性を削除するか、必要に応じて適切な Windows ランタイム属性に置き換えてください。 - - - 配列パラメーターが 'out' および ReadOnlyArray に設定されています - - - メソッド '{0}' には、配列である出力パラメーター '{1}' がありますが、ReadOnlyArray 属性を持っています。 - - - Windows ランタイムでは、出力配列の内容は書き込み可能です。属性を'{1}'から削除してください。 - - - ReadOnlyArray と WriteOnlyArray の両方をマークした配列パラメーター - - - メソッド '{0}' には、配列であり、ReadOnlyArray と WriteOnlyArray の両方を持つパラメーター '{1}' があります。 - - - Windows ランタイムでは、コンテンツ配列パラメーターは読み取り可能または書き込み可能である必要があります。属性の 1 つを '{1}' から削除してください。 - - - ReadOnlyArray または WriteOnlyArray の方法でマークされていない配列パラメーター - - - メソッド '{0}'に、配列であるパラメーター '{1}'があります。 - - - Windows ランタイムでは、配列パラメーターの内容を読み取り可能または書き込み可能にする必要があります。ReadOnlyArray または WriteOnlyArray を'{1}'に適用してください。 - - - クラス コンストラクター ルール - - - クラスは、Windows ランタイムで同じアリティの複数のコンストラクターを持つことはできません。クラス {0} には、複数の {1} アリティ コンストラクターがあります。 - - - 名前空間がメイン (winmd) 名前空間から離れています - - - パブリック型に、共通のプレフィックスを他の名前空間 ('{0}') と共有しない名前空間 ('{1}') があります。 - {1} and {0} will be some user-defined keyword - - - Windows メタデータ ファイル内のすべての型は、ファイル名によって示される名前空間のサブ名前空間に存在する必要があります。 - "sub namespace" means a namespace defined within another namespace - - - クラス (またはインターフェイス) はジェネリックです - - - 型 {0} はジェネリックであり、Windows ランタイムの種類はジェネリックにすることはできません - {0} will be some user-defined keyword - - - ジャグ配列で配列シグネチャが見つかりましたが、これは有効な WinRT 型ではありません - - - メソッド {0} のシグネチャには、型 {1} のネストされた配列があります。Windows ランタイム メソッド シグネチャの配列はネストできません - - - 有効な Windows ランタイムの種類ではない多次元配列で見つかった配列シグネチャ - - - メソッド '{0}' のシグネチャには、型 '{1}' の多次元配列があります。Windows ランタイム メソッド シグネチャの配列は 1 次元である必要があります - - - 1 つのオーバーロードのみを既定として指定する必要があります - - - クラス {2} の場合: '{1}' の複数の {0} パラメーターのオーバーロードは、Windows.Foundation.Metadata.DefaultOverloadAttribute で装飾されます。 - - - 属性は、メソッドの 1 つのオーバーロードにのみ適用できます。 - - - 名前空間名は大文字と小文字だけで異なることはできません - - - '{0}' という名前で複数の名前空間が見つかりました; 名前空間名は、Windows ランタイムでは大文字と小文字だけで異なることはできません - {0} will be some user-defined keyword - - - DefaultOverload 属性なしで見られる複数のオーバーロード - - - クラス {2} の場合: {1} の {0} パラメーターのオーバーロードには、Windows.Foundation.Metadata.DefaultOverloadAttribute で装飾することにより、既定のオーバーロードとして 1 つのメソッドを指定する必要があります。 - - - InAttribute または OutAttribute に設定されたパラメーター (配列型ではありません) - - - メソッド '{0}' には、System.Runtime.InteropServices.InAttribute または System.Runtime.InteropServices.OutAttribute.Windows ランタイムを使用したパラメーター '{1}' があり、System.Runtime.InteropServices.InAttribute または System.Runtime.InteropServices.OutAttribute を使用したパラメーターのマーキングはサポートされていません。 - - - System.Runtime.InteropServices.InAttribute を削除することを検討し、代わりに System.Runtime.InteropServices.OutAttribute を 'out' 修飾子に置き換えてください。 - - - ReadOnlyArray または WriteOnlyArray でマークされた非配列パラメーター - - - メソッド '{0}' に、配列ではなく、ReadOnlyArray 属性または WriteOnlyArray 属性を持つパラメーター '{1}'があります。 - - - Windows ランタイムは、ReadOnlyArray または WriteOnlyArray による非配列パラメーターのマーキングをサポートしていません。 - - - 無効なインターフェイスが継承されました - - - インターフェイスが有効な Windows ランタイム インターフェイスではないため、Windows ランタイム コンポーネントの型 {0} はインターフェイス {1} を実装できません - - - パブリック型は定義されていません - - - Windows ランタイム コンポーネントには少なくとも 1 つのパブリック型が必要です - - - 演算子のオーバーロードが公開されました - - - {0} は演算子のオーバーロードであり、マネージ型は Windows ランタイムで演算子のオーバーロードを公開できません - - - パラメータ名前付き値ルール - - - メソッド {0} のパラメーター名{1}が、生成された C#/WinRT 相互運用機能で使用される戻り値パラメーター名と同じです。別のパラメーター名を使用する - - - プロパティにはパブリック ゲッターが必要です - - - プロパティ '{0}' にパブリック ゲッター メソッドがありません。Windows ランタイムはセッター専用プロパティをサポートしていません。 - {0} will be some user-defined keyword - - - 参照によって渡されたパラメーター - - - メソッド '{0}' には、'ref' とマークされたパラメーター '{1}' があります。参照パラメーターは Windows ランタイムでは許可されていません - - - 構造体の Const フィールド - - - 構造体 {0} には const フィールドがあります - 定数は Windows ランタイム列挙にのみ表示できます。 - - - 構造体の無効なフィールド - - - 構造体{0}に型 {1}; のフィールドがあります。{1}は有効なWindows ランタイムフィールドの種類ではありません。 - - - Windows ランタイム構造体の各フィールドには、UInt8、Int16、UInt16、Int32、UInt32、Int64、UInt64、Single、Double、Boolean、String、Enum、またはその構造体のみを指定できます。 - - - 構造体内のプライベート フィールド - - - 構造体 {0} には非公開フィールドがあります。Windows ランタイム構造体では、すべてのフィールドがパブリックである必要があります。 - - - 空の構造体ルール - - - 構造体 {0} にはパブリック フィールドは含まれていません。Windows ランタイム構造体には、少なくとも 1 つのパブリック フィールドが含まれている必要があります。 - - - クラスがインターフェイスを誤って実装しています - - - メンバー '{2}' が見つからないかパブリックでないため、クラス '{0}' はインターフェイス '{1}' を正しく実装しません - - - クラスが封印されていません - - - 封印されていない型のエクスポートは CsWinRT ではサポートされていません。型 {0} を封印済みとしてマークしてください - {0} will be some user-defined keyword - - - サポートされていない型を公開しています - - - メンバー '{0}'のシグネチャに'{1}'型が含まれています。 - {0} and {1} will be user-defined keywords - - - 型'{1}'は有効なWindows ランタイム型ではありません。 - {1} will be some user-defined keyword - - - ただし、型 (またはそのジェネリック パラメーター) は、有効な Windows ランタイムの種類であるインターフェイスを実装します。 - - - メンバー シグネチャの型 '{1} を 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 + + + 配列パラメーターが InAttribute または OutAttribute に設定されています + + + メソッド '{0}'には、配列であるパラメーター '{1}'があり、System.Runtime.InteropServices.InAttribute または System.Runtime.InteropServices.OutAttribute を持っています。 + + + Windows ランタイムでは、配列パラメーターには ReadOnlyArray または WriteOnlyArray が必要です。 + + + これらの属性を削除するか、必要に応じて適切な Windows ランタイム属性に置き換えてください。 + + + 配列パラメーターが 'out' および ReadOnlyArray に設定されています + + + メソッド '{0}' には、配列である出力パラメーター '{1}' がありますが、ReadOnlyArray 属性を持っています。 + + + Windows ランタイムでは、出力配列の内容は書き込み可能です。属性を'{1}'から削除してください。 + + + ReadOnlyArray と WriteOnlyArray の両方をマークした配列パラメーター + + + メソッド '{0}' には、配列であり、ReadOnlyArray と WriteOnlyArray の両方を持つパラメーター '{1}' があります。 + + + Windows ランタイムでは、コンテンツ配列パラメーターは読み取り可能または書き込み可能である必要があります。属性の 1 つを '{1}' から削除してください。 + + + ReadOnlyArray または WriteOnlyArray の方法でマークされていない配列パラメーター + + + メソッド '{0}'に、配列であるパラメーター '{1}'があります。 + + + Windows ランタイムでは、配列パラメーターの内容を読み取り可能または書き込み可能にする必要があります。ReadOnlyArray または WriteOnlyArray を'{1}'に適用してください。 + + + クラス コンストラクター ルール + + + クラスは、Windows ランタイムで同じアリティの複数のコンストラクターを持つことはできません。クラス {0} には、複数の {1} アリティ コンストラクターがあります。 + + + 名前空間がメイン (winmd) 名前空間から離れています + + + パブリック型に、共通のプレフィックスを他の名前空間 ('{0}') と共有しない名前空間 ('{1}') があります。 + {1} and {0} will be some user-defined keyword + + + Windows メタデータ ファイル内のすべての型は、ファイル名によって示される名前空間のサブ名前空間に存在する必要があります。 + "sub namespace" means a namespace defined within another namespace + + + クラス (またはインターフェイス) はジェネリックです + + + 型 {0} はジェネリックであり、Windows ランタイムの種類はジェネリックにすることはできません + {0} will be some user-defined keyword + + + ジャグ配列で配列シグネチャが見つかりましたが、これは有効な WinRT 型ではありません + + + メソッド {0} のシグネチャには、型 {1} のネストされた配列があります。Windows ランタイム メソッド シグネチャの配列はネストできません + + + 有効な Windows ランタイムの種類ではない多次元配列で見つかった配列シグネチャ + + + メソッド '{0}' のシグネチャには、型 '{1}' の多次元配列があります。Windows ランタイム メソッド シグネチャの配列は 1 次元である必要があります + + + 1 つのオーバーロードのみを既定として指定する必要があります + + + クラス {2} の場合: '{1}' の複数の {0} パラメーターのオーバーロードは、Windows.Foundation.Metadata.DefaultOverloadAttribute で装飾されます。 + + + 属性は、メソッドの 1 つのオーバーロードにのみ適用できます。 + + + 名前空間名は大文字と小文字だけで異なることはできません + + + '{0}' という名前で複数の名前空間が見つかりました; 名前空間名は、Windows ランタイムでは大文字と小文字だけで異なることはできません + {0} will be some user-defined keyword + + + DefaultOverload 属性なしで見られる複数のオーバーロード + + + クラス {2} の場合: {1} の {0} パラメーターのオーバーロードには、Windows.Foundation.Metadata.DefaultOverloadAttribute で装飾することにより、既定のオーバーロードとして 1 つのメソッドを指定する必要があります。 + + + InAttribute または OutAttribute に設定されたパラメーター (配列型ではありません) + + + メソッド '{0}' には、System.Runtime.InteropServices.InAttribute または System.Runtime.InteropServices.OutAttribute.Windows ランタイムを使用したパラメーター '{1}' があり、System.Runtime.InteropServices.InAttribute または System.Runtime.InteropServices.OutAttribute を使用したパラメーターのマーキングはサポートされていません。 + + + System.Runtime.InteropServices.InAttribute を削除することを検討し、代わりに System.Runtime.InteropServices.OutAttribute を 'out' 修飾子に置き換えてください。 + + + ReadOnlyArray または WriteOnlyArray でマークされた非配列パラメーター + + + メソッド '{0}' に、配列ではなく、ReadOnlyArray 属性または WriteOnlyArray 属性を持つパラメーター '{1}'があります。 + + + Windows ランタイムは、ReadOnlyArray または WriteOnlyArray による非配列パラメーターのマーキングをサポートしていません。 + + + 無効なインターフェイスが継承されました + + + インターフェイスが有効な Windows ランタイム インターフェイスではないため、Windows ランタイム コンポーネントの型 {0} はインターフェイス {1} を実装できません + + + パブリック型は定義されていません + + + Windows ランタイム コンポーネントには少なくとも 1 つのパブリック型が必要です + + + 演算子のオーバーロードが公開されました + + + {0} は演算子のオーバーロードであり、マネージ型は Windows ランタイムで演算子のオーバーロードを公開できません + + + パラメータ名前付き値ルール + + + メソッド {0} のパラメーター名{1}が、生成された C#/WinRT 相互運用機能で使用される戻り値パラメーター名と同じです。別のパラメーター名を使用する + + + プロパティにはパブリック ゲッターが必要です + + + プロパティ '{0}' にパブリック ゲッター メソッドがありません。Windows ランタイムはセッター専用プロパティをサポートしていません。 + {0} will be some user-defined keyword + + + 参照によって渡されたパラメーター + + + メソッド '{0}' には、'ref' とマークされたパラメーター '{1}' があります。参照パラメーターは Windows ランタイムでは許可されていません + + + 構造体の Const フィールド + + + 構造体 {0} には const フィールドがあります - 定数は Windows ランタイム列挙にのみ表示できます。 + + + 構造体の無効なフィールド + + + 構造体{0}に型 {1}; のフィールドがあります。{1}は有効なWindows ランタイムフィールドの種類ではありません。 + + + Windows ランタイム構造体の各フィールドには、UInt8、Int16、UInt16、Int32、UInt32、Int64、UInt64、Single、Double、Boolean、String、Enum、またはその構造体のみを指定できます。 + + + 構造体内のプライベート フィールド + + + 構造体 {0} には非公開フィールドがあります。Windows ランタイム構造体では、すべてのフィールドがパブリックである必要があります。 + + + 空の構造体ルール + + + 構造体 {0} にはパブリック フィールドは含まれていません。Windows ランタイム構造体には、少なくとも 1 つのパブリック フィールドが含まれている必要があります。 + + + クラスがインターフェイスを誤って実装しています + + + メンバー '{2}' が見つからないかパブリックでないため、クラス '{0}' はインターフェイス '{1}' を正しく実装しません + + + クラスが封印されていません + + + 封印されていない型のエクスポートは CsWinRT ではサポートされていません。型 {0} を封印済みとしてマークしてください + {0} will be some user-defined keyword + + + サポートされていない型を公開しています + + + メンバー '{0}'のシグネチャに'{1}'型が含まれています。 + {0} and {1} will be user-defined keywords + + + 型'{1}'は有効なWindows ランタイム型ではありません。 + {1} will be some user-defined keyword + + + ただし、型 (またはそのジェネリック パラメーター) は、有効な Windows ランタイムの種類であるインターフェイスを実装します。 + + + メンバー シグネチャの型 '{1} を System.Collections.Generic から次の種類のいずれかに変更することを検討してください: {2}。 + {1} and {2} will be keywords (types) from DotNet + \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/ResX/ko-KR/CsWinRTDiagnosticStrings.resx b/src/Authoring/WinRT.SourceGenerator/ResX/ko-KR/CsWinRTDiagnosticStrings.resx index d1e7141ca..ac71b9015 100644 --- a/src/Authoring/WinRT.SourceGenerator/ResX/ko-KR/CsWinRTDiagnosticStrings.resx +++ b/src/Authoring/WinRT.SourceGenerator/ResX/ko-KR/CsWinRTDiagnosticStrings.resx @@ -1,271 +1,271 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - InAttribute 또는 OutAttribute로 표시된 배열 매개 변수 - - - '{0}' 메서드에 배열인 매개 변수 '{1}' System.Runtime.InteropServices.InAttribute 또는 System.Runtime.InteropServices.OutAttribute가 있는 매개 변수가 있습니다. - - - Windows 런타임에서 배열 매개 변수에는 ReadOnlyArray 또는 WriteOnlyArray가 있어야 합니다. - - - 이러한 특성을 제거하거나 필요한 경우 적절한 Windows 런타임 특성으로 바꾸십시오. - - - 'out'으로 표시된 배열 매개 변수 및 ReadOnlyArray - - - 메서드 '{0}' 배열이지만 ReadOnlyArray 특성이 있는 출력 매개 변수 '{1}' 있습니다. - - - Windows 런타임 출력 배열의 내용을 쓸 수 있습니다. '{1}' 특성을 제거하십시오. - - - ReadOnlyArray 및 WriteOnlyArray 모두로 표시된 배열 매개 변수 - - - '{0}' 메서드에 배열인 매개 변수 '{1}' ReadOnlyArray와 WriteOnlyArray가 둘 다 있습니다. - - - Windows 런타임에서 콘텐츠 배열 매개 변수는 읽기 또는 쓰기가 가능해야 합니다. '{1}'에서 특성 중 하나를 제거하세요. - - - 배열 매개 변수가 ReadOnlyArray 또는 WriteOnlyArray 방식으로 표시되지 않았습니다. - - - '{0}' 메서드에 배열인 매개 변수 '{1}' 있습니다. - - - Windows 런타임 배열 매개 변수의 내용은 읽기 가능하거나 쓰기 가능해야 합니다. readOnlyArray 또는 WriteOnlyArray를 '{1}' 적용하세요. - - - 클래스 생성자 규칙 - - - 클래스는 Windows 런타임에서 동일한 arity의 여러 생성자를 가질 수 없습니다. 클래스 {0}에는 여러 {1}-arity 생성자가 있습니다. - - - 네임스페이스가 기본(winmd) 네임스페이스와 분리되어 있습니다. - - - public 형식에 다른 네임스페이스와 공통 접두사를 공유하지 않는 네임스페이스('{1}')가 있습니다('{0}'). - {1} and {0} will be some user-defined keyword - - - Windows 메타데이터 파일 내의 모든 형식은 파일 이름에 포함된 네임스페이스의 하위 네임스페이스에 있어야 합니다. - "sub namespace" means a namespace defined within another namespace - - - 클래스(또는 인터페이스)가 일반적입니다. - - - 형식 {0} 제네릭이므로 Windows 런타임 형식은 제네릭일 수 없습니다. - {0} will be some user-defined keyword - - - 유효한 WinRT 유형이 아닌 들쭉날쭉한 배열로 배열 서명을 찾았습니다. - - - 메서드 {0} 시그니처에 {1} 형식의 중첩 배열이 있습니다. Windows 런타임 메서드 시그니처의 배열은 중첩될 수 없습니다. - - - 유효한 Windows 런타임 유형이 아닌 다차원 배열로 배열 서명을 찾았습니다. - - - 메서드 '{0}' 시그니처에 '{1}' 형식의 다차원 배열이 있습니다. Windows 런타임 메서드 시그니처의 배열은 1차원이어야 합니다. - - - 하나의 과부하만 기본값으로 지정해야 합니다. - - - 클래스 {2}에서: '{1}'의 여러 {0} 매개 변수 오버로드가 Windows.Foundation.Metadata.DefaultOverloadAttribute로 장식됩니다. - - - 특성은 메서드의 한 오버로드에만 적용할 수 있습니다. - - - 네임스페이스 이름은 대소문자만 다를 수 없습니다. - - - 이름이 '{0}';인 네임스페이스가 여러 개 있습니다. 네임스페이스 이름은 Windows 런타임 대/소문자만 다를 수 없습니다. - {0} will be some user-defined keyword - - - DefaultOverload 특성 없이 여러 오버로드가 표시됨 - - - 클래스 {2}에서: {1}의 {0}-매개 변수 오버로드에는 Windows.Foundation.Metadata.DefaultOverloadAttribute로 데코레이션하여 기본 오버로드로 지정된 정확히 하나의 메서드가 있어야 합니다. - - - InAttribute 또는 OutAttribute로 표시된 매개 변수(배열 형식 아님) - - - '{0}' 메서드에 System.Runtime.InteropServices.InAttribute 또는 System.Runtime.InteropServices.OutAttribute가 있는 '{1}' 매개 변수가 있습니다. Windows 런타임은 System.Runtime.InteropServices.InAttribute 또는 System.Runtime.InteropServices.OutAttribute로 매개 변수 표시를 지원하지 않습니다. - - - System.Runtime.InteropServices.InAttribute를 제거하고 System.Runtime.InteropServices.OutAttribute를 대신 'out' 한정자로 바꾸세요. - - - ReadOnlyArray 또는 WriteOnlyArray로 표시된 비배열 매개변수 - - - 메서드 '{0}' 배열이 아닌 매개 변수 '{1}' ReadOnlyArray 특성 또는 WriteOnlyArray 특성이 있습니다. - - - Windows 런타임 ReadOnlyArray 또는 WriteOnlyArray를 사용하여 배열이 아닌 매개 변수를 표시할 수 없습니다. - - - 잘못된 인터페이스 상속됨 - - - 인터페이스가 올바른 Windows 런타임 인터페이스가 아니므로 Windows 런타임 구성 요소 형식 {0} 인터페이스 {1} 구현할 수 없습니다. - - - 정의된 공개 유형이 없습니다. - - - Windows 런타임 구성 요소에는 하나 이상의 공개 유형이 있어야 합니다. - - - 운영자 과부하 노출 - - - {0} 연산자 오버로드이므로 관리되는 형식은 Windows 런타임 연산자 오버로드를 노출할 수 없습니다. - - - 매개 변수 명명된 값 규칙 - - - {0} 메서드에 {1} 매개 변수 이름이 생성된 C#/WinRT interop에 사용된 반환 값 매개 변수 이름과 같습니다. 다른 매개 변수 이름 사용 - - - 속성에는 public getter가 있어야 합니다. - - - 속성 '{0}' public getter 메서드가 없습니다. Windows 런타임 setter 전용 속성을 지원하지 않습니다. - {0} will be some user-defined keyword - - - 참조로 전달된 매개변수 - - - 메서드 '{0}' 'ref'로 표시된 매개 변수 '{1}' 있습니다. Windows 런타임 참조 매개 변수를 사용할 수 없습니다. - - - 구조체의 Const 필드 - - - 구조 {0}에는 const 필드가 있습니다. 상수는 Windows 런타임 열거형에만 나타날 수 있습니다. - - - 구조체의 잘못된 필드 - - - 구조체 {0} {1}; 형식의 필드가 있습니다. {1} 올바른 Windows 런타임 필드 형식이 아닙니다. - - - Windows 런타임 구조의 각 필드는 UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Boolean, String, Enum 또는 자체 구조일 수만 있습니다. - - - 구조체의 개인 필드 - - - 구조체 {0} public이 아닌 필드가 있습니다. 모든 필드는 Windows 런타임 구조에 대해 public이어야 합니다. - - - 빈 구조체 규칙 - - - 구조 {0} 공용 필드가 없습니다. Windows 런타임 구조체에는 하나 이상의 공용 필드가 있어야 합니다. - - - 클래스가 인터페이스를 잘못 구현함 - - - 멤버 '{2}' 없거나 public이 아니므로 클래스 '{0}' 인터페이스 '{1}' 올바르게 구현하지 않습니다. - - - 클래스가 봉인되지 않음 - - - 봉인되지 않은 형식 내보내기는 CsWinRT에서 지원되지 않습니다. 형식 {0}을 봉인된 것으로 표시하세요. - {0} will be some user-defined keyword - - - 지원되지 않는 유형 노출 - - - 멤버 '{0}' 시그니처에 '{1}' 형식이 있습니다. - {0} and {1} will be user-defined keywords - - - '{1}' 형식이 올바른 Windows 런타임 형식이 아닙니다. - {1} will be some user-defined keyword - - - 그러나 형식(또는 제네릭 매개 변수)은 올바른 Windows 런타임 형식인 인터페이스를 구현합니다. - - - 멤버 서명의 '{1} 유형을 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 + + + InAttribute 또는 OutAttribute로 표시된 배열 매개 변수 + + + '{0}' 메서드에 배열인 매개 변수 '{1}' System.Runtime.InteropServices.InAttribute 또는 System.Runtime.InteropServices.OutAttribute가 있는 매개 변수가 있습니다. + + + Windows 런타임에서 배열 매개 변수에는 ReadOnlyArray 또는 WriteOnlyArray가 있어야 합니다. + + + 이러한 특성을 제거하거나 필요한 경우 적절한 Windows 런타임 특성으로 바꾸십시오. + + + 'out'으로 표시된 배열 매개 변수 및 ReadOnlyArray + + + 메서드 '{0}' 배열이지만 ReadOnlyArray 특성이 있는 출력 매개 변수 '{1}' 있습니다. + + + Windows 런타임 출력 배열의 내용을 쓸 수 있습니다. '{1}' 특성을 제거하십시오. + + + ReadOnlyArray 및 WriteOnlyArray 모두로 표시된 배열 매개 변수 + + + '{0}' 메서드에 배열인 매개 변수 '{1}' ReadOnlyArray와 WriteOnlyArray가 둘 다 있습니다. + + + Windows 런타임에서 콘텐츠 배열 매개 변수는 읽기 또는 쓰기가 가능해야 합니다. '{1}'에서 특성 중 하나를 제거하세요. + + + 배열 매개 변수가 ReadOnlyArray 또는 WriteOnlyArray 방식으로 표시되지 않았습니다. + + + '{0}' 메서드에 배열인 매개 변수 '{1}' 있습니다. + + + Windows 런타임 배열 매개 변수의 내용은 읽기 가능하거나 쓰기 가능해야 합니다. readOnlyArray 또는 WriteOnlyArray를 '{1}' 적용하세요. + + + 클래스 생성자 규칙 + + + 클래스는 Windows 런타임에서 동일한 arity의 여러 생성자를 가질 수 없습니다. 클래스 {0}에는 여러 {1}-arity 생성자가 있습니다. + + + 네임스페이스가 기본(winmd) 네임스페이스와 분리되어 있습니다. + + + public 형식에 다른 네임스페이스와 공통 접두사를 공유하지 않는 네임스페이스('{1}')가 있습니다('{0}'). + {1} and {0} will be some user-defined keyword + + + Windows 메타데이터 파일 내의 모든 형식은 파일 이름에 포함된 네임스페이스의 하위 네임스페이스에 있어야 합니다. + "sub namespace" means a namespace defined within another namespace + + + 클래스(또는 인터페이스)가 일반적입니다. + + + 형식 {0} 제네릭이므로 Windows 런타임 형식은 제네릭일 수 없습니다. + {0} will be some user-defined keyword + + + 유효한 WinRT 유형이 아닌 들쭉날쭉한 배열로 배열 서명을 찾았습니다. + + + 메서드 {0} 시그니처에 {1} 형식의 중첩 배열이 있습니다. Windows 런타임 메서드 시그니처의 배열은 중첩될 수 없습니다. + + + 유효한 Windows 런타임 유형이 아닌 다차원 배열로 배열 서명을 찾았습니다. + + + 메서드 '{0}' 시그니처에 '{1}' 형식의 다차원 배열이 있습니다. Windows 런타임 메서드 시그니처의 배열은 1차원이어야 합니다. + + + 하나의 과부하만 기본값으로 지정해야 합니다. + + + 클래스 {2}에서: '{1}'의 여러 {0} 매개 변수 오버로드가 Windows.Foundation.Metadata.DefaultOverloadAttribute로 장식됩니다. + + + 특성은 메서드의 한 오버로드에만 적용할 수 있습니다. + + + 네임스페이스 이름은 대소문자만 다를 수 없습니다. + + + 이름이 '{0}';인 네임스페이스가 여러 개 있습니다. 네임스페이스 이름은 Windows 런타임 대/소문자만 다를 수 없습니다. + {0} will be some user-defined keyword + + + DefaultOverload 특성 없이 여러 오버로드가 표시됨 + + + 클래스 {2}에서: {1}의 {0}-매개 변수 오버로드에는 Windows.Foundation.Metadata.DefaultOverloadAttribute로 데코레이션하여 기본 오버로드로 지정된 정확히 하나의 메서드가 있어야 합니다. + + + InAttribute 또는 OutAttribute로 표시된 매개 변수(배열 형식 아님) + + + '{0}' 메서드에 System.Runtime.InteropServices.InAttribute 또는 System.Runtime.InteropServices.OutAttribute가 있는 '{1}' 매개 변수가 있습니다. Windows 런타임은 System.Runtime.InteropServices.InAttribute 또는 System.Runtime.InteropServices.OutAttribute로 매개 변수 표시를 지원하지 않습니다. + + + System.Runtime.InteropServices.InAttribute를 제거하고 System.Runtime.InteropServices.OutAttribute를 대신 'out' 한정자로 바꾸세요. + + + ReadOnlyArray 또는 WriteOnlyArray로 표시된 비배열 매개변수 + + + 메서드 '{0}' 배열이 아닌 매개 변수 '{1}' ReadOnlyArray 특성 또는 WriteOnlyArray 특성이 있습니다. + + + Windows 런타임 ReadOnlyArray 또는 WriteOnlyArray를 사용하여 배열이 아닌 매개 변수를 표시할 수 없습니다. + + + 잘못된 인터페이스 상속됨 + + + 인터페이스가 올바른 Windows 런타임 인터페이스가 아니므로 Windows 런타임 구성 요소 형식 {0} 인터페이스 {1} 구현할 수 없습니다. + + + 정의된 공개 유형이 없습니다. + + + Windows 런타임 구성 요소에는 하나 이상의 공개 유형이 있어야 합니다. + + + 운영자 과부하 노출 + + + {0} 연산자 오버로드이므로 관리되는 형식은 Windows 런타임 연산자 오버로드를 노출할 수 없습니다. + + + 매개 변수 명명된 값 규칙 + + + {0} 메서드에 {1} 매개 변수 이름이 생성된 C#/WinRT interop에 사용된 반환 값 매개 변수 이름과 같습니다. 다른 매개 변수 이름 사용 + + + 속성에는 public getter가 있어야 합니다. + + + 속성 '{0}' public getter 메서드가 없습니다. Windows 런타임 setter 전용 속성을 지원하지 않습니다. + {0} will be some user-defined keyword + + + 참조로 전달된 매개변수 + + + 메서드 '{0}' 'ref'로 표시된 매개 변수 '{1}' 있습니다. Windows 런타임 참조 매개 변수를 사용할 수 없습니다. + + + 구조체의 Const 필드 + + + 구조 {0}에는 const 필드가 있습니다. 상수는 Windows 런타임 열거형에만 나타날 수 있습니다. + + + 구조체의 잘못된 필드 + + + 구조체 {0} {1}; 형식의 필드가 있습니다. {1} 올바른 Windows 런타임 필드 형식이 아닙니다. + + + Windows 런타임 구조의 각 필드는 UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Boolean, String, Enum 또는 자체 구조일 수만 있습니다. + + + 구조체의 개인 필드 + + + 구조체 {0} public이 아닌 필드가 있습니다. 모든 필드는 Windows 런타임 구조에 대해 public이어야 합니다. + + + 빈 구조체 규칙 + + + 구조 {0} 공용 필드가 없습니다. Windows 런타임 구조체에는 하나 이상의 공용 필드가 있어야 합니다. + + + 클래스가 인터페이스를 잘못 구현함 + + + 멤버 '{2}' 없거나 public이 아니므로 클래스 '{0}' 인터페이스 '{1}' 올바르게 구현하지 않습니다. + + + 클래스가 봉인되지 않음 + + + 봉인되지 않은 형식 내보내기는 CsWinRT에서 지원되지 않습니다. 형식 {0}을 봉인된 것으로 표시하세요. + {0} will be some user-defined keyword + + + 지원되지 않는 유형 노출 + + + 멤버 '{0}' 시그니처에 '{1}' 형식이 있습니다. + {0} and {1} will be user-defined keywords + + + '{1}' 형식이 올바른 Windows 런타임 형식이 아닙니다. + {1} will be some user-defined keyword + + + 그러나 형식(또는 제네릭 매개 변수)은 올바른 Windows 런타임 형식인 인터페이스를 구현합니다. + + + 멤버 서명의 '{1} 유형을 System.Collections.Generic: {2}에서 다음 유형 중 하나로 변경하는 것이 좋습니다. + {1} and {2} will be keywords (types) from DotNet + \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/ResX/pt-BR/CsWinRTDiagnosticStrings.resx b/src/Authoring/WinRT.SourceGenerator/ResX/pt-BR/CsWinRTDiagnosticStrings.resx index 5c54d7e99..dd2478b24 100644 --- a/src/Authoring/WinRT.SourceGenerator/ResX/pt-BR/CsWinRTDiagnosticStrings.resx +++ b/src/Authoring/WinRT.SourceGenerator/ResX/pt-BR/CsWinRTDiagnosticStrings.resx @@ -1,271 +1,271 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - Parâmetro de matriz marcado como InAttribute ou OutAttribute - - - O '{0}' tem parâmetro '{1}' que é uma matriz e que tem Um System.Runtime.InteropServices.InAttribute ou System.Runtime.InteropServices.OutAttribute. - - - No Windows Runtime, os parâmetros de matriz devem ter ReadOnlyArray ou WriteOnlyArray. - - - Remova esses atributos ou substitua-os pelo atributo Windows Runtime apropriado, se necessário. - - - Parâmetro de matriz marcado como `out` e ReadOnlyArray - - - O '{0}' tem um parâmetro de '{1}' que é uma matriz, mas que tem o atributo ReadOnlyArray. - - - No Windows Runtime, o conteúdo das matrizes de saída é gravável. Remova o atributo de '{1}'. - - - Parâmetro de matriz marcado como ReadOnlyArray e WriteOnlyArray - - - O '{0}' tem parâmetro '{1}' que é uma matriz e que tem ReadOnlyArray e WriteOnlyArray. - - - No Windows Runtime, os parâmetros da matriz de conteúdo devem ser legívels ou graváveis. Remova um dos atributos do '{1}'. - - - Parâmetro de matriz não marcado como ReadOnlyArray ou WriteOnlyArray - - - O '{0}' tem parâmetro '{1}' que é uma matriz. - - - No Windows Runtime, o conteúdo dos parâmetros da matriz deve ser legível ou gravável; aplique ReadOnlyArray ou WriteOnlyArray ao '{1}'. - - - Regra de Construtor de Classe - - - Classes não podem ter vários construtores do mesmo arity no Windows Runtime, a classe {0} tem vários construtores {1}-arity - - - Namespace não contíguo com o namespace principal (winmd) - - - Um tipo público tem um namespace ('{1}') que não compartilha prefixo comum com outros namespaces ('{0}'). - {1} and {0} will be some user-defined keyword - - - Todos os tipos em um arquivo de Metadados do Windows devem existir em um sub namespace do namespace implícito pelo nome do arquivo. - "sub namespace" means a namespace defined within another namespace - - - A classe (ou interface) é genérica - - - O {0} tipo é genérico, Windows Runtime tipos não podem ser genéricos - {0} will be some user-defined keyword - - - Assinatura de matriz encontrada com matriz irregular, que não é um tipo WinRT válido - - - O {0} tem uma matriz aninhada do tipo {1} em sua assinatura; matrizes em Windows Runtime assinatura de método não podem ser aninhadas - - - Assinatura de matriz encontrada com matriz multidimensional, que não é um tipo Windows Runtime válido - - - O '{0}' tem uma matriz multidimensional do tipo '{1}' em sua assinatura; matrizes em Windows Runtime assinaturas de método devem ser unidimensionais - - - Apenas uma sobrecarga deve ser designada como padrão - - - Na classe {2}: várias sobrecargas de {0} parâmetro(s) de '{1}' são decoradas com Windows.Foundation.Metadata.DefaultOverloadAttribute. - - - O atributo só pode ser aplicado a uma sobrecarga do método. - - - Os nomes de namespace não podem diferir apenas por letras maiúsculas e minúsculas - - - Vários namespaces encontrados com o nome '{0}'; nomes de namespace não podem diferir somente por maiúsculas e minúsculas no Windows Runtime - {0} will be some user-defined keyword - - - Várias sobrecargas vistas sem o atributo DefaultOverload - - - Na classe {2}: as sobrecargas de {0} parâmetro(s) de {1} devem ter exatamente um método especificado como a sobrecarga padrão decorando-a com Windows.Foundation.Metadata.DefaultOverloadAttribute - - - Parâmetro (não tipo de matriz) marcado como InAttribute ou OutAttribute - - - O método '{0}' tem o parâmetro '{1}' com um System.Runtime.InteropServices.InAttribute ou System.Runtime.InteropServices.OutAttribute. O Windows Runtime não oferece suporte à marcação de parâmetros com System.Runtime.InteropServices.InAttribute ou System.Runtime.InteropServices.OutAttribute. - - - Considere remover System.Runtime.InteropServices.InAttribute e substituir System.Runtime.InteropServices.OutAttribute pelo modificador 'out'. - - - Parâmetro não matriz marcado com ReadOnlyArray ou WriteOnlyArray - - - O '{0}' tem parâmetro '{1}' que não é uma matriz e que tem um atributo ReadOnlyArray ou um atributo WriteOnlyArray. - - - Windows Runtime não dá suporte à marcação de parâmetros que não são de matriz com ReadOnlyArray ou WriteOnlyArray. - - - Interface Inválida Herdada - - - Windows Runtime tipo de componente {0} não pode implementar a interface {1}, pois a interface não é uma interface Windows Runtime interface válida - - - Nenhum tipo público definido - - - Windows Runtime componentes devem ter pelo menos um tipo público - - - Sobrecarga do operador exposta - - - {0} é uma sobrecarga de operador, os tipos gerenciados não podem expor sobrecargas de operador no Windows Runtime - - - Regra de Valor Nomeado do Parâmetro - - - O nome do {1} no método {0} é igual ao nome do parâmetro de valor de retorno usado na interoperabilidade C#/WinRT gerada; usar um nome de parâmetro diferente - - - A propriedade deve ter getter público - - - A '{0}' não tem um método getter público. Windows Runtime não dá suporte a propriedades somente setter. - {0} will be some user-defined keyword - - - Parâmetro passado por referência - - - O '{0}' tem parâmetro '{1}' marcado como 'ref'; parâmetros de referência não são permitidos Windows Runtime - - - Campo const em struct - - - Estrutura {0} tem campo const - constantes só podem aparecer em Windows Runtime enumerações. - - - Campo inválido no struct - - - Estrutura {0} tem campo do tipo {1}; {1} não é um tipo de campo Windows Runtime válido. - - - Cada campo em uma estrutura Windows Runtime só pode ser UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Único, Duplo, Booliano, Cadeia de caracteres, Enum ou uma estrutura em si. - - - Campo privado no struct - - - O {0} estrutura tem um campo não público. Todos os campos devem ser públicos para Windows Runtime estruturas. - - - Regra de struct vazia - - - O {0} não contém campos públicos. Windows Runtime estruturas devem conter pelo menos um campo público. - - - A classe implementa incorretamente uma interface - - - O '{0}' classe não implementa corretamente a interface '{1}' porque o '{2}' membro está ausente ou não é público - - - A classe não está selada - - - Não há suporte para a exportação de tipos não selados no CsWinRT. marque o tipo {0} como selado - {0} will be some user-defined keyword - - - Expondo tipo sem suporte - - - O membro '{0}' tem o tipo '{1}' em sua assinatura. - {0} and {1} will be user-defined keywords - - - O tipo '{1}' não é um tipo Windows Runtime válido. - {1} will be some user-defined keyword - - - No entanto, o tipo (ou seus parâmetros genéricos) implementam interfaces que são tipos Windows Runtime válidos. - - - Considere alterar o tipo '{1} na assinatura do membro para um dos seguintes tipos de 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 + + + Parâmetro de matriz marcado como InAttribute ou OutAttribute + + + O '{0}' tem parâmetro '{1}' que é uma matriz e que tem Um System.Runtime.InteropServices.InAttribute ou System.Runtime.InteropServices.OutAttribute. + + + No Windows Runtime, os parâmetros de matriz devem ter ReadOnlyArray ou WriteOnlyArray. + + + Remova esses atributos ou substitua-os pelo atributo Windows Runtime apropriado, se necessário. + + + Parâmetro de matriz marcado como `out` e ReadOnlyArray + + + O '{0}' tem um parâmetro de '{1}' que é uma matriz, mas que tem o atributo ReadOnlyArray. + + + No Windows Runtime, o conteúdo das matrizes de saída é gravável. Remova o atributo de '{1}'. + + + Parâmetro de matriz marcado como ReadOnlyArray e WriteOnlyArray + + + O '{0}' tem parâmetro '{1}' que é uma matriz e que tem ReadOnlyArray e WriteOnlyArray. + + + No Windows Runtime, os parâmetros da matriz de conteúdo devem ser legívels ou graváveis. Remova um dos atributos do '{1}'. + + + Parâmetro de matriz não marcado como ReadOnlyArray ou WriteOnlyArray + + + O '{0}' tem parâmetro '{1}' que é uma matriz. + + + No Windows Runtime, o conteúdo dos parâmetros da matriz deve ser legível ou gravável; aplique ReadOnlyArray ou WriteOnlyArray ao '{1}'. + + + Regra de Construtor de Classe + + + Classes não podem ter vários construtores do mesmo arity no Windows Runtime, a classe {0} tem vários construtores {1}-arity + + + Namespace não contíguo com o namespace principal (winmd) + + + Um tipo público tem um namespace ('{1}') que não compartilha prefixo comum com outros namespaces ('{0}'). + {1} and {0} will be some user-defined keyword + + + Todos os tipos em um arquivo de Metadados do Windows devem existir em um sub namespace do namespace implícito pelo nome do arquivo. + "sub namespace" means a namespace defined within another namespace + + + A classe (ou interface) é genérica + + + O {0} tipo é genérico, Windows Runtime tipos não podem ser genéricos + {0} will be some user-defined keyword + + + Assinatura de matriz encontrada com matriz irregular, que não é um tipo WinRT válido + + + O {0} tem uma matriz aninhada do tipo {1} em sua assinatura; matrizes em Windows Runtime assinatura de método não podem ser aninhadas + + + Assinatura de matriz encontrada com matriz multidimensional, que não é um tipo Windows Runtime válido + + + O '{0}' tem uma matriz multidimensional do tipo '{1}' em sua assinatura; matrizes em Windows Runtime assinaturas de método devem ser unidimensionais + + + Apenas uma sobrecarga deve ser designada como padrão + + + Na classe {2}: várias sobrecargas de {0} parâmetro(s) de '{1}' são decoradas com Windows.Foundation.Metadata.DefaultOverloadAttribute. + + + O atributo só pode ser aplicado a uma sobrecarga do método. + + + Os nomes de namespace não podem diferir apenas por letras maiúsculas e minúsculas + + + Vários namespaces encontrados com o nome '{0}'; nomes de namespace não podem diferir somente por maiúsculas e minúsculas no Windows Runtime + {0} will be some user-defined keyword + + + Várias sobrecargas vistas sem o atributo DefaultOverload + + + Na classe {2}: as sobrecargas de {0} parâmetro(s) de {1} devem ter exatamente um método especificado como a sobrecarga padrão decorando-a com Windows.Foundation.Metadata.DefaultOverloadAttribute + + + Parâmetro (não tipo de matriz) marcado como InAttribute ou OutAttribute + + + O método '{0}' tem o parâmetro '{1}' com um System.Runtime.InteropServices.InAttribute ou System.Runtime.InteropServices.OutAttribute. O Windows Runtime não oferece suporte à marcação de parâmetros com System.Runtime.InteropServices.InAttribute ou System.Runtime.InteropServices.OutAttribute. + + + Considere remover System.Runtime.InteropServices.InAttribute e substituir System.Runtime.InteropServices.OutAttribute pelo modificador 'out'. + + + Parâmetro não matriz marcado com ReadOnlyArray ou WriteOnlyArray + + + O '{0}' tem parâmetro '{1}' que não é uma matriz e que tem um atributo ReadOnlyArray ou um atributo WriteOnlyArray. + + + Windows Runtime não dá suporte à marcação de parâmetros que não são de matriz com ReadOnlyArray ou WriteOnlyArray. + + + Interface Inválida Herdada + + + Windows Runtime tipo de componente {0} não pode implementar a interface {1}, pois a interface não é uma interface Windows Runtime interface válida + + + Nenhum tipo público definido + + + Windows Runtime componentes devem ter pelo menos um tipo público + + + Sobrecarga do operador exposta + + + {0} é uma sobrecarga de operador, os tipos gerenciados não podem expor sobrecargas de operador no Windows Runtime + + + Regra de Valor Nomeado do Parâmetro + + + O nome do {1} no método {0} é igual ao nome do parâmetro de valor de retorno usado na interoperabilidade C#/WinRT gerada; usar um nome de parâmetro diferente + + + A propriedade deve ter getter público + + + A '{0}' não tem um método getter público. Windows Runtime não dá suporte a propriedades somente setter. + {0} will be some user-defined keyword + + + Parâmetro passado por referência + + + O '{0}' tem parâmetro '{1}' marcado como 'ref'; parâmetros de referência não são permitidos Windows Runtime + + + Campo const em struct + + + Estrutura {0} tem campo const - constantes só podem aparecer em Windows Runtime enumerações. + + + Campo inválido no struct + + + Estrutura {0} tem campo do tipo {1}; {1} não é um tipo de campo Windows Runtime válido. + + + Cada campo em uma estrutura Windows Runtime só pode ser UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Único, Duplo, Booliano, Cadeia de caracteres, Enum ou uma estrutura em si. + + + Campo privado no struct + + + O {0} estrutura tem um campo não público. Todos os campos devem ser públicos para Windows Runtime estruturas. + + + Regra de struct vazia + + + O {0} não contém campos públicos. Windows Runtime estruturas devem conter pelo menos um campo público. + + + A classe implementa incorretamente uma interface + + + O '{0}' classe não implementa corretamente a interface '{1}' porque o '{2}' membro está ausente ou não é público + + + A classe não está selada + + + Não há suporte para a exportação de tipos não selados no CsWinRT. marque o tipo {0} como selado + {0} will be some user-defined keyword + + + Expondo tipo sem suporte + + + O membro '{0}' tem o tipo '{1}' em sua assinatura. + {0} and {1} will be user-defined keywords + + + O tipo '{1}' não é um tipo Windows Runtime válido. + {1} will be some user-defined keyword + + + No entanto, o tipo (ou seus parâmetros genéricos) implementam interfaces que são tipos Windows Runtime válidos. + + + Considere alterar o tipo '{1} na assinatura do membro para um dos seguintes tipos de System.Collections.Generic: {2}. + {1} and {2} will be keywords (types) from DotNet + \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/ResX/ru-RU/CsWinRTDiagnosticStrings.resx b/src/Authoring/WinRT.SourceGenerator/ResX/ru-RU/CsWinRTDiagnosticStrings.resx index ff7d43835..36495b174 100644 --- a/src/Authoring/WinRT.SourceGenerator/ResX/ru-RU/CsWinRTDiagnosticStrings.resx +++ b/src/Authoring/WinRT.SourceGenerator/ResX/ru-RU/CsWinRTDiagnosticStrings.resx @@ -1,271 +1,271 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - Параметр массива помечен как InAttribute или OutAttribute - - - Метод '{0}' имеет параметр '{1}', который является массивом и имеет атрибут System.Runtime.InteropServices.InAttribute или System.Runtime.InteropServices.OutAttribute. - - - В среде выполнения Windows параметры массива должны иметь метку ReadOnlyArray или WriteOnlyArray. - - - Удалите эти атрибуты или замените их соответствующим атрибутом среды выполнения Windows при необходимости. - - - Параметр массива помечен как out и ReadOnlyArray - - - Метод '{0}' имеет выходной '{1}', который является массивом, но имеет атрибут ReadOnlyArray. - - - В среда выполнения Windows записи содержимое выходных массивов. Удалите атрибут из '{1}'. - - - Параметр массива помечен одновременно как ReadOnlyArray и WriteOnlyArray - - - Метод '{0}' имеет параметр '{1}', который является массивом и имеет readOnlyArray и WriteOnlyArray. - - - В среда выполнения Windows параметры массива содержимого должны быть доступны для чтения или записи. Удалите один из атрибутов из '{1}'. - - - Параметр массива не помечен как ReadOnlyArray или WriteOnlyArray - - - Метод '{0}' имеет параметр '{1}', который является массивом. - - - В среде выполнения Windows содержимое параметров массива должно быть доступно для чтения или записи. Примените ReadOnlyArray или WriteOnlyArray к "{1}". - - - Правило конструктора классов - - - Классы не могут содержать несколько конструкторов с одинаковой арностью в среде выполнения Windows. Класс {0} содержит несколько конструкторов арности {1} - - - Пространство имен отсоединено от основного пространства имен (winmd) - - - Открытый тип имеет пространство имен ('{1}'), которое не имеет общего префикса с другими пространствами имен ('{0}'). - {1} and {0} will be some user-defined keyword - - - Все типы в файле метаданных Windows должны существовать во вложенном пространстве имен пространства имен, которое соответствует имени файла. - "sub namespace" means a namespace defined within another namespace - - - Класс (или интерфейс) является универсальным - - - Тип {0} является универсальным. Типы среды выполнения Windows не могут быть универсальными - {0} will be some user-defined keyword - - - Обнаружена подпись с массивом массивов, который не является допустимым типом WinRT - - - Метод {0} содержит вложенный массив типа {1} в подписи. Массивы в подписи метода среды выполнения Windows не могут быть вложенными - - - Обнаружена подпись массива с многомерным массивом, который не является допустимым типом среды выполнения Windows - - - Метод "{0}" содержит многомерный массив типа {1} в подписи. Массивы в подписях методов среды выполнения Windows должны быть одномерными - - - По умолчанию должна быть назначена только одна перегрузка - - - В классе {2}: множественные перегрузки "{1}" параметра {0} снабжены атрибутом Windows.Foundation.Metadata.DefaultOverloadAttribute. - - - Атрибут может применяться только к одной перегрузке метода. - - - Имена пространств имен не могут отличаться только регистром - - - Найдено несколько пространств имен с именем "{0}". Имена пространств имен не могут отличаться только регистром в среде выполнения Windows - {0} will be some user-defined keyword - - - Обнаружено несколько перегрузок без атрибута DefaultOverload - - - В классе {2}: перегрузки {1} параметра {0} должны использовать только один метод, указанный в качестве перегрузки по умолчанию, путем добавления Windows.Foundation.Metadata.DefaultOverloadAttribute - - - Параметр (не относящийся к массиву) помечен как InAttribute или OutAttribute - - - Метод "{0}" содержит параметр "{1}" с System.Runtime.InteropServices.InAttribute или System.Runtime.InteropServices.OutAttribute. Среда выполнения Windows не поддерживает маркировку параметров с помощью System.Runtime.InteropServices.InAttribute или System.Runtime.InteropServices.OutAttribute. - - - Рассмотрите возможность удаления System.Runtime.InteropServices.InAttribute и замены System.Runtime.InteropServices.OutAttribute модификатором "out". - - - Параметр, не относящийся к массиву, помечен как ReadOnlyArray или WriteOnlyArray - - - Метод '{0}' имеет параметр '{1}', который не является массивом и имеет либо атрибут ReadOnlyArray, либо атрибут WriteOnlyArray. - - - Среда выполнения Windows не поддерживает маркировку параметров, не относящихся к массиву, с помощью ReadOnlyArray или WriteOnlyArray. - - - Унаследованный интерфейс недопустим - - - Тип компонента среды выполнения Windows {0} не может реализовать интерфейс {1}, так как интерфейс не является допустимым интерфейсом среды выполнения Windows - - - Общедоступные типы не определены - - - Компоненты среды выполнения Windows должны содержать по крайней мере один общедоступный тип - - - Предоставлена перегрузка оператора - - - {0} является перегрузкой оператора. Управляемые типы не могут предоставлять доступ к перегрузкам операторов в среде выполнения Windows - - - Правило именованного значения параметра - - - Имя параметра {1} в методе {0} совпадает с именем параметра возвращаемого значения, используемым в созданном взаимодействии C#/WinRT. Примените другое имя параметра - - - Свойство должно иметь общедоступный метод получения - - - Свойство "{0}" не имеет общедоступного метода получения. Среда выполнения Windows не поддерживает свойства только метода задания. - {0} will be some user-defined keyword - - - Параметр, переданный ссылкой - - - Метод "{0}" содержит параметр "{1}", помеченный как "ref". Ссылочные параметры не разрешены в среде выполнения Windows - - - Поле константы в структуре - - - Структура {0} содержит поле константы — константы могут отображаться только в перечислениях среды выполнения Windows. - - - Недопустимое поле в структуре - - - Структура {0} имеет поле типа {1}; {1} не является допустимым типом среда выполнения Windows поля. - - - Каждое поле в структуре среды выполнения Windows может быть только типом UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Boolean, String, Enum или самостоятельной структурой. - - - Частное поле в структуре - - - Структура {0} содержит поле, не являющееся общедоступным. Для структур среды выполнения Windows все поля должны быть общедоступными. - - - Правило пустой структуры - - - Структура {0} не содержит общедоступных полей. Структуры среды выполнения Windows должны содержать по крайней мере одно общедоступное поле. - - - Класс неправильно реализует интерфейс - - - Класс '{0}' неправильно реализует интерфейс '{1}', так как '{2}' элемент отсутствует или не является общедоступным - - - Класс не запечатан - - - Экспорт незапечатанных типов не поддерживается в CsWinRT. Пометьте тип {0} как запечатанный - {0} will be some user-defined keyword - - - Предоставление неподдерживаемого типа - - - Элемент '{0}' имеет тип, '{1}' сигнатуре. - {0} and {1} will be user-defined keywords - - - Тип '{1}' не является допустимым среда выполнения Windows типа. - {1} will be some user-defined keyword - - - Однако тип (или его универсальные параметры) реализует интерфейсы, которые являются допустимыми типами среды выполнения Windows. - - - Попробуйте изменить тип "{1}" в подписи элемента на один из следующих типов из 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 + + + Параметр массива помечен как InAttribute или OutAttribute + + + Метод '{0}' имеет параметр '{1}', который является массивом и имеет атрибут System.Runtime.InteropServices.InAttribute или System.Runtime.InteropServices.OutAttribute. + + + В среде выполнения Windows параметры массива должны иметь метку ReadOnlyArray или WriteOnlyArray. + + + Удалите эти атрибуты или замените их соответствующим атрибутом среды выполнения Windows при необходимости. + + + Параметр массива помечен как out и ReadOnlyArray + + + Метод '{0}' имеет выходной '{1}', который является массивом, но имеет атрибут ReadOnlyArray. + + + В среда выполнения Windows записи содержимое выходных массивов. Удалите атрибут из '{1}'. + + + Параметр массива помечен одновременно как ReadOnlyArray и WriteOnlyArray + + + Метод '{0}' имеет параметр '{1}', который является массивом и имеет readOnlyArray и WriteOnlyArray. + + + В среда выполнения Windows параметры массива содержимого должны быть доступны для чтения или записи. Удалите один из атрибутов из '{1}'. + + + Параметр массива не помечен как ReadOnlyArray или WriteOnlyArray + + + Метод '{0}' имеет параметр '{1}', который является массивом. + + + В среде выполнения Windows содержимое параметров массива должно быть доступно для чтения или записи. Примените ReadOnlyArray или WriteOnlyArray к "{1}". + + + Правило конструктора классов + + + Классы не могут содержать несколько конструкторов с одинаковой арностью в среде выполнения Windows. Класс {0} содержит несколько конструкторов арности {1} + + + Пространство имен отсоединено от основного пространства имен (winmd) + + + Открытый тип имеет пространство имен ('{1}'), которое не имеет общего префикса с другими пространствами имен ('{0}'). + {1} and {0} will be some user-defined keyword + + + Все типы в файле метаданных Windows должны существовать во вложенном пространстве имен пространства имен, которое соответствует имени файла. + "sub namespace" means a namespace defined within another namespace + + + Класс (или интерфейс) является универсальным + + + Тип {0} является универсальным. Типы среды выполнения Windows не могут быть универсальными + {0} will be some user-defined keyword + + + Обнаружена подпись с массивом массивов, который не является допустимым типом WinRT + + + Метод {0} содержит вложенный массив типа {1} в подписи. Массивы в подписи метода среды выполнения Windows не могут быть вложенными + + + Обнаружена подпись массива с многомерным массивом, который не является допустимым типом среды выполнения Windows + + + Метод "{0}" содержит многомерный массив типа {1} в подписи. Массивы в подписях методов среды выполнения Windows должны быть одномерными + + + По умолчанию должна быть назначена только одна перегрузка + + + В классе {2}: множественные перегрузки "{1}" параметра {0} снабжены атрибутом Windows.Foundation.Metadata.DefaultOverloadAttribute. + + + Атрибут может применяться только к одной перегрузке метода. + + + Имена пространств имен не могут отличаться только регистром + + + Найдено несколько пространств имен с именем "{0}". Имена пространств имен не могут отличаться только регистром в среде выполнения Windows + {0} will be some user-defined keyword + + + Обнаружено несколько перегрузок без атрибута DefaultOverload + + + В классе {2}: перегрузки {1} параметра {0} должны использовать только один метод, указанный в качестве перегрузки по умолчанию, путем добавления Windows.Foundation.Metadata.DefaultOverloadAttribute + + + Параметр (не относящийся к массиву) помечен как InAttribute или OutAttribute + + + Метод "{0}" содержит параметр "{1}" с System.Runtime.InteropServices.InAttribute или System.Runtime.InteropServices.OutAttribute. Среда выполнения Windows не поддерживает маркировку параметров с помощью System.Runtime.InteropServices.InAttribute или System.Runtime.InteropServices.OutAttribute. + + + Рассмотрите возможность удаления System.Runtime.InteropServices.InAttribute и замены System.Runtime.InteropServices.OutAttribute модификатором "out". + + + Параметр, не относящийся к массиву, помечен как ReadOnlyArray или WriteOnlyArray + + + Метод '{0}' имеет параметр '{1}', который не является массивом и имеет либо атрибут ReadOnlyArray, либо атрибут WriteOnlyArray. + + + Среда выполнения Windows не поддерживает маркировку параметров, не относящихся к массиву, с помощью ReadOnlyArray или WriteOnlyArray. + + + Унаследованный интерфейс недопустим + + + Тип компонента среды выполнения Windows {0} не может реализовать интерфейс {1}, так как интерфейс не является допустимым интерфейсом среды выполнения Windows + + + Общедоступные типы не определены + + + Компоненты среды выполнения Windows должны содержать по крайней мере один общедоступный тип + + + Предоставлена перегрузка оператора + + + {0} является перегрузкой оператора. Управляемые типы не могут предоставлять доступ к перегрузкам операторов в среде выполнения Windows + + + Правило именованного значения параметра + + + Имя параметра {1} в методе {0} совпадает с именем параметра возвращаемого значения, используемым в созданном взаимодействии C#/WinRT. Примените другое имя параметра + + + Свойство должно иметь общедоступный метод получения + + + Свойство "{0}" не имеет общедоступного метода получения. Среда выполнения Windows не поддерживает свойства только метода задания. + {0} will be some user-defined keyword + + + Параметр, переданный ссылкой + + + Метод "{0}" содержит параметр "{1}", помеченный как "ref". Ссылочные параметры не разрешены в среде выполнения Windows + + + Поле константы в структуре + + + Структура {0} содержит поле константы — константы могут отображаться только в перечислениях среды выполнения Windows. + + + Недопустимое поле в структуре + + + Структура {0} имеет поле типа {1}; {1} не является допустимым типом среда выполнения Windows поля. + + + Каждое поле в структуре среды выполнения Windows может быть только типом UInt8, Int16, UInt16, Int32, UInt32, Int64, UInt64, Single, Double, Boolean, String, Enum или самостоятельной структурой. + + + Частное поле в структуре + + + Структура {0} содержит поле, не являющееся общедоступным. Для структур среды выполнения Windows все поля должны быть общедоступными. + + + Правило пустой структуры + + + Структура {0} не содержит общедоступных полей. Структуры среды выполнения Windows должны содержать по крайней мере одно общедоступное поле. + + + Класс неправильно реализует интерфейс + + + Класс '{0}' неправильно реализует интерфейс '{1}', так как '{2}' элемент отсутствует или не является общедоступным + + + Класс не запечатан + + + Экспорт незапечатанных типов не поддерживается в CsWinRT. Пометьте тип {0} как запечатанный + {0} will be some user-defined keyword + + + Предоставление неподдерживаемого типа + + + Элемент '{0}' имеет тип, '{1}' сигнатуре. + {0} and {1} will be user-defined keywords + + + Тип '{1}' не является допустимым среда выполнения Windows типа. + {1} will be some user-defined keyword + + + Однако тип (или его универсальные параметры) реализует интерфейсы, которые являются допустимыми типами среды выполнения Windows. + + + Попробуйте изменить тип "{1}" в подписи элемента на один из следующих типов из System.Collections.Generic: {2}. + {1} and {2} will be keywords (types) from DotNet + \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/ResX/zh-CN/CsWinRTDiagnosticStrings.resx b/src/Authoring/WinRT.SourceGenerator/ResX/zh-CN/CsWinRTDiagnosticStrings.resx index b20f79e9f..ed41a155d 100644 --- a/src/Authoring/WinRT.SourceGenerator/ResX/zh-CN/CsWinRTDiagnosticStrings.resx +++ b/src/Authoring/WinRT.SourceGenerator/ResX/zh-CN/CsWinRTDiagnosticStrings.resx @@ -1,271 +1,271 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - 标记为 InAttribute 或 OutAttribute 的数组参数 - - - 方法'{0}'具有作为数组的参数 '{1}',该参数具有 System.Runtime.InteropServices.InAttribute 或 System.Runtime.InteropServices.OutAttribute。 - - - 在 Windows 运行时中,数组参数必须具有 ReadOnlyArray 或 WriteOnlyArray。 - - - 如有必要,请删除这些属性或将其替换为适当的Windows 运行时属性。 - - - 标记为‘out’和 ReadOnlyArray 的数组参数 - - - 方法‘{0}’为具有输出参数‘{1}’的数组,但具有 ReadOnlyArray 属性。 - - - 在 Windows 运行时中,输出数组的内容是可写入的。请从‘{1}’中删除属性。 - - - 数组参数标记为 ReadOnlyArray 和 WriteOnlyArray - - - 方法‘{0}’为具有参数‘{1}’的数组,同时具有 ReadOnlyArray 和 WriteOnlyArray。 - - - 在 Windows 运行时中,内容数组参数必须可读或可写,请从‘{1}’中删除其中一个属性。 - - - 数组参数未标记为 ReadOnlyArray 或 WriteOnlyArray 方式 - - - 方法‘{0}’为具有参数‘{1}’的数组。 - - - 在 Windows 运行时中,数组参数的内容必须可读或可写; 请将 ReadOnlyArray 或 WriteOnlyArray 应用于‘{1}’。 - - - 类构造函数规则 - - - 类不能在 Windows 运行时中具有多个同一性的构造函数,类 {0} 具有多个 {1}-性构造函数 - - - 命名空间与主 (winmd) 命名空间不相互连接 - - - 公共类型具有一个命名空间 (‘{1}’),该命名空间与其他命名空间 (‘{0}’) 不共享公共前缀。 - {1} and {0} will be some user-defined keyword - - - Windows 元数据文件中的所有类型都必须存在于该文件名隐含的命名空间的子命名空间中。 - "sub namespace" means a namespace defined within another namespace - - - 类 (或接口) 为泛型 - - - 类型 {0} 为泛型,Windows 运行时类型不能为泛型 - {0} will be some user-defined keyword - - - 发现数组签名有锯齿状阵列,这不是有效的WinRT类型 - - - 方法 {0} 的签名中具有 {1} 类型的嵌套数组; 无法嵌套 Windows 运行时方法签名中的数组 - - - 使用多维数组找到数组签名,该数组不是有效的Windows 运行时类型 - - - 方法‘{0}’的签名中具有类型为‘{1}’的多维数组; Windows 运行时方法签名中的数组必须是一维的 - - - 默认情况下只应指定一个重载 - - - 在类 {2} 中;‘{1}’的多个 {0}-参数重载使用 Windows.Foundation.Metadata.DefaultOverloadAttribute 进行修饰。 - - - 该属性只能应用于方法的一个重载。 - - - 命名空间名称不能仅因大小写而异 - - - 找到名称为‘{0}’的多个名称空间; 在Windows 运行时中,名称空间的名称不能只因大小写而不同 - {0} will be some user-defined keyword - - - 在没有 DefaultOverload 属性的情况下看到多个重载 - - - 在类 {2}: {1} 的 {0}-参数重载必须通过使用 Windows.Foundation.Metadata.DefaultOverloadAttribute 对其进行修饰,将其指定为默认重载 - - - 标记为 InAttribute 或 OutAttribute 的参数 (非数组类型) - - - 方法‘{0}’具有带 System.Runtime.InteropServices.InAttribute 或 System.Runtime.InteropServices.OutAttribute 的参数‘{1}’。Windows 运行时不支持使用 System.Runtime.InteropServices.InAttribute 或 System.Runtime.InteropServices.OutAttribute 标记参数。 - - - 请考虑删除 System.Runtime.InteropServices.InAttribute,并将 System.Runtime.InteropServices.OutAttribute 替换为‘out’修饰符。 - - - 标有 ReadOnlyArray 或 WriteOnlyArray 的非数组参数 - - - 方法‘{0}’具有不为数组的参数‘{1},并且其具有 ReadOnlyArray 特性或 WriteOnlyArray 特性。 - - - Windows 运行时不支持使用 ReadOnlyArray 或 WriteOnlyArray 标记非数组参数。 - - - 继承的接口无效 - - - Windows 运行时组件类型{0}无法实现接口{1},因为该接口不是有效的Windows 运行时接口 - - - 未定义公共类型 - - - Windows 运行时组件必须至少有一个公共类型 - - - 运算符重载已公开 - - - {0} 运算符重载,托管类型无法在 Windows 运行时中公开运算符重载 - - - 参数命名值规则 - - - 方法{0}中{1}的参数名称与生成的 C#/WinRT 互操作中使用的返回值参数名称相同;使用其他参数名称 - - - 属性必须具有公共 getter - - - 属性'{0}'没有公共 getter 方法。Windows 运行时不支持仅 setter 属性。 - {0} will be some user-defined keyword - - - 通过引用传递的参数 - - - 方法‘{0}’具有标记为‘ref’的参数‘{1}’; Windows 运行时中不允许使用引用参数 - - - 结构中的常量字段 - - - 结构{0}具有常量字段 - 常量只能出现在Windows 运行时枚举上。 - - - 结构中的字段无效 - - - 结构{0}具有{1}类型的字段;{1}不是有效的Windows 运行时字段类型。 - - - Windows 运行时结构中的每个字段只能是 UInt8、Int16、UInt16、Int32、UInt32、Int64、UInt64、Single、Double、Boolean、String、Enum 或其自身的结构。 - - - 结构中的专用字段 - - - 结构 {0} 具有非公共字段。对于 Windows 运行时结构,所有字段都必须为公共字段。 - - - 空结构规则 - - - 结构{0}不包含公共字段。Windows 运行时结构必须至少包含一个公共字段。 - - - 类错误地实现接口 - - - 类‘{0}’未正确实现接口‘{1}’,因为缺少成员‘{2}’或非公开 - - - 类以解封 - - - CsWinRT 中不支持导出未密封的类型,请将类型{0}标记为密封 - {0} will be some user-defined keyword - - - 公开不受支持的类型 - - - 成员‘{0}’在其签名中具有类型‘{1}’。 - {0} and {1} will be user-defined keywords - - - 类型'{1}'不是有效的Windows 运行时类型。 - {1} will be some user-defined keyword - - - 但是,类型 (或其泛型参数) 实现的接口是有效的 Windows 运行时类型。 - - - 请考虑将成员签名中的类型‘{1} 更改为 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 + + + 标记为 InAttribute 或 OutAttribute 的数组参数 + + + 方法'{0}'具有作为数组的参数 '{1}',该参数具有 System.Runtime.InteropServices.InAttribute 或 System.Runtime.InteropServices.OutAttribute。 + + + 在 Windows 运行时中,数组参数必须具有 ReadOnlyArray 或 WriteOnlyArray。 + + + 如有必要,请删除这些属性或将其替换为适当的Windows 运行时属性。 + + + 标记为‘out’和 ReadOnlyArray 的数组参数 + + + 方法‘{0}’为具有输出参数‘{1}’的数组,但具有 ReadOnlyArray 属性。 + + + 在 Windows 运行时中,输出数组的内容是可写入的。请从‘{1}’中删除属性。 + + + 数组参数标记为 ReadOnlyArray 和 WriteOnlyArray + + + 方法‘{0}’为具有参数‘{1}’的数组,同时具有 ReadOnlyArray 和 WriteOnlyArray。 + + + 在 Windows 运行时中,内容数组参数必须可读或可写,请从‘{1}’中删除其中一个属性。 + + + 数组参数未标记为 ReadOnlyArray 或 WriteOnlyArray 方式 + + + 方法‘{0}’为具有参数‘{1}’的数组。 + + + 在 Windows 运行时中,数组参数的内容必须可读或可写; 请将 ReadOnlyArray 或 WriteOnlyArray 应用于‘{1}’。 + + + 类构造函数规则 + + + 类不能在 Windows 运行时中具有多个同一性的构造函数,类 {0} 具有多个 {1}-性构造函数 + + + 命名空间与主 (winmd) 命名空间不相互连接 + + + 公共类型具有一个命名空间 (‘{1}’),该命名空间与其他命名空间 (‘{0}’) 不共享公共前缀。 + {1} and {0} will be some user-defined keyword + + + Windows 元数据文件中的所有类型都必须存在于该文件名隐含的命名空间的子命名空间中。 + "sub namespace" means a namespace defined within another namespace + + + 类 (或接口) 为泛型 + + + 类型 {0} 为泛型,Windows 运行时类型不能为泛型 + {0} will be some user-defined keyword + + + 发现数组签名有锯齿状阵列,这不是有效的WinRT类型 + + + 方法 {0} 的签名中具有 {1} 类型的嵌套数组; 无法嵌套 Windows 运行时方法签名中的数组 + + + 使用多维数组找到数组签名,该数组不是有效的Windows 运行时类型 + + + 方法‘{0}’的签名中具有类型为‘{1}’的多维数组; Windows 运行时方法签名中的数组必须是一维的 + + + 默认情况下只应指定一个重载 + + + 在类 {2} 中;‘{1}’的多个 {0}-参数重载使用 Windows.Foundation.Metadata.DefaultOverloadAttribute 进行修饰。 + + + 该属性只能应用于方法的一个重载。 + + + 命名空间名称不能仅因大小写而异 + + + 找到名称为‘{0}’的多个名称空间; 在Windows 运行时中,名称空间的名称不能只因大小写而不同 + {0} will be some user-defined keyword + + + 在没有 DefaultOverload 属性的情况下看到多个重载 + + + 在类 {2}: {1} 的 {0}-参数重载必须通过使用 Windows.Foundation.Metadata.DefaultOverloadAttribute 对其进行修饰,将其指定为默认重载 + + + 标记为 InAttribute 或 OutAttribute 的参数 (非数组类型) + + + 方法‘{0}’具有带 System.Runtime.InteropServices.InAttribute 或 System.Runtime.InteropServices.OutAttribute 的参数‘{1}’。Windows 运行时不支持使用 System.Runtime.InteropServices.InAttribute 或 System.Runtime.InteropServices.OutAttribute 标记参数。 + + + 请考虑删除 System.Runtime.InteropServices.InAttribute,并将 System.Runtime.InteropServices.OutAttribute 替换为‘out’修饰符。 + + + 标有 ReadOnlyArray 或 WriteOnlyArray 的非数组参数 + + + 方法‘{0}’具有不为数组的参数‘{1},并且其具有 ReadOnlyArray 特性或 WriteOnlyArray 特性。 + + + Windows 运行时不支持使用 ReadOnlyArray 或 WriteOnlyArray 标记非数组参数。 + + + 继承的接口无效 + + + Windows 运行时组件类型{0}无法实现接口{1},因为该接口不是有效的Windows 运行时接口 + + + 未定义公共类型 + + + Windows 运行时组件必须至少有一个公共类型 + + + 运算符重载已公开 + + + {0} 运算符重载,托管类型无法在 Windows 运行时中公开运算符重载 + + + 参数命名值规则 + + + 方法{0}中{1}的参数名称与生成的 C#/WinRT 互操作中使用的返回值参数名称相同;使用其他参数名称 + + + 属性必须具有公共 getter + + + 属性'{0}'没有公共 getter 方法。Windows 运行时不支持仅 setter 属性。 + {0} will be some user-defined keyword + + + 通过引用传递的参数 + + + 方法‘{0}’具有标记为‘ref’的参数‘{1}’; Windows 运行时中不允许使用引用参数 + + + 结构中的常量字段 + + + 结构{0}具有常量字段 - 常量只能出现在Windows 运行时枚举上。 + + + 结构中的字段无效 + + + 结构{0}具有{1}类型的字段;{1}不是有效的Windows 运行时字段类型。 + + + Windows 运行时结构中的每个字段只能是 UInt8、Int16、UInt16、Int32、UInt32、Int64、UInt64、Single、Double、Boolean、String、Enum 或其自身的结构。 + + + 结构中的专用字段 + + + 结构 {0} 具有非公共字段。对于 Windows 运行时结构,所有字段都必须为公共字段。 + + + 空结构规则 + + + 结构{0}不包含公共字段。Windows 运行时结构必须至少包含一个公共字段。 + + + 类错误地实现接口 + + + 类‘{0}’未正确实现接口‘{1}’,因为缺少成员‘{2}’或非公开 + + + 类以解封 + + + CsWinRT 中不支持导出未密封的类型,请将类型{0}标记为密封 + {0} will be some user-defined keyword + + + 公开不受支持的类型 + + + 成员‘{0}’在其签名中具有类型‘{1}’。 + {0} and {1} will be user-defined keywords + + + 类型'{1}'不是有效的Windows 运行时类型。 + {1} will be some user-defined keyword + + + 但是,类型 (或其泛型参数) 实现的接口是有效的 Windows 运行时类型。 + + + 请考虑将成员签名中的类型‘{1} 更改为 System.Collections.Generic 中的以下类型之一: {2}。 + {1} and {2} will be keywords (types) from DotNet + \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/ResX/zh-TW/CsWinRTDiagnosticStrings.resx b/src/Authoring/WinRT.SourceGenerator/ResX/zh-TW/CsWinRTDiagnosticStrings.resx index f19607b03..c593122d9 100644 --- a/src/Authoring/WinRT.SourceGenerator/ResX/zh-TW/CsWinRTDiagnosticStrings.resx +++ b/src/Authoring/WinRT.SourceGenerator/ResX/zh-TW/CsWinRTDiagnosticStrings.resx @@ -1,271 +1,271 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 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 - - - 陣列參數標示為 InAttribute 或 OutAttribute - - - 方法 [{0}] 具有參數 [{1}],它是陣列,而且具有 System.Runtime.InteropServices.InAttribute 或 System.Runtime.InteropServices.OutAttribute。 - - - 在Windows 執行階段中,陣列參數必須有 ReadOnlyArray 或 WriteOnlyArray。 - - - 請移除這些屬性,或視需要以適當的 Windows 執行階段屬性加以取代。 - - - 陣列參數已標示為 [out] 和 ReadOnlyArray - - - 方法 [{0}] 具有輸出參數 [{1}] 這是陣列,但具有 ReadOnlyArray 屬性。 - - - 在Windows 執行階段中,輸出陣列的內容是可寫入的。請從'{1}'移除屬性。 - - - 陣列參數同時標示為 ReadOnlyArray 和 WriteOnlyArray - - - 方法 [{0}] 具有參數 [{1}],它是一個陣列,而且同時具有 ReadOnlyArray 和 WriteOnlyArray。 - - - 在Windows 執行階段中,內容陣列參數必須是可讀取或可寫入,請從'{1}'移除其中一個屬性。 - - - 陣列參數未標示為 ReadOnlyArray 或 WriteOnlyArray 方式 - - - 方法'{0}'具有參數'{1}'為數組。 - - - 在Windows 執行階段中,陣列參數的內容必須是可讀取或可寫入;請將 ReadOnlyArray 或 WriteOnlyArray 套用至 '{1}'。 - - - 類別建構函式規則 - - - 類別在 Windows 執行階段中不能有多個相同 arity 的建構函式,類別 {0} 具有多個 {1} arity 建構函式 - - - 命名空間與主要 (winmd) 命名空間不相符 - - - 公用類型具有命名空間 ('{1}')與其他命名空間 ('{0}')沒有共同首碼。 - {1} and {0} will be some user-defined keyword - - - Windows 中繼資料檔案內的所有類型都必須存在於檔案名所隱含的命名空間子命名空間中。 - "sub namespace" means a namespace defined within another namespace - - - 類別 (或介面) 為一般 - - - 類型 {0} 為一般,Windows 執行階段類型不可以是一般 - {0} will be some user-defined keyword - - - 找到有鋸齒狀陣列的陣列簽章,這不是有效的 WinRT 類型 - - - 方法 {0} 的簽章中有類型為 {1} 的巢狀陣列;Windows 執行階段方法簽章中的陣列無法為巢狀 - - - 使用多重維度陣列找到陣列簽章,這不是有效的 Windows 執行時間類型 - - - 方法 [{0}] 的簽章中有類型為 [{1}] 的多維陣列;Windows 執行階段方法簽章中的陣列必須是一維 - - - 只應指定一個超載預設值 - - - 在類別 {2}: 使用 Windows.Foundation.Metadata.DefaultOverloadAttribute 裝飾多個 {1} 的 {0}-參數超載。 - - - 屬性只能套用至方法的一個超載。 - - - 命名空間名稱不能只因大小寫而不同 - - - 找到多個名稱為 '{0}';命名空間名稱不能只因Windows 執行階段中的大小寫不同 - {0} will be some user-defined keyword - - - 已查看多個超載,但未顯示 DefaultOverload 屬性 - - - 類別{2}: {1} 的 {0}-參數超載必須使用 Windows.Foundation.Metadata.DefaultOverloadAttribute 裝飾,將它指定為預設超載的方法 - - - 參數(標示為 InAttribute 或 OutAttribute)陣列類型 - - - 方法 [{0}] 具有具有 System.Runtime.InteropServices.InAttribute 或 System.Runtime.InteropServices.OutAttribute.Windows 執行階段的 [{1}] 參數 Windows 執行階段不支援標記具有 System.Runtime.InteropServices.InAttribute 或 System.Runtime.InteropServices.OutAttribute 的參數。 - - - 請考慮移除 System.Runtime.InteropServices.InAttribute,並改用 'out' 修飾詞取代 System.Runtime.InteropServices.OutAttribute。 - - - 以 ReadOnlyArray 或 WriteOnlyArray 標示的非陣列參數 - - - 方法 [{0}] 具有非陣列的參數 [{1}],而且該參數具有 ReadOnlyArray 屬性或 WriteOnlyArray 屬性。 - - - Windows 執行階段不支援以 ReadOnlyArray 或 WriteOnlyArray 標示非陣列參數。 - - - 繼承的介面無效 - - - Windows 執行階段元件類型{0}無法實作介面{1},因為介面不是有效的Windows 執行階段介面 - - - 未定義公用類型 - - - Windows 執行階段元件必須至少有一個公用類型 - - - 運算子超載已公開 - - - {0} 為運算子超載,受管理類型無法在Windows 執行階段中公開運算子超載 - - - 參數命名值規則 - - - 方法 {0} 中{1}的參數名稱,與產生 C#/WinRT interop 中使用的傳回值參數名稱相同;使用不同的參數名稱 - - - 屬性必須有公用 getter - - - 屬性'{0}'沒有公用 getter 方法。Windows 執行階段不支援 setter 專用屬性。 - {0} will be some user-defined keyword - - - 參考所傳遞的參數 - - - 方法 [{0}] 具有參數 [{1}] 標示為 `ref';Windows 執行階段中不允許參照參數 - - - 結構中的 Const 欄位 - - - 結構 {0} 具有常數位段 - 常數只能出現在Windows 執行階段列舉上。 - - - 結構中的欄位無效 - - - 結構{0}具有類型為 {1};{1}不是有效的Windows 執行階段欄位類型。 - - - 在 Windows 執行階段結構中的每個欄位只能是 UInt8、Int16、UInt16、Int32、UInt32、Int64、UInt64、Single、Double、Boolean、String、Enum 或本身的結構。 - - - 結構中的私用欄位 - - - 結構 {0} 具有非公用欄位。Windows 執行時間結構的所有欄位都必須為公用。 - - - 空白結構規則 - - - 結構{0}未包含公用欄位。Windows 執行階段結構至少必須包含一個公用欄位。 - - - 類別不正確地實作介面 - - - 類別'{0}'未正確實作介面'{1}',因為成員'{2}'遺失或非公用 - - - 類別未密封 - - - CsWinRT 不支援匯出未密封的類型,請將類型 {0} 標示為密封 - {0} will be some user-defined keyword - - - 正在公開不支援的類型 - - - 成員'{0}'的簽章中有類型'{1}'。 - {0} and {1} will be user-defined keywords - - - 類型 '{1}' 不是有效的Windows 執行階段類型。 - {1} will be some user-defined keyword - - - 不過,此類型 (或它的一般參數) 會執行有效的 Windows 執行時間類型的介面。 - - - 請考慮將成員簽章中的類型 ‘{1} 從 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 + + + 陣列參數標示為 InAttribute 或 OutAttribute + + + 方法 [{0}] 具有參數 [{1}],它是陣列,而且具有 System.Runtime.InteropServices.InAttribute 或 System.Runtime.InteropServices.OutAttribute。 + + + 在Windows 執行階段中,陣列參數必須有 ReadOnlyArray 或 WriteOnlyArray。 + + + 請移除這些屬性,或視需要以適當的 Windows 執行階段屬性加以取代。 + + + 陣列參數已標示為 [out] 和 ReadOnlyArray + + + 方法 [{0}] 具有輸出參數 [{1}] 這是陣列,但具有 ReadOnlyArray 屬性。 + + + 在Windows 執行階段中,輸出陣列的內容是可寫入的。請從'{1}'移除屬性。 + + + 陣列參數同時標示為 ReadOnlyArray 和 WriteOnlyArray + + + 方法 [{0}] 具有參數 [{1}],它是一個陣列,而且同時具有 ReadOnlyArray 和 WriteOnlyArray。 + + + 在Windows 執行階段中,內容陣列參數必須是可讀取或可寫入,請從'{1}'移除其中一個屬性。 + + + 陣列參數未標示為 ReadOnlyArray 或 WriteOnlyArray 方式 + + + 方法'{0}'具有參數'{1}'為數組。 + + + 在Windows 執行階段中,陣列參數的內容必須是可讀取或可寫入;請將 ReadOnlyArray 或 WriteOnlyArray 套用至 '{1}'。 + + + 類別建構函式規則 + + + 類別在 Windows 執行階段中不能有多個相同 arity 的建構函式,類別 {0} 具有多個 {1} arity 建構函式 + + + 命名空間與主要 (winmd) 命名空間不相符 + + + 公用類型具有命名空間 ('{1}')與其他命名空間 ('{0}')沒有共同首碼。 + {1} and {0} will be some user-defined keyword + + + Windows 中繼資料檔案內的所有類型都必須存在於檔案名所隱含的命名空間子命名空間中。 + "sub namespace" means a namespace defined within another namespace + + + 類別 (或介面) 為一般 + + + 類型 {0} 為一般,Windows 執行階段類型不可以是一般 + {0} will be some user-defined keyword + + + 找到有鋸齒狀陣列的陣列簽章,這不是有效的 WinRT 類型 + + + 方法 {0} 的簽章中有類型為 {1} 的巢狀陣列;Windows 執行階段方法簽章中的陣列無法為巢狀 + + + 使用多重維度陣列找到陣列簽章,這不是有效的 Windows 執行時間類型 + + + 方法 [{0}] 的簽章中有類型為 [{1}] 的多維陣列;Windows 執行階段方法簽章中的陣列必須是一維 + + + 只應指定一個超載預設值 + + + 在類別 {2}: 使用 Windows.Foundation.Metadata.DefaultOverloadAttribute 裝飾多個 {1} 的 {0}-參數超載。 + + + 屬性只能套用至方法的一個超載。 + + + 命名空間名稱不能只因大小寫而不同 + + + 找到多個名稱為 '{0}';命名空間名稱不能只因Windows 執行階段中的大小寫不同 + {0} will be some user-defined keyword + + + 已查看多個超載,但未顯示 DefaultOverload 屬性 + + + 類別{2}: {1} 的 {0}-參數超載必須使用 Windows.Foundation.Metadata.DefaultOverloadAttribute 裝飾,將它指定為預設超載的方法 + + + 參數(標示為 InAttribute 或 OutAttribute)陣列類型 + + + 方法 [{0}] 具有具有 System.Runtime.InteropServices.InAttribute 或 System.Runtime.InteropServices.OutAttribute.Windows 執行階段的 [{1}] 參數 Windows 執行階段不支援標記具有 System.Runtime.InteropServices.InAttribute 或 System.Runtime.InteropServices.OutAttribute 的參數。 + + + 請考慮移除 System.Runtime.InteropServices.InAttribute,並改用 'out' 修飾詞取代 System.Runtime.InteropServices.OutAttribute。 + + + 以 ReadOnlyArray 或 WriteOnlyArray 標示的非陣列參數 + + + 方法 [{0}] 具有非陣列的參數 [{1}],而且該參數具有 ReadOnlyArray 屬性或 WriteOnlyArray 屬性。 + + + Windows 執行階段不支援以 ReadOnlyArray 或 WriteOnlyArray 標示非陣列參數。 + + + 繼承的介面無效 + + + Windows 執行階段元件類型{0}無法實作介面{1},因為介面不是有效的Windows 執行階段介面 + + + 未定義公用類型 + + + Windows 執行階段元件必須至少有一個公用類型 + + + 運算子超載已公開 + + + {0} 為運算子超載,受管理類型無法在Windows 執行階段中公開運算子超載 + + + 參數命名值規則 + + + 方法 {0} 中{1}的參數名稱,與產生 C#/WinRT interop 中使用的傳回值參數名稱相同;使用不同的參數名稱 + + + 屬性必須有公用 getter + + + 屬性'{0}'沒有公用 getter 方法。Windows 執行階段不支援 setter 專用屬性。 + {0} will be some user-defined keyword + + + 參考所傳遞的參數 + + + 方法 [{0}] 具有參數 [{1}] 標示為 `ref';Windows 執行階段中不允許參照參數 + + + 結構中的 Const 欄位 + + + 結構 {0} 具有常數位段 - 常數只能出現在Windows 執行階段列舉上。 + + + 結構中的欄位無效 + + + 結構{0}具有類型為 {1};{1}不是有效的Windows 執行階段欄位類型。 + + + 在 Windows 執行階段結構中的每個欄位只能是 UInt8、Int16、UInt16、Int32、UInt32、Int64、UInt64、Single、Double、Boolean、String、Enum 或本身的結構。 + + + 結構中的私用欄位 + + + 結構 {0} 具有非公用欄位。Windows 執行時間結構的所有欄位都必須為公用。 + + + 空白結構規則 + + + 結構{0}未包含公用欄位。Windows 執行階段結構至少必須包含一個公用欄位。 + + + 類別不正確地實作介面 + + + 類別'{0}'未正確實作介面'{1}',因為成員'{2}'遺失或非公用 + + + 類別未密封 + + + CsWinRT 不支援匯出未密封的類型,請將類型 {0} 標示為密封 + {0} will be some user-defined keyword + + + 正在公開不支援的類型 + + + 成員'{0}'的簽章中有類型'{1}'。 + {0} and {1} will be user-defined keywords + + + 類型 '{1}' 不是有效的Windows 執行階段類型。 + {1} will be some user-defined keyword + + + 不過,此類型 (或它的一般參數) 會執行有效的 Windows 執行時間類型的介面。 + + + 請考慮將成員簽章中的類型 ‘{1} 從 System.Collections.Generic 變更為下列其中一種類型: {2}。 + {1} and {2} will be keywords (types) from DotNet + \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/TypeMapper.cs b/src/Authoring/WinRT.SourceGenerator/TypeMapper.cs new file mode 100644 index 000000000..b591e76b0 --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator/TypeMapper.cs @@ -0,0 +1,125 @@ +using Microsoft.CodeAnalysis; +using System; +using System.Collections.Generic; +using System.Text; +using static Generator.GeneratorHelper; + +namespace Generator +{ + internal sealed class TypeMapper + { + private readonly Dictionary typeMapping; + + // Based on whether System.Type is used in an attribute declaration or elsewhere, we need to choose the correct custom mapping + // as attributes don't use the TypeName mapping. + private static (string, string, string, bool, bool) GetSystemTypeCustomMapping(ISymbol containingSymbol) + { + bool isDefinedInAttribute = + containingSymbol != null && + string.CompareOrdinal((containingSymbol as INamedTypeSymbol).BaseType?.ToString(), "System.Attribute") == 0; + return isDefinedInAttribute ? + ("System", "Type", "mscorlib", true, false) : + ("Windows.UI.Xaml.Interop", "TypeName", "Windows.Foundation.UniversalApiContract", false, true); + } + + public TypeMapper(bool useWindowsUIXamlProjections) + { + // This should be in sync with the reverse mapping from WinRT.Runtime/Projections.cs and cswinrt/helpers.h. + if (useWindowsUIXamlProjections) + { + typeMapping = new(StringComparer.Ordinal) + { + { "System.DateTimeOffset", new MappedType("Windows.Foundation", "DateTime", "Windows.Foundation.FoundationContract", true, false) }, + { "System.Exception", new MappedType("Windows.Foundation", "HResult", "Windows.Foundation.FoundationContract", true, false) }, + { "System.EventHandler`1", new MappedType("Windows.Foundation", "EventHandler`1", "Windows.Foundation.FoundationContract") }, + { "System.FlagsAttribute", new MappedType("System", "FlagsAttribute", "mscorlib" ) }, + { "System.IDisposable", new MappedType("Windows.Foundation", "IClosable", "Windows.Foundation.FoundationContract") }, + { "System.Nullable`1", new MappedType("Windows.Foundation", "IReference`1", "Windows.Foundation.FoundationContract" ) }, + { "System.Object", new MappedType("System", "Object", "mscorlib" ) }, + { "System.TimeSpan", new MappedType("Windows.Foundation", "TimeSpan", "Windows.Foundation.FoundationContract", true, false) }, + { "System.Uri", new MappedType("Windows.Foundation", "Uri", "Windows.Foundation.FoundationContract") }, + { "System.ComponentModel.INotifyPropertyChanged", new MappedType("Windows.UI.Xaml.Data", "INotifyPropertyChanged", "Windows.UI.Xaml") }, + { "System.ComponentModel.PropertyChangedEventArgs", new MappedType("Windows.UI.Xaml.Data", "PropertyChangedEventArgs", "Windows.UI.Xaml") }, + { "System.ComponentModel.PropertyChangedEventHandler", new MappedType("Windows.UI.Xaml.Data", "PropertyChangedEventHandler", "Windows.UI.Xaml") }, + { "System.Windows.Input.ICommand", new MappedType("Windows.UI.Xaml.Input", "ICommand", "Windows.UI.Xaml") }, + { "System.Collections.IEnumerable", new MappedType("Windows.UI.Xaml.Interop", "IBindableIterable", "Windows.UI.Xaml") }, + { "System.Collections.IList", new MappedType("Windows.UI.Xaml.Interop", "IBindableVector", "Windows.UI.Xaml") }, + { "System.Collections.Specialized.INotifyCollectionChanged", new MappedType("Windows.UI.Xaml.Interop", "INotifyCollectionChanged", "Windows.UI.Xaml") }, + { "System.Collections.Specialized.NotifyCollectionChangedAction", new MappedType("Windows.UI.Xaml.Interop", "NotifyCollectionChangedAction", "Windows.UI.Xaml", true, true) }, + { "System.Collections.Specialized.NotifyCollectionChangedEventArgs", new MappedType("Windows.UI.Xaml.Interop", "NotifyCollectionChangedEventArgs", "Windows.UI.Xaml") }, + { "System.Collections.Specialized.NotifyCollectionChangedEventHandler", new MappedType("Windows.UI.Xaml.Interop", "NotifyCollectionChangedEventHandler", "Windows.UI.Xaml") }, + { "WinRT.EventRegistrationToken", new MappedType("Windows.Foundation", "EventRegistrationToken", "Windows.Foundation.FoundationContract", true, true) }, + { "System.AttributeTargets", new MappedType("Windows.Foundation.Metadata", "AttributeTargets", "Windows.Foundation.FoundationContract", true, true) }, + { "System.AttributeUsageAttribute", new MappedType("Windows.Foundation.Metadata", "AttributeUsageAttribute", "Windows.Foundation.FoundationContract") }, + { "System.Numerics.Matrix3x2", new MappedType("Windows.Foundation.Numerics", "Matrix3x2", "Windows.Foundation.FoundationContract", true, true) }, + { "System.Numerics.Matrix4x4", new MappedType("Windows.Foundation.Numerics", "Matrix4x4", "Windows.Foundation.FoundationContract", true, true) }, + { "System.Numerics.Plane", new MappedType("Windows.Foundation.Numerics", "Plane", "Windows.Foundation.FoundationContract", true, true) }, + { "System.Numerics.Quaternion", new MappedType("Windows.Foundation.Numerics", "Quaternion", "Windows.Foundation.FoundationContract", true, true) }, + { "System.Numerics.Vector2", new MappedType("Windows.Foundation.Numerics", "Vector2", "Windows.Foundation.FoundationContract", true, true) }, + { "System.Numerics.Vector3", new MappedType("Windows.Foundation.Numerics", "Vector3", "Windows.Foundation.FoundationContract", true, true) }, + { "System.Numerics.Vector4", new MappedType("Windows.Foundation.Numerics", "Vector4", "Windows.Foundation.FoundationContract", true, true) }, + { "System.Type", new MappedType(GetSystemTypeCustomMapping) }, + { "System.Collections.Generic.IEnumerable`1", new MappedType("Windows.Foundation.Collections", "IIterable`1", "Windows.Foundation.FoundationContract") }, + { "System.Collections.Generic.IEnumerator`1", new MappedType("Windows.Foundation.Collections", "IIterator`1", "Windows.Foundation.FoundationContract") }, + { "System.Collections.Generic.KeyValuePair`2", new MappedType("Windows.Foundation.Collections", "IKeyValuePair`2", "Windows.Foundation.FoundationContract") }, + { "System.Collections.Generic.IReadOnlyDictionary`2", new MappedType("Windows.Foundation.Collections", "IMapView`2", "Windows.Foundation.FoundationContract") }, + { "System.Collections.Generic.IDictionary`2", new MappedType("Windows.Foundation.Collections", "IMap`2", "Windows.Foundation.FoundationContract") }, + { "System.Collections.Generic.IReadOnlyList`1", new MappedType("Windows.Foundation.Collections", "IVectorView`1", "Windows.Foundation.FoundationContract") }, + { "System.Collections.Generic.IList`1", new MappedType("Windows.Foundation.Collections", "IVector`1", "Windows.Foundation.FoundationContract") }, + { "Windows.UI.Color", new MappedType("Windows.UI", "Color", "Windows.Foundation.UniversalApiContract", true, true) }, + }; + } + else + { + typeMapping = new(StringComparer.Ordinal) + { + { "System.DateTimeOffset", new MappedType("Windows.Foundation", "DateTime", "Windows.Foundation.FoundationContract", true, false) }, + { "System.Exception", new MappedType("Windows.Foundation", "HResult", "Windows.Foundation.FoundationContract", true, false) }, + { "System.EventHandler`1", new MappedType("Windows.Foundation", "EventHandler`1", "Windows.Foundation.FoundationContract") }, + { "System.FlagsAttribute", new MappedType("System", "FlagsAttribute", "mscorlib" ) }, + { "System.IDisposable", new MappedType("Windows.Foundation", "IClosable", "Windows.Foundation.FoundationContract") }, + { "System.IServiceProvider", new MappedType("Microsoft.UI.Xaml", "IXamlServiceProvider", "Microsoft.UI") }, + { "System.Nullable`1", new MappedType("Windows.Foundation", "IReference`1", "Windows.Foundation.FoundationContract" ) }, + { "System.Object", new MappedType("System", "Object", "mscorlib" ) }, + { "System.TimeSpan", new MappedType("Windows.Foundation", "TimeSpan", "Windows.Foundation.FoundationContract", true, false) }, + { "System.Uri", new MappedType("Windows.Foundation", "Uri", "Windows.Foundation.FoundationContract") }, + { "System.ComponentModel.DataErrorsChangedEventArgs", new MappedType("Microsoft.UI.Xaml.Data", "DataErrorsChangedEventArgs", "Microsoft.UI") }, + { "System.ComponentModel.INotifyDataErrorInfo", new MappedType("Microsoft.UI.Xaml.Data", "INotifyDataErrorInfo", "Microsoft.UI") }, + { "System.ComponentModel.INotifyPropertyChanged", new MappedType("Microsoft.UI.Xaml.Data", "INotifyPropertyChanged", "Microsoft.UI") }, + { "System.ComponentModel.PropertyChangedEventArgs", new MappedType("Microsoft.UI.Xaml.Data", "PropertyChangedEventArgs", "Microsoft.UI") }, + { "System.ComponentModel.PropertyChangedEventHandler", new MappedType("Microsoft.UI.Xaml.Data", "PropertyChangedEventHandler", "Microsoft.UI") }, + { "System.Windows.Input.ICommand", new MappedType("Microsoft.UI.Xaml.Input", "ICommand", "Microsoft.UI") }, + { "System.Collections.IEnumerable", new MappedType("Microsoft.UI.Xaml.Interop", "IBindableIterable", "Microsoft.UI") }, + { "System.Collections.IList", new MappedType("Microsoft.UI.Xaml.Interop", "IBindableVector", "Microsoft.UI") }, + { "System.Collections.Specialized.INotifyCollectionChanged", new MappedType("Microsoft.UI.Xaml.Interop", "INotifyCollectionChanged", "Microsoft.UI") }, + { "System.Collections.Specialized.NotifyCollectionChangedAction", new MappedType("Microsoft.UI.Xaml.Interop", "NotifyCollectionChangedAction", "Microsoft.UI", true, true) }, + { "System.Collections.Specialized.NotifyCollectionChangedEventArgs", new MappedType("Microsoft.UI.Xaml.Interop", "NotifyCollectionChangedEventArgs", "Microsoft.UI") }, + { "System.Collections.Specialized.NotifyCollectionChangedEventHandler", new MappedType("Microsoft.UI.Xaml.Interop", "NotifyCollectionChangedEventHandler", "Microsoft.UI") }, + { "WinRT.EventRegistrationToken", new MappedType("Windows.Foundation", "EventRegistrationToken", "Windows.Foundation.FoundationContract", true, true) }, + { "System.AttributeTargets", new MappedType("Windows.Foundation.Metadata", "AttributeTargets", "Windows.Foundation.FoundationContract", true, true) }, + { "System.AttributeUsageAttribute", new MappedType("Windows.Foundation.Metadata", "AttributeUsageAttribute", "Windows.Foundation.FoundationContract") }, + { "System.Numerics.Matrix3x2", new MappedType("Windows.Foundation.Numerics", "Matrix3x2", "Windows.Foundation.FoundationContract", true, true) }, + { "System.Numerics.Matrix4x4", new MappedType("Windows.Foundation.Numerics", "Matrix4x4", "Windows.Foundation.FoundationContract", true, true) }, + { "System.Numerics.Plane", new MappedType("Windows.Foundation.Numerics", "Plane", "Windows.Foundation.FoundationContract", true, true) }, + { "System.Numerics.Quaternion", new MappedType("Windows.Foundation.Numerics", "Quaternion", "Windows.Foundation.FoundationContract", true, true) }, + { "System.Numerics.Vector2", new MappedType("Windows.Foundation.Numerics", "Vector2", "Windows.Foundation.FoundationContract", true, true) }, + { "System.Numerics.Vector3", new MappedType("Windows.Foundation.Numerics", "Vector3", "Windows.Foundation.FoundationContract", true, true) }, + { "System.Numerics.Vector4", new MappedType("Windows.Foundation.Numerics", "Vector4", "Windows.Foundation.FoundationContract", true, true) }, + { "System.Type", new MappedType(GetSystemTypeCustomMapping) }, + { "System.Collections.Generic.IEnumerable`1", new MappedType("Windows.Foundation.Collections", "IIterable`1", "Windows.Foundation.FoundationContract") }, + { "System.Collections.Generic.IEnumerator`1", new MappedType("Windows.Foundation.Collections", "IIterator`1", "Windows.Foundation.FoundationContract") }, + { "System.Collections.Generic.KeyValuePair`2", new MappedType("Windows.Foundation.Collections", "IKeyValuePair`2", "Windows.Foundation.FoundationContract") }, + { "System.Collections.Generic.IReadOnlyDictionary`2", new MappedType("Windows.Foundation.Collections", "IMapView`2", "Windows.Foundation.FoundationContract") }, + { "System.Collections.Generic.IDictionary`2", new MappedType("Windows.Foundation.Collections", "IMap`2", "Windows.Foundation.FoundationContract") }, + { "System.Collections.Generic.IReadOnlyList`1", new MappedType("Windows.Foundation.Collections", "IVectorView`1", "Windows.Foundation.FoundationContract") }, + { "System.Collections.Generic.IList`1", new MappedType("Windows.Foundation.Collections", "IVector`1", "Windows.Foundation.FoundationContract") }, + { "Windows.UI.Color", new MappedType("Windows.UI", "Color", "Windows.Foundation.UniversalApiContract", true, true) }, + }; + } + } + + public bool HasMappingForType(string typeName) => typeMapping.ContainsKey(typeName); + + public MappedType GetMappedType(string typeName) => typeMapping[typeName]; + } +} diff --git a/src/Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.csproj b/src/Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.csproj deleted file mode 100644 index dd02d9c1e..000000000 --- a/src/Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.csproj +++ /dev/null @@ -1,44 +0,0 @@ - - - - netstandard2.0 - preview - Microsoft Corporation - Microsoft Corporation - C#/WinRT - WinRT.SourceGenerator - $(VersionNumber) - $(VersionNumber) - $(VersionNumber) - $(VersionNumber) - en - C#/WinRT Authoring Source Generator Preview $(VersionString) - C#/WinRT Authoring Source Generator Preview v$(VersionString) - Copyright (c) Microsoft Corporation. All rights reserved. - - - - - - - - - - - - - - - True - True - CsWinRTDiagnosticStrings.resx - - - - - ResXFileCodeGenerator - CsWinRTDiagnosticStrings.Designer.cs - - - - diff --git a/src/Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.projitems b/src/Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.projitems new file mode 100644 index 000000000..3a9ee53d4 --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.projitems @@ -0,0 +1,65 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + b0e5e9cc-5fb4-4f94-bd54-115a91a6ae9d + + + WinRT.SourceGenerator + + + + + + + + + + + + + CsWinRTDiagnosticStrings.resx + True + True + + + + + + + + + + + + + + + + + + + + + + CsWinRTDiagnosticStrings.Designer.cs + ResXFileCodeGenerator + WinRT.SourceGenerator.CsWinRTDiagnosticStrings.resources + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.props b/src/Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.props new file mode 100644 index 000000000..e9aa39fed --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.props @@ -0,0 +1,71 @@ + + + + netstandard2.0 + 12.0 + Microsoft Corporation + Microsoft Corporation + C#/WinRT + WinRT.SourceGenerator + $(VersionNumber) + $(VersionNumber) + $(VersionNumber) + $(VersionNumber) + en + C#/WinRT Authoring Source Generator Preview $(VersionString) + C#/WinRT Authoring Source Generator Preview v$(VersionString) + Copyright (c) Microsoft Corporation. All rights reserved. + true + $(SolutionDir)WinRT.Runtime\key.snk + true + true + + + embedded + + + + + + + $(MSBuildProjectName.Substring(0, $([MSBuild]::Subtract($(MSBuildProjectName.Length), 11)))) + + + $(MSBuildProjectName.Substring($([MSBuild]::Subtract($(MSBuildProjectName.Length), 4)), 1)) + $(MSBuildProjectName.Substring($([MSBuild]::Subtract($(MSBuildProjectName.Length), 3)), 2)) + $(MSBuildProjectName.Substring($([MSBuild]::Subtract($(MSBuildProjectName.Length), 1)), 1)) + $(CsWinRTSourceGeneratorRoslynMajorVersion).$(CsWinRTSourceGeneratorRoslynMinorVersion).$(CsWinRTSourceGeneratorRoslynPatchVersion) + + + $(DefineConstants);ROSLYN_4_8_0_OR_GREATER + $(DefineConstants);ROSLYN_4_12_0_OR_GREATER + + + $(NoWarn);RS2003 + + + + + + + + \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.shproj b/src/Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.shproj new file mode 100644 index 000000000..c1ba05e5b --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator/WinRT.SourceGenerator.shproj @@ -0,0 +1,13 @@ + + + + b0e5e9cc-5fb4-4f94-bd54-115a91a6ae9d + 14.0 + + + + + + + + \ No newline at end of file diff --git a/src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs b/src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs new file mode 100644 index 000000000..7154b4c03 --- /dev/null +++ b/src/Authoring/WinRT.SourceGenerator/WinRTAotCodeFixer.cs @@ -0,0 +1,543 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using Generator; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeActions; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Composition; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace WinRT.SourceGenerator +{ + [DiagnosticAnalyzer(LanguageNames.CSharp), Shared] + public sealed class WinRTAotDiagnosticAnalyzer : DiagnosticAnalyzer + { + private static ImmutableArray _supportedDiagnostics = + ImmutableArray.Create( + WinRTRules.ClassNotAotCompatibleWarning, + WinRTRules.ClassNotAotCompatibleInfo, + WinRTRules.ClassNotAotCompatibleOldProjectionWarning, + WinRTRules.ClassNotAotCompatibleOldProjectionInfo, + WinRTRules.ClassEnableUnsafeWarning, + WinRTRules.ClassEnableUnsafeInfo, + WinRTRules.ClassWithBindableCustomPropertyNotPartial); + + public override ImmutableArray SupportedDiagnostics => _supportedDiagnostics; + + public override void Initialize(AnalysisContext context) + { + context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics); + context.EnableConcurrentExecution(); + + context.RegisterCompilationStartAction(static context => + { + if (!context.Options.AnalyzerConfigOptionsProvider.IsCsWinRTAotOptimizerEnabled()) + { + return; + } + + bool isComponentProject = context.Options.AnalyzerConfigOptionsProvider.IsCsWinRTComponent(); + var winrtTypeAttribute = context.Compilation.GetTypeByMetadataName("WinRT.WindowsRuntimeTypeAttribute"); + var winrtExposedTypeAttribute = context.Compilation.GetTypeByMetadataName("WinRT.WinRTExposedTypeAttribute"); + var generatedBindableCustomPropertyAttribute = context.Compilation.GetTypeByMetadataName("WinRT.GeneratedBindableCustomPropertyAttribute"); + if (winrtTypeAttribute is null || winrtExposedTypeAttribute is null || generatedBindableCustomPropertyAttribute is null) + { + return; + } + + bool isCsWinRTAotOptimizerAutoMode = GeneratorExecutionContextHelper.IsCsWinRTAotOptimizerInAutoMode(context.Options.AnalyzerConfigOptionsProvider, context.Compilation); + var generatedWinRTExposedTypeAttribute = context.Compilation.GetTypeByMetadataName("WinRT.GeneratedWinRTExposedTypeAttribute"); + var generatedWinRTExposedExternalTypeAttribute = context.Compilation.GetTypeByMetadataName("WinRT.GeneratedWinRTExposedExternalTypeAttribute"); + if (!isCsWinRTAotOptimizerAutoMode && (generatedWinRTExposedTypeAttribute is null || generatedWinRTExposedExternalTypeAttribute is null)) + { + return; + } + + var typeMapper = new TypeMapper(context.Options.AnalyzerConfigOptionsProvider.GetCsWinRTUseWindowsUIXamlProjections()); + var csWinRTAotWarningLevel = context.Options.AnalyzerConfigOptionsProvider.GetCsWinRTAotWarningLevel(); + var allowUnsafe = GeneratorHelper.AllowUnsafe(context.Compilation); + var isCsWinRTCcwLookupTableGeneratorEnabled = context.Options.AnalyzerConfigOptionsProvider.IsCsWinRTCcwLookupTableGeneratorEnabled(); + + context.RegisterSymbolAction(context => + { + // Filter to classes that can be passed as objects. + if (context.Symbol is INamedTypeSymbol namedType && + namedType.TypeKind == TypeKind.Class && + !namedType.IsAbstract && + !namedType.IsStatic) + { + // Make sure classes with the GeneratedBindableCustomProperty attribute are marked partial. + if (GeneratorHelper.HasAttributeWithType(namedType, generatedBindableCustomPropertyAttribute) && + !GeneratorHelper.IsPartial(namedType)) + { + context.ReportDiagnostic(Diagnostic.Create(WinRTRules.ClassWithBindableCustomPropertyNotPartial, namedType.Locations[0], namedType.Name)); + } + + // In opt-in mode, make sure it has the attribute asking to generate the vtable for it. + if (!isCsWinRTAotOptimizerAutoMode && !GeneratorHelper.HasAttributeWithType(namedType, generatedWinRTExposedTypeAttribute)) + { + return; + } + + // Make sure this is a class that we would generate the WinRTExposedType attribute on + // and that it isn't already partial. + if (!GeneratorHelper.IsWinRTType(namedType, winrtTypeAttribute, typeMapper, isComponentProject, context.Compilation.Assembly) && + !GeneratorHelper.HasNonInstantiatedWinRTGeneric(namedType, typeMapper) && + !GeneratorHelper.HasAttributeWithType(namedType, winrtExposedTypeAttribute)) + { + var interfacesFromOldProjections = new HashSet(); + bool implementsWinRTInterfaces = false; + bool implementsCustomMappedInterfaces = false; + bool implementsGenericInterfaces = false; + foreach (var iface in namedType.AllInterfaces) + { + if (GeneratorHelper.IsWinRTType(iface, winrtTypeAttribute, typeMapper, isComponentProject, context.Compilation.Assembly)) + { + if (!iface.IsGenericType && + GeneratorHelper.IsOldProjectionAssembly(iface.ContainingAssembly)) + { + interfacesFromOldProjections.Add(iface.Name); + } + + bool isCustomMappedType = GeneratorHelper.IsCustomMappedType(iface, typeMapper); + implementsCustomMappedInterfaces |= isCustomMappedType; + implementsWinRTInterfaces |= !isCustomMappedType; + implementsGenericInterfaces |= iface.IsGenericType; + } + } + + if (!GeneratorHelper.IsPartial(namedType) && + (implementsWinRTInterfaces || implementsCustomMappedInterfaces)) + { + // Based on the warning level, emit as a warning or as an info. + var diagnosticDescriptor = (csWinRTAotWarningLevel >= 2 || (csWinRTAotWarningLevel == 1 && implementsWinRTInterfaces)) ? + WinRTRules.ClassNotAotCompatibleWarning : WinRTRules.ClassNotAotCompatibleInfo; + context.ReportDiagnostic(Diagnostic.Create(diagnosticDescriptor, namedType.Locations[0], namedType.Name)); + } + + if (!allowUnsafe && implementsGenericInterfaces) + { + // Based on the warning level, emit as a warning or as an info. + var diagnosticDescriptor = (csWinRTAotWarningLevel >= 2 || (csWinRTAotWarningLevel == 1 && implementsWinRTInterfaces)) ? + WinRTRules.ClassEnableUnsafeWarning : WinRTRules.ClassEnableUnsafeInfo; + context.ReportDiagnostic(Diagnostic.Create(diagnosticDescriptor, namedType.Locations[0], namedType.Name)); + } + + if (interfacesFromOldProjections.Count > 0) + { + var diagnosticDescriptor = csWinRTAotWarningLevel >= 1 ? + WinRTRules.ClassNotAotCompatibleOldProjectionWarning : WinRTRules.ClassNotAotCompatibleOldProjectionInfo; + context.ReportDiagnostic(Diagnostic.Create(diagnosticDescriptor, namedType.Locations[0], namedType.Name, string.Join(", ", interfacesFromOldProjections))); + } + } + } + }, SymbolKind.NamedType); + + if (!allowUnsafe && isCsWinRTCcwLookupTableGeneratorEnabled && isCsWinRTAotOptimizerAutoMode) + { + context.RegisterSyntaxNodeAction(context => + { + var isWinRTType = GeneratorHelper.IsWinRTType(context.SemanticModel.Compilation, isComponentProject); + var isWinRTClassOrInterface = GeneratorHelper.IsWinRTClassOrInterface(context.SemanticModel.Compilation, isWinRTType, typeMapper); + + if (context.Node is InvocationExpressionSyntax invocation) + { + var invocationSymbol = context.SemanticModel.GetSymbolInfo(invocation).Symbol; + if (invocationSymbol is IMethodSymbol methodSymbol) + { + var taskAdapter = GeneratorHelper.GetTaskAdapterIfAsyncMethod(methodSymbol); + if (!string.IsNullOrEmpty(taskAdapter)) + { + ReportEnableUnsafeDiagnostic(context.ReportDiagnostic, methodSymbol, invocation.GetLocation(), true); + return; + } + // 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. + else if(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); + if (IsTypeOnLookupTable(argumentType, methodSymbol.Parameters[paramsIdx].Type, out var implementsOnlyCustomMappedInterface)) + { + ReportEnableUnsafeDiagnostic(context.ReportDiagnostic, argumentType.Type, invocation.GetLocation(), !implementsOnlyCustomMappedInterface); + return; + } + } + + // 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 leftSymbol = context.SemanticModel.GetSymbolInfo(assignment.Left).Symbol; + if (leftSymbol is IPropertySymbol propertySymbol && + (isWinRTClassOrInterface(propertySymbol.ContainingSymbol, true) || + SymbolEqualityComparer.Default.Equals(propertySymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly))) + { + var assignmentType = context.SemanticModel.GetTypeInfo(assignment.Right); + if (IsTypeOnLookupTable(assignmentType, propertySymbol.Type, out var implementsOnlyCustomMappedInterface)) + { + ReportEnableUnsafeDiagnostic(context.ReportDiagnostic, assignmentType.Type, assignment.GetLocation(), !implementsOnlyCustomMappedInterface); + return; + } + } + 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) || + SymbolEqualityComparer.Default.Equals(fieldSymbol.ContainingAssembly, context.SemanticModel.Compilation.Assembly))) + { + var assignmentType = context.SemanticModel.GetTypeInfo(assignment.Right); + if (IsTypeOnLookupTable(assignmentType, fieldSymbol.Type, out var implementsOnlyCustomMappedInterface)) + { + ReportEnableUnsafeDiagnostic(context.ReportDiagnostic, assignmentType.Type, assignment.GetLocation(), !implementsOnlyCustomMappedInterface); + return; + } + } + } + // Detect scenarios where the variable declaration is to a boxed or cast type during initialization. + else if (context.Node is VariableDeclarationSyntax variableDeclaration) + { + 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); + if (IsTypeOnLookupTable(instantiatedType, namedType, out var implementsOnlyCustomMappedInterface)) + { + ReportEnableUnsafeDiagnostic(context.ReportDiagnostic, instantiatedType.Type, variableDeclaration.GetLocation(), !implementsOnlyCustomMappedInterface); + return; + } + } + } + } + } + // Detect scenarios where the property declaration has an initializer and is to a boxed or cast type during initialization. + else if (context.Node is PropertyDeclarationSyntax propertyDeclaration) + { + 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); + if (IsTypeOnLookupTable(instantiatedType, namedType, out var implementsOnlyCustomMappedInterface)) + { + ReportEnableUnsafeDiagnostic(context.ReportDiagnostic, instantiatedType.Type, propertyDeclaration.GetLocation(), !implementsOnlyCustomMappedInterface); + return; + } + } + } + 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); + if (IsTypeOnLookupTable(instantiatedType, namedType, out var implementsOnlyCustomMappedInterface)) + { + ReportEnableUnsafeDiagnostic(context.ReportDiagnostic, instantiatedType.Type, propertyDeclaration.GetLocation(), !implementsOnlyCustomMappedInterface); + return; + } + } + } + } + // Detect scenarios where the method or property being returned from is doing a box or cast of the type + // in the return statement. + else if (context.Node is ReturnStatementSyntax returnDeclaration && returnDeclaration.Expression is not null) + { + 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) + { + if (IsTypeOnLookupTable(returnSymbol, typeSymbol, out var implementsOnlyCustomMappedInterface)) + { + ReportEnableUnsafeDiagnostic(context.ReportDiagnostic, returnSymbol.Type, returnDeclaration.GetLocation(), !implementsOnlyCustomMappedInterface); + return; + } + } + } + else if (parent is BasePropertyDeclarationSyntax propertyDeclarationSyntax) + { + var propertyTypeSymbol = context.SemanticModel.GetSymbolInfo(propertyDeclarationSyntax.Type).Symbol; + if (propertyTypeSymbol is ITypeSymbol typeSymbol) + { + if (IsTypeOnLookupTable(returnSymbol, typeSymbol, out var implementsOnlyCustomMappedInterface)) + { + ReportEnableUnsafeDiagnostic(context.ReportDiagnostic, returnSymbol.Type, returnDeclaration.GetLocation(), !implementsOnlyCustomMappedInterface); + return; + } + } + } + } + + bool IsTypeOnLookupTable(Microsoft.CodeAnalysis.TypeInfo instantiatedType, ITypeSymbol convertedToTypeSymbol, out bool implementsOnlyCustomMappedInterface) + { + if (instantiatedType.Type 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 && + GeneratorHelper.IsWinRTTypeOrImplementsWinRTType(arrayType.ElementType, winrtTypeAttribute, typeMapper, isComponentProject, context.Compilation.Assembly)) + { + // Arrays are projected as a list which is a custom mapped type, but if the element type is an actual WinRT type, + // then we don't consider it as implemented only custom mapped interfaces. + implementsOnlyCustomMappedInterface = GeneratorHelper.IsCustomMappedType(arrayType.ElementType, typeMapper); + return true; + } + } + else if (instantiatedType.Type is not null || instantiatedType.ConvertedType is not null) + { + // Type might be null such as for lambdas, so check converted type in that case. + var instantiatedTypeSymbol = instantiatedType.Type ?? instantiatedType.ConvertedType; + + // 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("`") && + GeneratorHelper.IsWinRTType(instantiatedTypeSymbol, winrtTypeAttribute, typeMapper, isComponentProject, context.Compilation.Assembly) && + convertedToTypeSymbol.SpecialType == SpecialType.System_Object) + { + implementsOnlyCustomMappedInterface = GeneratorHelper.IsCustomMappedType(instantiatedTypeSymbol, typeMapper); + return true; + } + + // 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. + !SymbolEqualityComparer.Default.Equals(instantiatedTypeSymbol, convertedToTypeSymbol); + } + else if (!GeneratorHelper.IsWinRTType(instantiatedTypeSymbol, winrtTypeAttribute, typeMapper, isComponentProject, context.Compilation.Assembly)) + { + 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. + !SymbolEqualityComparer.Default.Equals(instantiatedTypeSymbol, convertedToTypeSymbol); + } + + if (addClassOnLookupTable) + { + // We loop through all the interfaces rather than stopping at the first WinRT interface + // to determine whether it is also custom mapped WinRT interfaces or not. + implementsOnlyCustomMappedInterface = true; + bool implementsWinRTInterface = false; + foreach (var iface in instantiatedTypeSymbol.AllInterfaces) + { + if (GeneratorHelper.IsWinRTType(iface, winrtTypeAttribute, typeMapper, isComponentProject, context.Compilation.Assembly)) + { + implementsWinRTInterface = true; + implementsOnlyCustomMappedInterface &= GeneratorHelper.IsCustomMappedType(iface, typeMapper); + } + } + return implementsWinRTInterface; + } + } + } + + implementsOnlyCustomMappedInterface = false; + return false; + } + }, SyntaxKind.InvocationExpression, SyntaxKind.VariableDeclaration, SyntaxKind.PropertyDeclaration, SyntaxKind.ReturnStatement, SyntaxKind.SimpleAssignmentExpression); + + void ReportEnableUnsafeDiagnostic(Action reportDiagnostic, ISymbol symbol, Location location, bool implementsWinRTInterfaces) + { + // Based on the warning level, emit as a warning or as an info. + var diagnosticDescriptor = (csWinRTAotWarningLevel >= 2 || (csWinRTAotWarningLevel == 1 && implementsWinRTInterfaces)) ? + WinRTRules.ClassEnableUnsafeWarning : WinRTRules.ClassEnableUnsafeInfo; + reportDiagnostic(Diagnostic.Create(diagnosticDescriptor, location, symbol)); + } + } + + if (!allowUnsafe && isCsWinRTCcwLookupTableGeneratorEnabled && !isCsWinRTAotOptimizerAutoMode) + { + context.RegisterCompilationEndAction(context => + { + // If any of generated code for the type in the attribute requires unsafe code, report the first one. + foreach (AttributeData attributeData in context.Compilation.Assembly.GetAttributes()) + { + if (SymbolEqualityComparer.Default.Equals(attributeData.AttributeClass, generatedWinRTExposedExternalTypeAttribute)) + { + if (attributeData.ConstructorArguments is [{ Kind: TypedConstantKind.Type, Value: ITypeSymbol vtableType }]) + { + if (vtableType is IArrayTypeSymbol) + { + context.ReportDiagnostic(Diagnostic.Create(WinRTRules.ClassEnableUnsafeWarning, null, vtableType)); + return; + } + else if (vtableType.TypeKind == TypeKind.Delegate && + vtableType.MetadataName.Contains("`") && + GeneratorHelper.IsWinRTType(vtableType, winrtTypeAttribute, typeMapper, isComponentProject, context.Compilation.Assembly)) + { + context.ReportDiagnostic(Diagnostic.Create(WinRTRules.ClassEnableUnsafeWarning, null, vtableType)); + return; + } + else if (vtableType.TypeKind == TypeKind.Class) + { + foreach (var iface in vtableType.AllInterfaces) + { + if (iface.IsGenericType && + (GeneratorHelper.IsWinRTType(iface, winrtTypeAttribute, typeMapper, isComponentProject, context.Compilation.Assembly) || + GeneratorHelper.IsWinRTType(iface.OriginalDefinition, winrtTypeAttribute, typeMapper, isComponentProject, context.Compilation.Assembly))) + { + context.ReportDiagnostic(Diagnostic.Create(WinRTRules.ClassEnableUnsafeWarning, null, vtableType)); + return; + } + } + } + } + } + } + }); + } + }); + } + } + + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(WinRTAotCodeFixer)), Shared] + public sealed class WinRTAotCodeFixer : CodeFixProvider + { + private const string title = "Make type partial"; + + private static ImmutableArray _fixableDiagnosticIds = ImmutableArray.Create(WinRTRules.ClassNotAotCompatibleWarning.Id); + + public override ImmutableArray FixableDiagnosticIds => _fixableDiagnosticIds; + + public override async Task RegisterCodeFixesAsync(CodeFixContext context) + { + var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false); + if (root is null) + return; + + var node = root.FindNode(context.Span); + if (node is null) + return; + + var declaration = node.FirstAncestorOrSelf(); + if (declaration is null) + return; + + context.RegisterCodeFix( + CodeAction.Create( + title, + ct => MakeTypePartial(context.Document, declaration, ct), + nameof(WinRTAotCodeFixer)), + context.Diagnostics); + } + + private static async Task MakeTypePartial(Document document, ClassDeclarationSyntax @class, CancellationToken token) + { + var oldRoot = await document.GetSyntaxRootAsync(token).ConfigureAwait(false); + if (oldRoot is null) + return document; + + var newRoot = oldRoot.ReplaceNodes(@class.AncestorsAndSelf().OfType(), + (_, typeDeclaration) => + { + if (!typeDeclaration.Modifiers.Any(SyntaxKind.PartialKeyword)) + { + return typeDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.PartialKeyword)); + } + + return typeDeclaration; + }); + + return document.WithSyntaxRoot(newRoot); + } + + public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + } + + [ExportCodeFixProvider(LanguageNames.CSharp, Name = nameof(WinRTAotEnableUnsafeCodeFixer)), Shared] + public sealed class WinRTAotEnableUnsafeCodeFixer : CodeFixProvider + { + private const string title = "Update project with EnableUnsafeBlocks set to true"; + + private static ImmutableArray _fixableDiagnosticIds = ImmutableArray.Create(WinRTRules.ClassEnableUnsafeWarning.Id); + + public override ImmutableArray FixableDiagnosticIds => _fixableDiagnosticIds; + + public override Task RegisterCodeFixesAsync(CodeFixContext context) + { + return Task.Run(() => RegisterCodeFixes(context)); + } + + private static void RegisterCodeFixes(CodeFixContext context) + { + if (context.Document.Project.CompilationOptions is not CSharpCompilationOptions) + return; + + context.RegisterCodeFix( + CodeAction.Create( + title, + ct => EnableUnsafe(context.Document.Project.Solution, context.Document.Project, ct), + nameof(WinRTAotEnableUnsafeCodeFixer)), + context.Diagnostics); + } + + private static Task EnableUnsafe(Solution solution, Project project, CancellationToken token) + { + return Task.FromResult( + solution.WithProjectCompilationOptions(project.Id, ((CSharpCompilationOptions)project.CompilationOptions).WithAllowUnsafe(true))); + } + + public override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + } +} diff --git a/src/Authoring/WinRT.SourceGenerator/WinRTRules.cs b/src/Authoring/WinRT.SourceGenerator/WinRTRules.cs index bcc994314..c836e7d7a 100644 --- a/src/Authoring/WinRT.SourceGenerator/WinRTRules.cs +++ b/src/Authoring/WinRT.SourceGenerator/WinRTRules.cs @@ -9,16 +9,16 @@ public class WinRTRules /// string, a few words generally describing the diagnostic /// string, describes the diagnostic -- formatted with {0}, ... -- /// such that data can be passed in for the code the diagnostic is reported for - private static DiagnosticDescriptor MakeRule(string id, string title, string messageFormat) + private static DiagnosticDescriptor MakeRule(string id, string title, string messageFormat, bool isError = true, bool isWarning = false) { return new DiagnosticDescriptor( id: id, title: title, messageFormat: messageFormat, category: "Usage", - defaultSeverity: DiagnosticSeverity.Error, + defaultSeverity: isError ? DiagnosticSeverity.Error : isWarning? DiagnosticSeverity.Warning : DiagnosticSeverity.Info, isEnabledByDefault: true, - helpLinkUri: "https://github.com/microsoft/CsWinRT/tree/master/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Unshipped.md"); + helpLinkUri: "https://github.com/microsoft/CsWinRT/tree/master/src/Authoring/WinRT.SourceGenerator/AnalyzerReleases.Shipped.md"); } public static DiagnosticDescriptor PrivateGetterRule = MakeRule( @@ -179,5 +179,97 @@ private static DiagnosticDescriptor MakeRule(string id, string title, string mes "CsWinRT1027", CsWinRTDiagnosticStrings.UnimplementedInterface_Brief, CsWinRTDiagnosticStrings.UnimplementedInterface_Text); + + public static DiagnosticDescriptor ClassNotAotCompatibleWarning = MakeRule( + "CsWinRT1028", + CsWinRTDiagnosticStrings.ClassNotMarkedPartial_Brief, + CsWinRTDiagnosticStrings.ClassNotMarkedPartial_Text, + false, + true); + + public static DiagnosticDescriptor ClassNotAotCompatibleInfo = MakeRule( + "CsWinRT1028", + CsWinRTDiagnosticStrings.ClassNotMarkedPartial_Brief, + CsWinRTDiagnosticStrings.ClassNotMarkedPartial_Text, + false); + + public static DiagnosticDescriptor ClassNotAotCompatibleOldProjectionWarning = MakeRule( + "CsWinRT1029", + CsWinRTDiagnosticStrings.ClassImplementsOldProjection_Brief, + CsWinRTDiagnosticStrings.ClassImplementsOldProjection_Text, + false, + true); + + public static DiagnosticDescriptor ClassNotAotCompatibleOldProjectionInfo = MakeRule( + "CsWinRT1029", + CsWinRTDiagnosticStrings.ClassImplementsOldProjection_Brief, + CsWinRTDiagnosticStrings.ClassImplementsOldProjection_Text, + false); + + public static DiagnosticDescriptor ClassEnableUnsafeWarning = MakeRule( + "CsWinRT1030", + CsWinRTDiagnosticStrings.EnableUnsafe_Brief, + CsWinRTDiagnosticStrings.EnableUnsafe_Text, + false, + true); + + public static DiagnosticDescriptor ClassEnableUnsafeInfo = MakeRule( + "CsWinRT1030", + CsWinRTDiagnosticStrings.EnableUnsafe_Brief, + CsWinRTDiagnosticStrings.EnableUnsafe_Text, + false); + + public static DiagnosticDescriptor ClassWithBindableCustomPropertyNotPartial = MakeRule( + "CsWinRT1028", + CsWinRTDiagnosticStrings.ClassNotMarkedPartial_Brief, + CsWinRTDiagnosticStrings.BindableCustomPropertyClassNotMarkedPartial_Text, + false, + true); + + public static DiagnosticDescriptor ClassNotAotCompatibleOldProjectionMultipleInstancesWarning = MakeRule( + "CsWinRT1029", + CsWinRTDiagnosticStrings.ClassImplementsOldProjection_Brief, + CsWinRTDiagnosticStrings.ClassOldProjectionMultipleInstances_Text, + false, + true); + + public static DiagnosticDescriptor ClassNotAotCompatibleOldProjectionMultipleInstancesInfo = MakeRule( + "CsWinRT1029", + CsWinRTDiagnosticStrings.ClassImplementsOldProjection_Brief, + CsWinRTDiagnosticStrings.ClassOldProjectionMultipleInstances_Text, + false); + + public static DiagnosticDescriptor SourceGeneratorFailed = MakeRule( + "CsWinRT1031", + CsWinRTDiagnosticStrings.SourceGeneratorFailed_Brief, + CsWinRTDiagnosticStrings.SourceGeneratorFailed_Text); + + public static DiagnosticDescriptor NonEmptyCollectionExpressionTargetingNonBuilderInterfaceType = MakeRule( + "CsWinRT1032", + CsWinRTDiagnosticStrings.NonEmptyCollectionExpressionTargetingNonBuilderInterfaceType_Brief, + CsWinRTDiagnosticStrings.NonEmptyCollectionExpressionTargetingNonBuilderInterfaceType_Text, + false, + true); + + public static DiagnosticDescriptor ComImportInterfaceCast = MakeRule( + "CsWinRT1033", + CsWinRTDiagnosticStrings.ComImportInterfaceCast_Brief, + CsWinRTDiagnosticStrings.ComImportInterfaceCast_Text, + false, + true); + + public static DiagnosticDescriptor RuntimeClassCast = MakeRule( + "CsWinRT1034", + CsWinRTDiagnosticStrings.RuntimeClassCast_Brief, + CsWinRTDiagnosticStrings.RuntimeClassCast_Text, + false, + true); + + public static DiagnosticDescriptor IReferenceTypeCast = MakeRule( + "CsWinRT1035", + CsWinRTDiagnosticStrings.IReferenceTypeCast_Brief, + CsWinRTDiagnosticStrings.IReferenceTypeCast_Text, + false, + true); } } diff --git a/src/Authoring/WinRT.SourceGenerator/WinRTTypeWriter.cs b/src/Authoring/WinRT.SourceGenerator/WinRTTypeWriter.cs index f52aff52e..c163ff2dd 100644 --- a/src/Authoring/WinRT.SourceGenerator/WinRTTypeWriter.cs +++ b/src/Authoring/WinRT.SourceGenerator/WinRTTypeWriter.cs @@ -1,2790 +1,2937 @@ -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.Reflection; -using System.Reflection.Metadata; -using System.Reflection.Metadata.Ecma335; -using System.Security.Cryptography; -using System.Text; - -namespace Generator -{ - class Parameter - { - public Symbol Type; - public string Name; - public ParameterAttributes Attributes; - public bool ByRef; - - public Parameter(Symbol type, string name, ParameterAttributes attributes) - : this(type, name, attributes, attributes == ParameterAttributes.Out) - { - } - - public Parameter(Symbol type, string name, ParameterAttributes attributes, bool byRef) - { - Type = type; - Name = name; - Attributes = attributes; - ByRef = byRef; - } - - public Parameter(ITypeSymbol type, string name, ParameterAttributes attributes) - : this(new Symbol(type), name, attributes) - { - } - - public Parameter(EntityHandle type, string name, ParameterAttributes attributes) - : this(new Symbol(type), name, attributes) - { - } - - public Parameter(IParameterSymbol parameterSymbol) - { - // Set out parameter attribute if write only array. - bool isWriteOnlyArray = parameterSymbol.Type is IArrayTypeSymbol && - parameterSymbol.GetAttributes().Where( - attr => string.CompareOrdinal(attr.AttributeClass.ToString(), "System.Runtime.InteropServices.WindowsRuntime.WriteOnlyArrayAttribute") == 0 - ).Count() != 0; - - Type = new Symbol(parameterSymbol.Type); - Name = parameterSymbol.Name; - Attributes = (parameterSymbol.RefKind == RefKind.Out || isWriteOnlyArray) ? ParameterAttributes.Out : ParameterAttributes.In; - ByRef = parameterSymbol.RefKind == RefKind.Out; - } - - public static Parameter[] GetParameters(ParameterListSyntax parameterList, SemanticModel model) - { - int numParameters = parameterList.Parameters.Count; - Parameter[] parameters = new Parameter[numParameters]; - for (int idx = 0; idx < numParameters; idx++) - { - parameters[idx] = new Parameter(model.GetDeclaredSymbol(parameterList.Parameters[idx])); - } - - return parameters; - } - - public static Parameter[] GetParameters(IMethodSymbol method) - { - int numParameters = method.Parameters.Count(); - Parameter[] parameters = new Parameter[numParameters]; - for (int idx = 0; idx < numParameters; idx++) - { - parameters[idx] = new Parameter(method.Parameters[idx]); - } - - return parameters; - } - } - - class Symbol - { - public ITypeSymbol Type; - public EntityHandle Handle; - public int GenericIndex; - public bool IsArray; - - public Symbol(ITypeSymbol type, bool isArray = false) - { - Type = type; - Handle = default; - GenericIndex = -1; - IsArray = isArray; - } - - public Symbol(EntityHandle handle) - { - Type = default; - Handle = handle; - GenericIndex = -1; - } - - public Symbol(int genericIndex, bool isArray) - { - Type = default; - Handle = default; - GenericIndex = genericIndex; - IsArray = isArray; - } - - public bool IsHandle() - { - return Handle != default; - } - - public bool IsGeneric() - { - return GenericIndex != -1; - } - } - - class TypeDeclaration - { - public readonly ISymbol Node; - public TypeDefinitionHandle Handle; - public string DefaultInterface; - public string StaticInterface; - public bool IsSynthesizedInterface; - - public Dictionary> MethodDefinitions = new Dictionary>(); - public Dictionary> MethodReferences = new Dictionary>(); - public Dictionary FieldDefinitions = new Dictionary(); - public Dictionary PropertyDefinitions = new Dictionary(); - public Dictionary EventDefinitions = new Dictionary(); - public Dictionary InterfaceImplDefinitions = new Dictionary(); - public Dictionary> MethodsByName = new Dictionary>(StringComparer.Ordinal); - public Dictionary OverloadedMethods = new Dictionary(); - public List CustomMappedSymbols = new List(); - public HashSet SymbolsWithAttributes = new HashSet(); - public Dictionary ClassInterfaceMemberMapping = new Dictionary(); - - public TypeDeclaration() - : this(null) - { - IsSynthesizedInterface = true; - } - - public TypeDeclaration(ISymbol node) - { - Node = node; - Handle = default; - IsSynthesizedInterface = false; - } - - public override string ToString() - { - return Node.ToString(); - } - - public void AddMethod(ISymbol node, string name, MethodDefinitionHandle handle) - { - if (!MethodDefinitions.ContainsKey(node)) - { - MethodDefinitions[node] = new List(); - MethodReferences[node] = new List(); - } - - if (!MethodsByName.ContainsKey(name)) - { - MethodsByName[name] = new List(); - } - - MethodDefinitions[node].Add(handle); - MethodReferences[node].Add(handle); - MethodsByName[name].Add(node); - } - - public void AddMethodReference(ISymbol node, MemberReferenceHandle handle) - { - if (!MethodReferences.ContainsKey(node)) - { - MethodReferences[node] = new List(); - } - - MethodReferences[node].Add(handle); - } - - public void AddMethodOverload(ISymbol node, string overloadedMethodName) - { - OverloadedMethods[node] = overloadedMethodName; - } - - public List GetMethodDefinitions() - { - return MethodDefinitions.Values.SelectMany(list => list).ToList(); - } - - public List GetMethodReferences() - { - return MethodReferences.Values.SelectMany(list => list).ToList(); - } - - public void AddField(ISymbol node, FieldDefinitionHandle handle) - { - FieldDefinitions[node] = handle; - } - - public List GetFieldDefinitions() - { - return FieldDefinitions.Values.ToList(); - } - - public void AddProperty(ISymbol node, PropertyDefinitionHandle handle) - { - PropertyDefinitions[node] = handle; - } - - public List GetPropertyDefinitions() - { - return PropertyDefinitions.Values.ToList(); - } - - public void AddEvent(ISymbol node, EventDefinitionHandle handle) - { - EventDefinitions[node] = handle; - } - - public List GetEventDefinitions() - { - return EventDefinitions.Values.ToList(); - } - - public void AddInterfaceImpl(ISymbol node, InterfaceImplementationHandle handle) - { - InterfaceImplDefinitions[node] = handle; - } - - } - - class WinRTTypeWriter : CSharpSyntaxWalker - { - internal struct MappedType - { - private readonly string @namespace; - private readonly string name; - private readonly string assembly; - private readonly bool isSystemType; - private readonly bool isValueType; - private readonly Func multipleMappingFunc; - - public MappedType(string @namespace, string name, string assembly, bool isValueType = false) - { - this.@namespace = @namespace; - this.name = name; - this.assembly = assembly; - isSystemType = string.CompareOrdinal(this.assembly, "mscorlib") == 0; - this.isValueType = isValueType; - multipleMappingFunc = null; - } - - public MappedType(Func multipleMappingFunc) - { - @namespace = null; - name = null; - assembly = null; - isSystemType = false; - isValueType = false; - this.multipleMappingFunc = multipleMappingFunc; - } - - public (string, string, string, bool, bool) GetMapping(ISymbol containingType = null) - { - return multipleMappingFunc != null ? - multipleMappingFunc(containingType) : (@namespace, name, assembly, isSystemType, isValueType); - } - } - - // This should be in sync with the reverse mapping from WinRT.Runtime/Projections.cs and cswinrt/helpers.h. - internal static readonly Dictionary MappedCSharpTypes = new Dictionary(StringComparer.Ordinal) - { - { "System.DateTimeOffset", new MappedType("Windows.Foundation", "DateTime", "Windows.Foundation.FoundationContract", true) }, - { "System.Exception", new MappedType("Windows.Foundation", "HResult", "Windows.Foundation.FoundationContract", true) }, - { "System.EventHandler`1", new MappedType("Windows.Foundation", "EventHandler`1", "Windows.Foundation.FoundationContract", true) }, - { "System.FlagsAttribute", new MappedType("System", "FlagsAttribute", "mscorlib" ) }, - { "System.IDisposable", new MappedType("Windows.Foundation", "IClosable", "Windows.Foundation.FoundationContract") }, - { "System.IServiceProvider", new MappedType("Microsoft.UI.Xaml", "IXamlServiceProvider", "Microsoft.UI") }, - { "System.Nullable`1", new MappedType("Windows.Foundation", "IReference`1", "Windows.Foundation.FoundationContract" ) }, - { "System.Object", new MappedType("System", "Object", "mscorlib" ) }, - { "System.TimeSpan", new MappedType("Windows.Foundation", "TimeSpan", "Windows.Foundation.FoundationContract", true) }, - { "System.Uri", new MappedType("Windows.Foundation", "Uri", "Windows.Foundation.FoundationContract") }, - { "System.ComponentModel.DataErrorsChangedEventArgs", new MappedType("Microsoft.UI.Xaml.Data", "DataErrorsChangedEventArgs", "Microsoft.UI") }, - { "System.ComponentModel.INotifyDataErrorInfo", new MappedType("Microsoft.UI.Xaml.Data", "INotifyDataErrorInfo", "Microsoft.UI") }, - { "System.ComponentModel.INotifyPropertyChanged", new MappedType("Microsoft.UI.Xaml.Data", "INotifyPropertyChanged", "Microsoft.UI") }, - { "System.ComponentModel.PropertyChangedEventArgs", new MappedType("Microsoft.UI.Xaml.Data", "PropertyChangedEventArgs", "Microsoft.UI") }, - { "System.ComponentModel.PropertyChangedEventHandler", new MappedType("Microsoft.UI.Xaml.Data", "PropertyChangedEventHandler", "Microsoft.UI") }, - { "System.Windows.Input.ICommand", new MappedType("Microsoft.UI.Xaml.Input", "ICommand", "Microsoft.UI") }, - { "System.Collections.IEnumerable", new MappedType("Microsoft.UI.Xaml.Interop", "IBindableIterable", "Microsoft.UI") }, - { "System.Collections.IList", new MappedType("Microsoft.UI.Xaml.Interop", "IBindableVector", "Microsoft.UI") }, - { "System.Collections.Specialized.INotifyCollectionChanged", new MappedType("Microsoft.UI.Xaml.Interop", "INotifyCollectionChanged", "Microsoft.UI") }, - { "System.Collections.Specialized.NotifyCollectionChangedAction", new MappedType("Microsoft.UI.Xaml.Interop", "NotifyCollectionChangedAction", "Microsoft.UI") }, - { "System.Collections.Specialized.NotifyCollectionChangedEventArgs", new MappedType("Microsoft.UI.Xaml.Interop", "NotifyCollectionChangedEventArgs", "Microsoft.UI") }, - { "System.Collections.Specialized.NotifyCollectionChangedEventHandler", new MappedType("Microsoft.UI.Xaml.Interop", "NotifyCollectionChangedEventHandler", "Microsoft.UI") }, - { "WinRT.EventRegistrationToken", new MappedType("Windows.Foundation", "EventRegistrationToken", "Windows.Foundation.FoundationContract", true) }, - { "System.AttributeTargets", new MappedType("Windows.Foundation.Metadata", "AttributeTargets", "Windows.Foundation.FoundationContract", true) }, - { "System.AttributeUsageAttribute", new MappedType("Windows.Foundation.Metadata", "AttributeUsageAttribute", "Windows.Foundation.FoundationContract") }, - { "System.Numerics.Matrix3x2", new MappedType("Windows.Foundation.Numerics", "Matrix3x2", "Windows.Foundation.FoundationContract", true) }, - { "System.Numerics.Matrix4x4", new MappedType("Windows.Foundation.Numerics", "Matrix4x4", "Windows.Foundation.FoundationContract", true) }, - { "System.Numerics.Plane", new MappedType("Windows.Foundation.Numerics", "Plane", "Windows.Foundation.FoundationContract", true) }, - { "System.Numerics.Quaternion", new MappedType("Windows.Foundation.Numerics", "Quaternion", "Windows.Foundation.FoundationContract", true) }, - { "System.Numerics.Vector2", new MappedType("Windows.Foundation.Numerics", "Vector2", "Windows.Foundation.FoundationContract", true) }, - { "System.Numerics.Vector3", new MappedType("Windows.Foundation.Numerics", "Vector3", "Windows.Foundation.FoundationContract", true) }, - { "System.Numerics.Vector4", new MappedType("Windows.Foundation.Numerics", "Vector4", "Windows.Foundation.FoundationContract", true) }, - { "System.Type", new MappedType(GetSystemTypeCustomMapping) }, - { "System.Collections.Generic.IEnumerable`1", new MappedType("Windows.Foundation.Collections", "IIterable`1", "Windows.Foundation.FoundationContract") }, - { "System.Collections.Generic.IEnumerator`1", new MappedType("Windows.Foundation.Collections", "IIterator`1", "Windows.Foundation.FoundationContract") }, - { "System.Collections.Generic.KeyValuePair`2", new MappedType("Windows.Foundation.Collections", "IKeyValuePair`2", "Windows.Foundation.FoundationContract") }, - { "System.Collections.Generic.IReadOnlyDictionary`2", new MappedType("Windows.Foundation.Collections", "IMapView`2", "Windows.Foundation.FoundationContract") }, - { "System.Collections.Generic.IDictionary`2", new MappedType("Windows.Foundation.Collections", "IMap`2", "Windows.Foundation.FoundationContract") }, - { "System.Collections.Generic.IReadOnlyList`1", new MappedType("Windows.Foundation.Collections", "IVectorView`1", "Windows.Foundation.FoundationContract") }, - { "System.Collections.Generic.IList`1", new MappedType("Windows.Foundation.Collections", "IVector`1", "Windows.Foundation.FoundationContract") }, - { "Windows.UI.Color", new MappedType("Windows.UI", "Color", "Windows.Foundation.UniversalApiContract", true) }, - }; - - internal static readonly List ImplementedInterfacesWithoutMapping = new List() - { - "System.Collections.Generic.ICollection`1", - "System.Collections.Generic.IReadOnlyCollection`1", - "System.Collections.ICollection", - "System.Collections.IEnumerator", - "System.IEquatable`1", - "System.Runtime.InteropServices.ICustomQueryInterface", - "System.Runtime.InteropServices.IDynamicInterfaceCastable", - "WinRT.IWinRTObject" - }; - - public SemanticModel Model; - - private readonly string assembly; - private readonly string version; - - private readonly Dictionary typeReferenceMapping; - private readonly Dictionary assemblyReferenceMapping; - private readonly MetadataBuilder metadataBuilder; - - private readonly Dictionary typeDefinitionMapping; - private TypeDeclaration currentTypeDeclaration; - - private Logger Logger { get; } - - public WinRTTypeWriter( - string assembly, - string version, - MetadataBuilder metadataBuilder, - Logger logger) - { - this.assembly = assembly; - this.version = version; - this.metadataBuilder = metadataBuilder; - Logger = logger; - typeReferenceMapping = new Dictionary(StringComparer.Ordinal); - assemblyReferenceMapping = new Dictionary(StringComparer.Ordinal); - typeDefinitionMapping = new Dictionary(StringComparer.Ordinal); - - CreteAssembly(); - } - - private void CreteAssembly() - { - Logger.Log("Generating assembly " + assembly + " version " + version); - metadataBuilder.AddAssembly( - metadataBuilder.GetOrAddString(assembly), - new Version(version), - default, - default, - AssemblyFlags.WindowsRuntime, - AssemblyHashAlgorithm.Sha1); - - var moduleDefinition = metadataBuilder.AddModule( - 0, - metadataBuilder.GetOrAddString(assembly + ".winmd"), - metadataBuilder.GetOrAddGuid(Guid.NewGuid()), - default, - default); - assemblyReferenceMapping[assembly] = moduleDefinition; - - metadataBuilder.AddTypeDefinition( - default, - default, - metadataBuilder.GetOrAddString(""), - default, - MetadataTokens.FieldDefinitionHandle(1), - MetadataTokens.MethodDefinitionHandle(1)); - } - - private bool IsEncodableAsSpecialType(SpecialType specialType) - { - return specialType != SpecialType.None && specialType <= SpecialType.System_Array; - } - - private void EncodeSpecialType(SpecialType specialType, SignatureTypeEncoder typeEncoder) - { - switch (specialType) - { - case SpecialType.System_Boolean: - typeEncoder.Boolean(); - break; - case SpecialType.System_Byte: - typeEncoder.Byte(); - break; - case SpecialType.System_Int16: - typeEncoder.Int16(); - break; - case SpecialType.System_Int32: - typeEncoder.Int32(); - break; - case SpecialType.System_Int64: - typeEncoder.Int64(); - break; - case SpecialType.System_UInt16: - typeEncoder.UInt16(); - break; - case SpecialType.System_UInt32: - typeEncoder.UInt32(); - break; - case SpecialType.System_UInt64: - typeEncoder.UInt64(); - break; - case SpecialType.System_Single: - typeEncoder.Single(); - break; - case SpecialType.System_Double: - typeEncoder.Double(); - break; - case SpecialType.System_Char: - typeEncoder.Char(); - break; - case SpecialType.System_String: - typeEncoder.String(); - break; - case SpecialType.System_Object: - typeEncoder.Object(); - break; - case SpecialType.System_IntPtr: - typeEncoder.IntPtr(); - break; - default: - Logger.Log("TODO special type: " + specialType); - break; - } - - // TODO: handle C# interface mappings for special types - } - - private BlobHandle GetStrongNameKey(string assembly) - { - if (string.CompareOrdinal(assembly, "mscorlib") == 0) - { - byte[] mscorlibStrongName = { 0xb7, 0x7a, 0x5c, 0x56, 0x19, 0x34, 0xe0, 0x89 }; - return metadataBuilder.GetOrAddBlob(mscorlibStrongName); - } - - return default; - } - - private EntityHandle GetTypeReference(string @namespace, string name, string assembly) - { - string fullname = QualifiedName(@namespace, name); - if (typeReferenceMapping.ContainsKey(fullname)) - { - return typeReferenceMapping[fullname]; - } - - if (!assemblyReferenceMapping.ContainsKey(assembly)) - { - EntityHandle assemblyReference = metadataBuilder.AddAssemblyReference( - metadataBuilder.GetOrAddString(assembly), - new Version(0xff, 0xff, 0xff, 0xff), - default, - GetStrongNameKey(assembly), - string.CompareOrdinal(assembly, "mscorlib") == 0 ? default : AssemblyFlags.WindowsRuntime, - default); - assemblyReferenceMapping[assembly] = assemblyReference; - } - - var typeRef = metadataBuilder.AddTypeReference( - assemblyReferenceMapping[assembly], - metadataBuilder.GetOrAddString(@namespace), - metadataBuilder.GetOrAddString(name)); - typeReferenceMapping[fullname] = typeRef; - return typeRef; - } - - public bool IsWinRTType(ISymbol type) - { - bool isProjectedType = type.GetAttributes(). - Any(attribute => string.CompareOrdinal(attribute.AttributeClass.Name, "WindowsRuntimeTypeAttribute") == 0); - return isProjectedType; - } - - public string GetAssemblyForWinRTType(ISymbol type) - { - var winrtTypeAttribute = type.GetAttributes(). - Where(attribute => string.CompareOrdinal(attribute.AttributeClass.Name, "WindowsRuntimeTypeAttribute") == 0); - if (winrtTypeAttribute.Any()) - { - return (string)winrtTypeAttribute.First().ConstructorArguments[0].Value; - } - - return null; - } - - private EntityHandle GetTypeReference(ISymbol symbol) - { - string @namespace = symbol.ContainingNamespace.ToString(); - string name = GetGenericName(symbol); - - string fullType = QualifiedName(@namespace, name); - var assembly = GetAssemblyForWinRTType(symbol); - if (assembly == null) - { - if (MappedCSharpTypes.ContainsKey(fullType)) - { - (@namespace, name, assembly, _, _) = MappedCSharpTypes[fullType].GetMapping(currentTypeDeclaration.Node); - Logger.Log("custom mapping " + fullType + " to " + QualifiedName(@namespace, name) + " from " + assembly); - } - else - { - assembly = symbol.ContainingAssembly.Name; - } - } - - return GetTypeReference(@namespace, name, assembly); - } - - private EntityHandle GetTypeSpecification(INamedTypeSymbol symbol) - { - if (symbol.IsGenericType) - { - Logger.Log("Adding TypeSpec for " + symbol.ToString()); - var typeSpecSignature = new BlobBuilder(); - var genericType = new BlobEncoder(typeSpecSignature) - .TypeSpecificationSignature() - .GenericInstantiation(GetTypeReference(symbol), symbol.TypeArguments.Length, false); - foreach (var typeArgument in symbol.TypeArguments) - { - EncodeSymbol(new Symbol(typeArgument), genericType.AddArgument()); - } - - return metadataBuilder.AddTypeSpecification(metadataBuilder.GetOrAddBlob(typeSpecSignature)); - } - else - { - return GetTypeReference(symbol); - } - } - - private void EncodeSymbol(Symbol symbol, SignatureTypeEncoder typeEncoder) - { - if (symbol.IsHandle()) - { - typeEncoder.Type(symbol.Handle, false); - } - else if (symbol.IsGeneric()) - { - if (symbol.IsArray) - { - typeEncoder.SZArray().GenericTypeParameter(symbol.GenericIndex); - } - else - { - typeEncoder.GenericTypeParameter(symbol.GenericIndex); - } - } - else if (symbol.IsArray) - { - EncodeSymbol(new Symbol(symbol.Type), typeEncoder.SZArray()); - } - else if (symbol.Type is IArrayTypeSymbol arrayType) - { - EncodeSymbol(new Symbol(arrayType.ElementType), typeEncoder.SZArray()); - } - else if (symbol.Type is INamedTypeSymbol namedType && namedType.TypeArguments.Length != 0) - { - var genericType = typeEncoder.GenericInstantiation(GetTypeReference(symbol.Type), namedType.TypeArguments.Length, false); - int parameterIndex = 0; - foreach (var typeArgument in namedType.TypeArguments) - { - if (namedType.IsUnboundGenericType) - { - genericType.AddArgument().GenericTypeParameter(parameterIndex); - } - else - { - EncodeSymbol(new Symbol(typeArgument), genericType.AddArgument()); - } - parameterIndex++; - } - } - else if (IsEncodableAsSpecialType(symbol.Type.SpecialType)) - { - EncodeSpecialType(symbol.Type.SpecialType, typeEncoder); - } - else - { - bool isValueType = symbol.Type.TypeKind == TypeKind.Enum || symbol.Type.TypeKind == TypeKind.Struct; - if (MappedCSharpTypes.ContainsKey(QualifiedName(symbol.Type))) - { - (_, _, _, _, isValueType) = MappedCSharpTypes[QualifiedName(symbol.Type)].GetMapping(currentTypeDeclaration.Node); - } - typeEncoder.Type(GetTypeReference(symbol.Type), isValueType); - } - } - - private void EncodeReturnType(Symbol symbol, ReturnTypeEncoder returnTypeEncoder) - { - if (symbol == null) - { - returnTypeEncoder.Void(); - } - else if (symbol.IsHandle() || symbol.IsGeneric() || !IsEncodableAsSpecialType(symbol.Type.SpecialType)) - { - EncodeSymbol(symbol, returnTypeEncoder.Type()); - } - else if (symbol.Type.SpecialType == SpecialType.System_Void) - { - returnTypeEncoder.Void(); - } - else - { - EncodeSpecialType(symbol.Type.SpecialType, returnTypeEncoder.Type()); - } - } - - private void EncodeParameters(Parameter[] parameters, ParametersEncoder parametersEncoder) - { - foreach (var parameter in parameters) - { - var parameterType = parameter.Type; - var parameterTypeEncoder = parametersEncoder.AddParameter(); - - if (!parameterType.IsHandle() && !parameterType.IsGeneric() && IsEncodableAsSpecialType(parameterType.Type.SpecialType)) - { - EncodeSpecialType(parameterType.Type.SpecialType, parameterTypeEncoder.Type(parameter.ByRef)); - } - else - { - EncodeSymbol(parameterType, parameterTypeEncoder.Type(parameter.ByRef)); - } - } - } - - public MethodDefinitionHandle AddMethodDefinition( - string name, - Parameter[] parameters, - Symbol returnSymbol, - bool isStatic, - bool isInterfaceParent, - bool isSpecialMethod = false, - bool isPublic = true, - bool isOverridable = false) - { - var methodSignature = new BlobBuilder(); - new BlobEncoder(methodSignature) - .MethodSignature( - SignatureCallingConvention.Default, - 0, - !isStatic) - .Parameters( - parameters.Length, - returnType => EncodeReturnType(returnSymbol, returnType), - parametersEncoder => EncodeParameters(parameters, parametersEncoder) - ); - - List parameterHandles = new List(); - for (int idx = 0; idx < parameters.Length; idx++) - { - parameterHandles.Add(metadataBuilder.AddParameter( - parameters[idx].Attributes, - metadataBuilder.GetOrAddString(parameters[idx].Name), - idx + 1)); - } - - var methodAttributes = - (isPublic ? MethodAttributes.Public : MethodAttributes.Private) | - MethodAttributes.HideBySig; - - var methodImplAttributes = MethodImplAttributes.Managed; - - if (isInterfaceParent) - { - methodAttributes |= MethodAttributes.Abstract; - } - else - { - methodImplAttributes |= MethodImplAttributes.Runtime; - } - - if (isSpecialMethod && string.CompareOrdinal(name, ".ctor") == 0) - { - methodAttributes |= MethodAttributes.RTSpecialName; - } - else if (isStatic) - { - methodAttributes |= MethodAttributes.Static; - } - else - { - methodAttributes |= - MethodAttributes.Virtual | - MethodAttributes.NewSlot; - - if (!isOverridable && !isInterfaceParent) - { - methodAttributes |= MethodAttributes.Final; - } - } - - if (isSpecialMethod) - { - methodAttributes |= MethodAttributes.SpecialName; - } - - var methodDefinitionHandle = metadataBuilder.AddMethodDefinition( - methodAttributes, - methodImplAttributes, - metadataBuilder.GetOrAddString(name), - metadataBuilder.GetOrAddBlob(methodSignature), - -1, - parameterHandles.Count == 0 ? - MetadataTokens.ParameterHandle(metadataBuilder.GetRowCount(TableIndex.Param) + 1) : - parameterHandles[0]); - return methodDefinitionHandle; - } - - public void AddFieldDeclaration(IFieldSymbol field, bool isEnum) - { - Logger.Log("defining field " + field.Name + " with type " + field.Type.ToString()); - - var fieldSignature = new BlobBuilder(); - var encoder = new BlobEncoder(fieldSignature); - EncodeSymbol(new Symbol(field.Type), encoder.FieldSignature()); - - var fieldAttributes = FieldAttributes.Public; - if (isEnum) - { - fieldAttributes |= - FieldAttributes.Static | - FieldAttributes.Literal | - FieldAttributes.HasDefault; - } - - var fieldDefinitionHandle = metadataBuilder.AddFieldDefinition( - fieldAttributes, - metadataBuilder.GetOrAddString(field.Name), - metadataBuilder.GetOrAddBlob(fieldSignature)); - currentTypeDeclaration.AddField(field, fieldDefinitionHandle); - - if (isEnum && field.HasConstantValue) - { - metadataBuilder.AddConstant(fieldDefinitionHandle, field.ConstantValue); - } - } - - public void AddPropertyDefinition( - string propertyName, - Symbol type, - ISymbol symbol, - bool isStatic, - bool hasSetMethod, - bool isInterfaceParent, - bool isPublic = true) - { - Logger.Log("defining property " + propertyName); - GetNamespaceAndTypename(propertyName, out var @namespace, out var typename); - - var propertySignature = new BlobBuilder(); - new BlobEncoder(propertySignature) - .PropertySignature(!isStatic) - .Parameters( - 0, - returnType => EncodeReturnType(type, returnType), - parameters => { } - ); - - var propertyDefinitonHandle = metadataBuilder.AddProperty( - PropertyAttributes.None, - metadataBuilder.GetOrAddString(propertyName), - metadataBuilder.GetOrAddBlob(propertySignature)); - currentTypeDeclaration.AddProperty(symbol, propertyDefinitonHandle); - - if (hasSetMethod) - { - string setMethodName = QualifiedName(@namespace, "put_" + typename); - var setMethod = AddMethodDefinition( - setMethodName, - new Parameter[] { new Parameter(type, "value", ParameterAttributes.In) }, - null, - !isInterfaceParent && isStatic, - isInterfaceParent, - true, - isPublic); - currentTypeDeclaration.AddMethod(symbol, setMethodName, setMethod); - - metadataBuilder.AddMethodSemantics( - propertyDefinitonHandle, - MethodSemanticsAttributes.Setter, - setMethod); - } - - string getMethodName = QualifiedName(@namespace, "get_" + typename); - var getMethod = AddMethodDefinition( - getMethodName, - new Parameter[0], - type, - !isInterfaceParent && isStatic, - isInterfaceParent, - true, - isPublic); - currentTypeDeclaration.AddMethod(symbol, getMethodName, getMethod); - - metadataBuilder.AddMethodSemantics( - propertyDefinitonHandle, - MethodSemanticsAttributes.Getter, - getMethod); - } - - public void AddPropertyDeclaration(IPropertySymbol property, bool isInterfaceParent) - { - AddPropertyDefinition( - property.Name, - new Symbol(property.Type), - property, - property.IsStatic, - property.SetMethod != null && - (property.SetMethod.DeclaredAccessibility == Accessibility.Public || - !property.SetMethod.ExplicitInterfaceImplementations.IsDefaultOrEmpty), - isInterfaceParent, - property.ExplicitInterfaceImplementations.IsDefaultOrEmpty); - } - - private TypeDefinitionHandle AddTypeDefinition( - TypeAttributes typeAttributes, - string @namespace, - string identifier, - EntityHandle baseType) - { - var fieldDefinitions = currentTypeDeclaration.GetFieldDefinitions(); - var methodDefinitions = currentTypeDeclaration.GetMethodDefinitions(); - - var typeDefinitionHandle = metadataBuilder.AddTypeDefinition( - typeAttributes, - metadataBuilder.GetOrAddString(@namespace), - metadataBuilder.GetOrAddString(identifier), - baseType, - fieldDefinitions.Count == 0 ? - MetadataTokens.FieldDefinitionHandle(metadataBuilder.GetRowCount(TableIndex.Field) + 1) : - fieldDefinitions[0], - methodDefinitions.Count == 0 ? - MetadataTokens.MethodDefinitionHandle(metadataBuilder.GetRowCount(TableIndex.MethodDef) + 1) : - methodDefinitions[0] - ); - - var propertyDefinitions = currentTypeDeclaration.GetPropertyDefinitions(); - if (propertyDefinitions.Count != 0) - { - metadataBuilder.AddPropertyMap( - typeDefinitionHandle, - propertyDefinitions[0]); - } - - var eventDefinitions = currentTypeDeclaration.GetEventDefinitions(); - if (eventDefinitions.Count != 0) - { - metadataBuilder.AddEventMap( - typeDefinitionHandle, - eventDefinitions[0]); - } - - return typeDefinitionHandle; - } - - // Based on whether System.Type is used in an attribute declaration or elsewhere, we need to choose the correct custom mapping - // as attributes don't use the TypeName mapping. - internal static (string, string, string, bool, bool) GetSystemTypeCustomMapping(ISymbol containingSymbol) - { - bool isDefinedInAttribute = - containingSymbol != null && - string.CompareOrdinal((containingSymbol as INamedTypeSymbol).BaseType?.ToString(), "System.Attribute") == 0; - return isDefinedInAttribute ? - ("System", "Type", "mscorlib", true, false) : - ("Windows.UI.Xaml.Interop", "TypeName", "Windows.Foundation.UniversalApiContract", false, true); - } - - private void ProcessCustomMappedInterfaces(INamedTypeSymbol classSymbol) - { - Logger.Log("writing custom mapped interfaces for " + QualifiedName(classSymbol)); - Dictionary isPublicImplementation = new Dictionary(); - - // Mark custom mapped interface members for removal later. - // Note we want to also mark members from interfaces without mappings. - foreach (var implementedInterface in GetInterfaces(classSymbol, true). - Where(symbol => MappedCSharpTypes.ContainsKey(QualifiedName(symbol)) || - ImplementedInterfacesWithoutMapping.Contains(QualifiedName(symbol)))) - { - bool isPubliclyImplemented = false; - Logger.Log("custom mapped interface: " + QualifiedName(implementedInterface, true)); - foreach (var interfaceMember in implementedInterface.GetMembers()) - { - var classMember = classSymbol.FindImplementationForInterfaceMember(interfaceMember); - currentTypeDeclaration.CustomMappedSymbols.Add(classMember); - - // For custom mapped interfaces, we don't have 1 to 1 mapping of members between the mapped from - // and mapped to interface and due to that we need to decide if the mapped inteface as a whole - // is public or not (explicitly implemented). Due to that, as long as one member is not - // explicitly implemented (i.e accessible via the class), we treat the entire mapped interface - // also as accessible via the class. - isPubliclyImplemented |= (classMember.DeclaredAccessibility == Accessibility.Public); - } - isPublicImplementation[implementedInterface] = isPubliclyImplemented; - } - - foreach (var implementedInterface in GetInterfaces(classSymbol) - .Where(symbol => MappedCSharpTypes.ContainsKey(QualifiedName(symbol)))) - { - WriteCustomMappedTypeMembers(implementedInterface, true, isPublicImplementation[implementedInterface]); - } - } - - INamedTypeSymbol GetTypeByMetadataName(string metadataName) - { - var namedType = Model.Compilation.GetTypeByMetadataName(metadataName); - if (namedType != null) - { - return namedType; - } - - // Model.Compilation.GetTypeByMetadataName doesn't return a type if there is multiple references with the same type. - // So as a fallback, go through all the references and check each one filtering to public ones. - var types = Model.Compilation.References - .Select(Model.Compilation.GetAssemblyOrModuleSymbol) - .OfType() - .Select(assemblySymbol => assemblySymbol.GetTypeByMetadataName(metadataName)) - .Where(type => type != null && type.DeclaredAccessibility == Accessibility.Public); - return types.FirstOrDefault(); - } - - // Convert the entire type name including the generic types to WinMD format. - private string GetMappedQualifiedTypeName(ITypeSymbol symbol) - { - string qualifiedName = QualifiedName(symbol); - if (MappedCSharpTypes.ContainsKey(qualifiedName)) - { - var (@namespace, mappedTypeName, _, _, _) = MappedCSharpTypes[qualifiedName].GetMapping(currentTypeDeclaration.Node); - qualifiedName = QualifiedName(@namespace, mappedTypeName); - if (symbol is INamedTypeSymbol namedType && namedType.TypeArguments.Length > 0) - { - return string.Format("{0}<{1}>", qualifiedName, string.Join(", ", namedType.TypeArguments.Select(type => GetMappedQualifiedTypeName(type)))); - } - } - else if ((string.CompareOrdinal(symbol.ContainingNamespace.ToString(), "System") == 0 && - symbol.IsValueType) || string.CompareOrdinal(qualifiedName, "System.String") == 0) - { - // WinRT fundamental types - return symbol.Name; - } - - return qualifiedName; - } - - private void WriteCustomMappedTypeMembers(INamedTypeSymbol symbol, bool isDefinition, bool isPublic = true) - { - var (_, mappedTypeName, _, _, _) = MappedCSharpTypes[QualifiedName(symbol)].GetMapping(currentTypeDeclaration.Node); - string qualifiedName = GetMappedQualifiedTypeName(symbol); - - Logger.Log("writing custom mapped type members for " + mappedTypeName + " public: " + isPublic + " qualified name: " + qualifiedName); - void AddMethod(string name, Parameter[] parameters, Symbol returnType) - { - parameters ??= new Parameter[0]; - if (isDefinition) - { - var methodName = isPublic ? name : QualifiedName(qualifiedName, name); - var methodDefinitionHandle = AddMethodDefinition(methodName, parameters, returnType, false, false, false, isPublic); - currentTypeDeclaration.AddMethod(symbol, methodName, methodDefinitionHandle); - } - else - { - var memberReferenceHandle = AddMethodReference(name, parameters, returnType, symbol, false); - currentTypeDeclaration.AddMethodReference(symbol, memberReferenceHandle); - } - } - - void AddProperty(string name, Symbol type, bool setProperty) - { - if (isDefinition) - { - var propertyName = isPublic ? name : QualifiedName(qualifiedName, name); - AddPropertyDefinition(propertyName, type, symbol, false, setProperty, false, isPublic); - } - else - { - AddPropertyReference(name, type, symbol, symbol, setProperty); - } - } - - void AddEvent(string name, Symbol eventType) - { - if (isDefinition) - { - var eventName = isPublic ? name : QualifiedName(qualifiedName, name); - AddEventDeclaration(eventName, eventType.Type, symbol, false, false, isPublic); - } - else - { - AddEventReference(name, eventType.Type, symbol, symbol); - } - } - - Symbol GetType(string type, bool isGeneric = false, int genericIndex = -1, bool isArray = false, ITypeSymbol[] genericTypes = null) - { - if (string.IsNullOrEmpty(type) && isGeneric) - { - return isDefinition ? new Symbol(symbol.TypeArguments[genericIndex], isArray) : new Symbol(genericIndex, isArray); - } - - var namedTypeSymbol = GetTypeByMetadataName(type); - if (!isGeneric) - { - return new Symbol(namedTypeSymbol, isArray); - } - - if (isDefinition) - { - var typeArguments = genericTypes ?? ((genericIndex == -1) ? - symbol.TypeArguments.ToArray() : new ITypeSymbol[] { symbol.TypeArguments[genericIndex] }); - return new Symbol(namedTypeSymbol.Construct(typeArguments), isArray); - } - else - { - return new Symbol(namedTypeSymbol.ConstructUnboundGenericType(), isArray); - } - } - - if (string.CompareOrdinal(mappedTypeName, "IClosable") == 0) - { - AddMethod("Close", null, null); - } - else if (string.CompareOrdinal(mappedTypeName, "IIterable`1") == 0) - { - AddMethod("First", null, GetType("System.Collections.Generic.IEnumerator`1", true)); - } - else if (string.CompareOrdinal(mappedTypeName, "IMap`2") == 0) - { - AddMethod("Clear", null, null); - AddMethod("GetView", null, GetType("System.Collections.Generic.IReadOnlyDictionary`2", true)); - AddMethod( - "HasKey", - new[] { new Parameter(GetType(null, true, 0), "key", ParameterAttributes.In) }, - GetType("System.Boolean") - ); - AddMethod( - "Insert", - new[] { - new Parameter(GetType(null, true, 0), "key", ParameterAttributes.In), - new Parameter(GetType(null, true, 1), "value", ParameterAttributes.In) - }, - GetType("System.Boolean") - ); - AddMethod( - "Lookup", - new[] { new Parameter(GetType(null, true, 0), "key", ParameterAttributes.In) }, - GetType(null, true, 1) - ); - AddMethod( - "Remove", - new[] { new Parameter(GetType(null, true, 0), "key", ParameterAttributes.In) }, - null - ); - AddProperty("Size", GetType("System.UInt32"), false); - } - else if (string.CompareOrdinal(mappedTypeName, "IMapView`2") == 0) - { - AddMethod( - "HasKey", - new[] { new Parameter(GetType(null, true, 0), "key", ParameterAttributes.In) }, - GetType("System.Boolean") - ); - AddMethod( - "Lookup", - new[] { new Parameter(GetType(null, true, 0), "key", ParameterAttributes.In) }, - GetType(null, true, 1) - ); - AddMethod( - "Split", - new[] { - new Parameter(GetType("System.Collections.Generic.IReadOnlyDictionary`2", true), "first", ParameterAttributes.Out), - new Parameter(GetType("System.Collections.Generic.IReadOnlyDictionary`2", true), "second", ParameterAttributes.Out) - }, - null - ); - AddProperty("Size", GetType("System.UInt32"), false); - } - else if (string.CompareOrdinal(mappedTypeName, "IIterator`1") == 0) - { - // make array - AddMethod( - "GetMany", - new[] { new Parameter(GetType(null, true, 0, true), "items", ParameterAttributes.In, false) }, - GetType("System.UInt32") - ); - AddMethod( - "MoveNext", - null, - GetType("System.Boolean") - ); - AddProperty("Current", GetType(null, true, 0), false); - AddProperty("HasCurrent", GetType("System.Boolean"), false); - } - else if (string.CompareOrdinal(mappedTypeName, "IVector`1") == 0) - { - AddMethod( - "Append", - new[] { new Parameter(GetType(null, true, 0), "value", ParameterAttributes.In) }, - null - ); - AddMethod("Clear", null, null); - AddMethod( - "GetAt", - new[] { new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In) }, - GetType(null, true, 0) - ); - AddMethod( - "GetMany", - new[] { - new Parameter(GetType("System.UInt32"), "startIndex", ParameterAttributes.In), - new Parameter(GetType(null, true, 0, true), "items", ParameterAttributes.In) - }, - GetType("System.UInt32") - ); - AddMethod("GetView", null, GetType("System.Collections.Generic.IReadOnlyList`1", true)); - AddMethod( - "IndexOf", - new[] { - new Parameter(GetType(null, true, 0), "value", ParameterAttributes.In), - new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.Out) - }, - GetType("System.Boolean") - ); - AddMethod( - "InsertAt", - new[] { - new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In), - new Parameter(GetType(null, true, 0), "value", ParameterAttributes.In), - }, - null - ); - AddMethod( - "RemoveAt", - new[] { new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In) }, - null - ); - AddMethod("RemoveAtEnd", null, null); - AddMethod( - "ReplaceAll", - new[] { - new Parameter(GetType(null, true, 0, true), "items", ParameterAttributes.In) - }, - null - ); - AddMethod( - "SetAt", - new[] { - new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In), - new Parameter(GetType(null, true, 0), "value", ParameterAttributes.In), - }, - null - ); - AddProperty("Size", GetType("System.UInt32"), false); - } - else if (string.CompareOrdinal(mappedTypeName, "IVectorView`1") == 0) - { - AddMethod( - "GetAt", - new[] { new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In) }, - GetType(null, true, 0) - ); - AddMethod( - "GetMany", - new[] { - new Parameter(GetType("System.UInt32"), "startIndex", ParameterAttributes.In), - new Parameter(GetType(null, true, 0, true), "items", ParameterAttributes.In) - }, - GetType("System.UInt32") - ); - AddMethod( - "IndexOf", - new[] { - new Parameter(GetType(null, true, 0), "value", ParameterAttributes.In), - new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.Out) - }, - GetType("System.Boolean") - ); - AddProperty("Size", GetType("System.UInt32"), false); - } - else if (string.CompareOrdinal(mappedTypeName, "IXamlServiceProvider") == 0) - { - AddMethod( - "GetService", - new[] { new Parameter(GetType("System.Type"), "type", ParameterAttributes.In) }, - GetType("System.Object") - ); - } - else if (string.CompareOrdinal(mappedTypeName, "INotifyDataErrorInfo") == 0) - { - AddProperty("HasErrors", GetType("System.Boolean"), false); - AddEvent( - "ErrorsChanged", - GetType("System.EventHandler`1", true, -1, false, new[] { GetType("System.ComponentModel.DataErrorsChangedEventArgs").Type })); - AddMethod( - "GetErrors", - new[] { new Parameter(GetType("System.String"), "propertyName", ParameterAttributes.In) }, - GetType("System.Collections.Generic.IEnumerable`1", true, -1, false, new[] { GetType("System.Object").Type }) - ); - } - else if (string.CompareOrdinal(mappedTypeName, "INotifyPropertyChanged") == 0) - { - AddEvent("PropertyChanged", GetType("System.ComponentModel.PropertyChangedEventHandler")); - } - else if (string.CompareOrdinal(mappedTypeName, "ICommand") == 0) - { - AddEvent( - "CanExecuteChanged", - GetType("System.EventHandler`1", true, -1, false, new[] { GetType("System.Object").Type })); - AddMethod( - "CanExecute", - new[] { new Parameter(GetType("System.Object"), "parameter", ParameterAttributes.In) }, - GetType("System.Boolean") - ); - AddMethod( - "Execute", - new[] { new Parameter(GetType("System.Object"), "parameter", ParameterAttributes.In) }, - null - ); - } - else if (string.CompareOrdinal(mappedTypeName, "IBindableIterable") == 0) - { - AddMethod("First", null, GetType("Microsoft.UI.Xaml.Interop.IBindableIterator")); - } - else if (string.CompareOrdinal(mappedTypeName, "IBindableVector") == 0) - { - AddMethod( - "Append", - new[] { new Parameter(GetType("System.Object"), "value", ParameterAttributes.In) }, - null - ); - AddMethod("Clear", null, null); - AddMethod( - "GetAt", - new[] { new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In) }, - GetType("System.Object") - ); - AddMethod("GetView", null, GetType("Microsoft.UI.Xaml.Interop.IBindableVectorView")); - AddMethod( - "IndexOf", - new[] { - new Parameter(GetType("System.Object"), "value", ParameterAttributes.In), - new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.Out) - }, - GetType("System.Boolean") - ); - AddMethod( - "InsertAt", - new[] { - new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In), - new Parameter(GetType("System.Object"), "value", ParameterAttributes.In), - }, - null - ); - AddMethod( - "RemoveAt", - new[] { new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In) }, - null - ); - AddMethod("RemoveAtEnd", null, null); - AddMethod( - "SetAt", - new[] { - new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In), - new Parameter(GetType("System.Object"), "value", ParameterAttributes.In), - }, - null - ); - AddProperty("Size", GetType("System.UInt32"), false); - } - else if (string.CompareOrdinal(mappedTypeName, "INotifyCollectionChanged") == 0) - { - AddEvent("CollectionChanged", GetType("System.Collections.Specialized.NotifyCollectionChangedEventHandler")); - } - } - - private IEnumerable GetInterfaces(INamedTypeSymbol symbol, bool includeInterfacesWithoutMappings = false) - { - HashSet interfaces = new HashSet(); - foreach (var @interface in symbol.Interfaces) - { - interfaces.Add(@interface); - interfaces.UnionWith(@interface.AllInterfaces); - } - - var baseType = symbol.BaseType; - while (baseType != null && !IsWinRTType(baseType)) - { - interfaces.UnionWith(baseType.Interfaces); - foreach (var @interface in baseType.Interfaces) - { - interfaces.UnionWith(@interface.AllInterfaces); - } - - baseType = baseType.BaseType; - } - - // If the generic enumerable is implemented, don't implement the non generic one to prevent issues - // with the interface members being implemented multiple times. - if (!includeInterfacesWithoutMappings && - interfaces.Any(@interface => string.CompareOrdinal(QualifiedName(@interface), "System.Collections.Generic.IEnumerable`1") == 0)) - { - interfaces.Remove(GetTypeByMetadataName("System.Collections.IEnumerable")); - } - - return interfaces.Where(@interface => - includeInterfacesWithoutMappings || - !ImplementedInterfacesWithoutMapping.Contains(QualifiedName(@interface))) - .OrderBy(implementedInterface => implementedInterface.ToString()); - } - - public override void VisitInterfaceDeclaration(InterfaceDeclarationSyntax node) - { - AddComponentType(Model.GetDeclaredSymbol(node), () => base.VisitInterfaceDeclaration(node)); - } - - public override void VisitClassDeclaration(ClassDeclarationSyntax node) - { - AddComponentType(Model.GetDeclaredSymbol(node), () => base.VisitClassDeclaration(node)); - } - - public override void VisitStructDeclaration(StructDeclarationSyntax node) - { - AddComponentType(Model.GetDeclaredSymbol(node), () => base.VisitStructDeclaration(node)); - } - - private void EncodeTypedConstant(TypedConstant constant, LiteralEncoder encoder) - { - Logger.Log("typed constant kind: " + constant.Kind); - Logger.Log("typed constant type: " + constant.Type); - Logger.Log("typed constant value: " + constant.Value); - - switch (constant.Kind) - { - case TypedConstantKind.Primitive: - encoder.Scalar().Constant(constant.Value); - break; - case TypedConstantKind.Enum: - encoder.TaggedScalar( - type => type.Enum(constant.Type.ToString()), - scalar => scalar.Constant(constant.Value) - ); - break; - case TypedConstantKind.Type: - encoder.Scalar().SystemType(constant.Type.ToString()); - break; - case TypedConstantKind.Array: - { - LiteralsEncoder arrayEncoder = encoder.Vector().Count(constant.Values.Length); - foreach (var arrayConstant in constant.Values) - { - EncodeTypedConstant(arrayConstant, arrayEncoder.AddLiteral()); - } - break; - } - } - } - - private void EncodeFixedArguments(IList arguments, FixedArgumentsEncoder argumentsEncoder) - { - foreach (var argument in arguments) - { - EncodeTypedConstant(argument, argumentsEncoder.AddArgument()); - } - } - - private void EncodeCustomElementType(ITypeSymbol type, CustomAttributeElementTypeEncoder typeEncoder) - { - switch (type.SpecialType) - { - case SpecialType.System_Boolean: - typeEncoder.Boolean(); - break; - case SpecialType.System_Byte: - typeEncoder.Byte(); - break; - case SpecialType.System_Int16: - typeEncoder.Int16(); - break; - case SpecialType.System_Int32: - typeEncoder.Int32(); - break; - case SpecialType.System_Int64: - typeEncoder.Int64(); - break; - case SpecialType.System_UInt16: - typeEncoder.UInt16(); - break; - case SpecialType.System_UInt32: - typeEncoder.UInt32(); - break; - case SpecialType.System_UInt64: - typeEncoder.UInt64(); - break; - case SpecialType.System_Single: - typeEncoder.Single(); - break; - case SpecialType.System_Double: - typeEncoder.Double(); - break; - case SpecialType.System_Char: - typeEncoder.Char(); - break; - case SpecialType.System_String: - typeEncoder.String(); - break; - case SpecialType.System_Enum: - typeEncoder.Enum(type.ToString()); - break; - case SpecialType.System_SByte: - typeEncoder.SByte(); - break; - default: - Logger.Log("TODO special type: " + type.SpecialType); - break; - } - } - - private void EncodeNamedArgumentType(ITypeSymbol type, NamedArgumentTypeEncoder encoder) - { - Logger.Log("encoding named type"); - if (type.SpecialType == SpecialType.System_Object) - { - encoder.Object(); - } - else if (type.SpecialType == SpecialType.System_Array) - { - // TODO array type encoder - encoder.SZArray(); - } - else - { - EncodeCustomElementType(type, encoder.ScalarType()); - } - } - - private ISymbol GetMember(INamedTypeSymbol type, string member) - { - var foundMembers = type.GetMembers(member); - var baseType = type.BaseType; - while (foundMembers.Count() == 0 && baseType != null) - { - foundMembers = baseType.GetMembers(member); - baseType = baseType.BaseType; - } - - Logger.Log("# members found: " + foundMembers.Count()); - var foundMember = foundMembers.First(); - Logger.Log("found member: " + foundMember); - return foundMember; - } - - private void EncodeNamedArguments( - INamedTypeSymbol attributeType, - IList> namedArguments, - CustomAttributeNamedArgumentsEncoder argumentsEncoder) - { - var encoder = argumentsEncoder.Count(namedArguments.Count); - foreach (var argument in namedArguments) - { - Logger.Log("named argument: " + argument.Key); - Logger.Log("value " + argument.Value); - - ITypeSymbol argumentType = null; - var attributeClassMember = GetMember(attributeType, argument.Key); - if (attributeClassMember is IFieldSymbol field) - { - argumentType = field.Type; - } - else if (attributeClassMember is IPropertySymbol property) - { - argumentType = property.Type; - } - else - { - Logger.Log("unexpected member: " + attributeClassMember.Name + " " + attributeClassMember.GetType()); - throw new InvalidOperationException(); - } - - encoder.AddArgument( - attributeClassMember is IFieldSymbol, - type => EncodeNamedArgumentType(argumentType, type), - name => name.Name(argument.Key), - literal => EncodeTypedConstant(argument.Value, literal) - ); - } - } - - private void EncodeFixedArguments(IList primitiveArguments, FixedArgumentsEncoder argumentsEncoder) - { - foreach (var argument in primitiveArguments) - { - var encoder = argumentsEncoder.AddArgument().Scalar(); - if (argument is string type) - { - encoder.SystemType(type); - } - else if (argument is INamedTypeSymbol namedTypeSymbol) - { - var typeEntity = GetTypeReference(namedTypeSymbol); - encoder.Builder.WriteReference(CodedIndex.TypeDefOrRef(typeEntity), false); - } - else - { - encoder.Constant(argument); - } - } - } - - private void AddDefaultVersionAttribute(EntityHandle parentHandle, int version = -1) - { - if (version == -1) - { - version = Version.Parse(this.version).Major; - } - - List types = new List - { - Model.Compilation.GetTypeByMetadataName("System.UInt32") - }; - - List arguments = new List - { - (UInt32) version - }; - - AddCustomAttributes("Windows.Foundation.Metadata.VersionAttribute", types, arguments, parentHandle); - } - - private void AddActivatableAttribute(EntityHandle parentHandle, UInt32 version, string factoryInterface) - { - List types = new List(2); - List arguments = new List(2); - - if (factoryInterface != null) - { - types.Add(Model.Compilation.GetTypeByMetadataName("System.Type")); - arguments.Add(factoryInterface); - } - types.Add(Model.Compilation.GetTypeByMetadataName("System.UInt32")); - arguments.Add(version); - - AddCustomAttributes("Windows.Foundation.Metadata.ActivatableAttribute", types, arguments, parentHandle); - } - - private void AddExclusiveToAttribute(EntityHandle interfaceHandle, string className) - { - List types = new List - { - Model.Compilation.GetTypeByMetadataName("System.Type") - }; - - List arguments = new List - { - className - }; - - AddCustomAttributes("Windows.Foundation.Metadata.ExclusiveToAttribute", types, arguments, interfaceHandle); - } - - private void AddStaticAttribute(EntityHandle parentHandle, UInt32 version, string staticInterface) - { - List types = new List - { - Model.Compilation.GetTypeByMetadataName("System.Type"), - Model.Compilation.GetTypeByMetadataName("System.UInt32") - }; - - List arguments = new List - { - staticInterface, - version - }; - - AddCustomAttributes("Windows.Foundation.Metadata.StaticAttribute", types, arguments, parentHandle); - } - - private void AddDefaultInterfaceImplAttribute(EntityHandle interfaceImplHandle) - { - AddCustomAttributes("Windows.Foundation.Metadata.DefaultAttribute", Array.Empty(), Array.Empty(), interfaceImplHandle); - } - - private void AddOverloadAttribute(EntityHandle methodHandle, string methodName) - { - List types = new List - { - Model.Compilation.GetTypeByMetadataName("System.String") - }; - - List arguments = new List - { - methodName - }; - - AddCustomAttributes("Windows.Foundation.Metadata.OverloadAttribute", types, arguments, methodHandle); - } - - private void AddOverloadAttributeForInterfaceMethods(TypeDeclaration interfaceTypeDeclaration) - { - // Generate unique names for any overloaded methods - foreach (var methodName in interfaceTypeDeclaration.MethodsByName.Where(symbol => symbol.Value.Count > 1)) - { - var methodSymbols = methodName.Value.Where(symbol => symbol is IMethodSymbol); - // Other members that are not methods such as properties and events can generate reserved methods - // which for the purposes of overloading are considered to be the non overloaded version. If there is no - // such function, then we consider the first encountered method to be the non overloaded version. - var skipFirstMethod = methodName.Value.Count == methodSymbols.Count(); - var lastSuffix = 1; - foreach (var method in methodSymbols) - { - if (skipFirstMethod) - { - skipFirstMethod = false; - continue; - } - - string overloadedMethodName = methodName.Key + (++lastSuffix); - while (interfaceTypeDeclaration.MethodsByName.ContainsKey(overloadedMethodName)) - { - overloadedMethodName = methodName.Key + (++lastSuffix); - } - - Logger.Log("Overloading " + methodName.Key + " with " + overloadedMethodName); - AddOverloadAttribute(interfaceTypeDeclaration.MethodDefinitions[method].First(), overloadedMethodName); - interfaceTypeDeclaration.AddMethodOverload(method, overloadedMethodName); - } - } - } - - private void AddGuidAttribute(EntityHandle parentHandle, string name) - { - Guid guid; - using (SHA1 sha = new SHA1CryptoServiceProvider()) - { - var hash = sha.ComputeHash(Encoding.UTF8.GetBytes(name)); - guid = Helper.EncodeGuid(hash); - } - - var uint32Type = Model.Compilation.GetTypeByMetadataName("System.UInt32"); - var uint16Type = Model.Compilation.GetTypeByMetadataName("System.UInt16"); - var byteType = Model.Compilation.GetTypeByMetadataName("System.Byte"); - List types = new List - { - uint32Type, - uint16Type, - uint16Type, - byteType, - byteType, - byteType, - byteType, - byteType, - byteType, - byteType, - byteType - }; - - var byteArray = guid.ToByteArray(); - List arguments = new List - { - BitConverter.ToUInt32(byteArray, 0), - BitConverter.ToUInt16(byteArray, 4), - BitConverter.ToUInt16(byteArray, 6), - byteArray[8], - byteArray[9], - byteArray[10], - byteArray[11], - byteArray[12], - byteArray[13], - byteArray[14], - byteArray[15] - }; - - AddCustomAttributes("Windows.Foundation.Metadata.GuidAttribute", types, arguments, parentHandle); - } - - private void AddCustomAttributes( - string attributeTypeName, - IList primitiveTypes, - IList primitiveValues, - EntityHandle parentHandle) - { - var attributeType = Model.Compilation.GetTypeByMetadataName(attributeTypeName); - Logger.Log("attribute type found " + attributeType); - if (!typeDefinitionMapping.ContainsKey(attributeTypeName)) - { - // Even if the attribute is an external non WinRT type, treat it as a projected type. - Logger.Log("adding attribute type"); - AddType(attributeType, true); - } - - Logger.Log("# constructor found: " + attributeType.Constructors.Length); - var matchingConstructor = attributeType.Constructors.Where(constructor => - constructor.Parameters.Length == primitiveValues.Count && - constructor.Parameters.Select(param => (param.Type is IErrorTypeSymbol) ? - Model.Compilation.GetTypeByMetadataName(param.Type.ToDisplayString()) : param.Type) - .SequenceEqual(primitiveTypes)); - - Logger.Log("# matching constructor found: " + matchingConstructor.Count()); - Logger.Log("matching constructor found: " + matchingConstructor.First()); - - var constructorReference = typeDefinitionMapping[attributeTypeName].MethodReferences[matchingConstructor.First()]; - Logger.Log("found constructor handle: " + constructorReference.Count); - - var attributeSignature = new BlobBuilder(); - new BlobEncoder(attributeSignature) - .CustomAttributeSignature( - fixedArguments => EncodeFixedArguments(primitiveValues, fixedArguments), - namedArguments => namedArguments.Count(0) - ); - - metadataBuilder.AddCustomAttribute( - parentHandle, - constructorReference.First(), - metadataBuilder.GetOrAddBlob(attributeSignature)); - } - - private void AddCustomAttributes(IEnumerable attributes, EntityHandle parentHandle) - { - foreach (var attribute in attributes) - { - var attributeType = attribute.AttributeClass; - if (attributeType.DeclaredAccessibility != Accessibility.Public) - { - continue; - } - - Logger.Log("attribute: " + attribute); - Logger.Log("attribute type: " + attributeType); - Logger.Log("attribute constructor: " + attribute.AttributeConstructor); - Logger.Log("atttribute # constructor arguments: " + attribute.ConstructorArguments.Length); - Logger.Log("atttribute # named arguments: " + attribute.NamedArguments.Length); - - if (!typeDefinitionMapping.ContainsKey(attributeType.ToString())) - { - // Even if the attribute is an external non WinRT type, treat it as a projected type. - AddType(attributeType, true); - } - - var constructorReference = typeDefinitionMapping[attributeType.ToString()].MethodReferences[attribute.AttributeConstructor]; - Logger.Log("found # constructors: " + constructorReference.Count); - - var attributeSignature = new BlobBuilder(); - new BlobEncoder(attributeSignature) - .CustomAttributeSignature( - fixedArguments => EncodeFixedArguments(attribute.ConstructorArguments, fixedArguments), - namedArguments => EncodeNamedArguments(attributeType, attribute.NamedArguments, namedArguments) - ); - - metadataBuilder.AddCustomAttribute( - parentHandle, - constructorReference.First(), - metadataBuilder.GetOrAddBlob(attributeSignature)); - } - } - - public override void VisitEnumDeclaration(EnumDeclarationSyntax node) - { - var symbol = Model.GetDeclaredSymbol(node); - - void processEnumDeclaration() - { - var enumTypeFieldAttributes = - FieldAttributes.Private | - FieldAttributes.SpecialName | - FieldAttributes.RTSpecialName; - - var enumTypeSymbol = symbol.EnumUnderlyingType; - var fieldSignature = new BlobBuilder(); - var encoder = new BlobEncoder(fieldSignature); - // TODO: special type enforcement for 64 bit - EncodeSpecialType(enumTypeSymbol.SpecialType, encoder.FieldSignature()); - - var fieldDefinitionHandle = metadataBuilder.AddFieldDefinition( - enumTypeFieldAttributes, - metadataBuilder.GetOrAddString("value__"), - metadataBuilder.GetOrAddBlob(fieldSignature)); - currentTypeDeclaration.AddField(symbol, fieldDefinitionHandle); - - base.VisitEnumDeclaration(node); - } - - AddComponentType(symbol, processEnumDeclaration); - } - - public override void VisitDelegateDeclaration(DelegateDeclarationSyntax node) - { - var symbol = Model.GetDeclaredSymbol(node); - if (!IsPublic(symbol)) - { - return; - } - - Logger.Log("defining delegate " + symbol.Name); - currentTypeDeclaration = new TypeDeclaration(symbol); - - base.VisitDelegateDeclaration(node); - CheckAndMarkSymbolForAttributes(symbol); - - var objType = Model.Compilation.GetTypeByMetadataName(typeof(object).FullName); - var nativeIntType = Model.Compilation.GetTypeByMetadataName(typeof(IntPtr).FullName); - - currentTypeDeclaration.AddMethod( - symbol, - ".ctor", - AddMethodDefinition( - ".ctor", - new Parameter[] { - new Parameter(objType, "object", ParameterAttributes.None), - new Parameter(nativeIntType, "method", ParameterAttributes.None) - }, - null, - false, - false, - true, - false, - true - ) - ); - - Parameter[] parameters = Parameter.GetParameters(node.ParameterList, Model); - currentTypeDeclaration.AddMethod( - symbol, - "Invoke", - AddMethodDefinition( - "Invoke", - parameters, - new Symbol(symbol.DelegateInvokeMethod.ReturnType), - false, - false, - true, - true, - true - ) - ); - - TypeAttributes typeAttributes = - TypeAttributes.Public | - TypeAttributes.WindowsRuntime | - TypeAttributes.AutoLayout | - TypeAttributes.AnsiClass | - TypeAttributes.Sealed; - - var typeDefinitionHandle = AddTypeDefinition( - typeAttributes, - symbol.ContainingNamespace.ToString(), - symbol.Name, - GetTypeReference("System", "MulticastDelegate", "mscorlib")); - currentTypeDeclaration.Handle = typeDefinitionHandle; - typeDefinitionMapping[QualifiedName(symbol, true)] = currentTypeDeclaration; - - AddGuidAttribute(typeDefinitionHandle, symbol.ToString()); - } - - public void AddEventDeclaration(string eventName, ITypeSymbol eventType, ISymbol symbol, bool isStatic, bool isInterfaceParent, bool isPublic = true) - { - Logger.Log("defining event " + eventName + " with type " + eventType.ToString()); - - GetNamespaceAndTypename(eventName, out var @namespace, out var typename); - - var delegateSymbolType = eventType as INamedTypeSymbol; - EntityHandle typeReferenceHandle = GetTypeSpecification(delegateSymbolType); - EntityHandle eventRegistrationTokenTypeHandle = GetTypeReference("Windows.Foundation", "EventRegistrationToken", "Windows.Foundation.FoundationContract"); - Symbol eventRegistrationToken = new Symbol(eventRegistrationTokenTypeHandle); - - var eventDefinitionHandle = metadataBuilder.AddEvent( - EventAttributes.None, - metadataBuilder.GetOrAddString(eventName), - typeReferenceHandle); - currentTypeDeclaration.AddEvent(symbol, eventDefinitionHandle); - - string addMethodName = QualifiedName(@namespace, "add_" + typename); - var addMethod = AddMethodDefinition( - addMethodName, - new Parameter[] { new Parameter(delegateSymbolType, "handler", ParameterAttributes.In) }, - eventRegistrationToken, - !isInterfaceParent && isStatic, - isInterfaceParent, - true, - isPublic); - currentTypeDeclaration.AddMethod(symbol, addMethodName, addMethod); - - metadataBuilder.AddMethodSemantics( - eventDefinitionHandle, - MethodSemanticsAttributes.Adder, - addMethod); - - string removeMethodName = QualifiedName(@namespace, "remove_" + typename); - var removeMethod = AddMethodDefinition( - removeMethodName, - new Parameter[] { new Parameter(eventRegistrationToken, "token", ParameterAttributes.In) }, - null, - !isInterfaceParent && isStatic, - isInterfaceParent, - true, - isPublic); - currentTypeDeclaration.AddMethod(symbol, removeMethodName, removeMethod); - - metadataBuilder.AddMethodSemantics( - eventDefinitionHandle, - MethodSemanticsAttributes.Remover, - removeMethod); - } - - public void AddEventDeclaration(IEventSymbol @event, bool isInterfaceParent) - { - AddEventDeclaration(@event.Name, @event.Type, @event, @event.IsStatic, isInterfaceParent, @event.ExplicitInterfaceImplementations.IsDefaultOrEmpty); - } - - void AddMethodDeclaration(IMethodSymbol method, bool isInterfaceParent) - { - Logger.Log("add method from symbol: " + method.Name); - - bool isConstructor = method.MethodKind == MethodKind.Constructor; - string methodName = isConstructor ? ".ctor" : method.Name; - var returnType = isConstructor ? null : new Symbol(method.ReturnType); - Parameter[] parameters = Parameter.GetParameters(method); - var methodDefinitionHandle = AddMethodDefinition( - methodName, - parameters, - returnType, - !isInterfaceParent && method.IsStatic, - isInterfaceParent, - isConstructor, - method.ExplicitInterfaceImplementations.IsDefaultOrEmpty); - currentTypeDeclaration.AddMethod(method, methodName, methodDefinitionHandle); - } - - void AddFactoryMethod(INamedTypeSymbol classSymbol, IMethodSymbol method) - { - Logger.Log("adding factory method: " + method.Name); - - string methodName = "Create" + classSymbol.Name; - Parameter[] parameters = Parameter.GetParameters(method); - var methodDefinitionHandle = AddMethodDefinition( - methodName, - parameters, - new Symbol(classSymbol), - false, - true, - false, - true, - true); - currentTypeDeclaration.AddMethod(method, methodName, methodDefinitionHandle); - } - - void AddComponentType(INamedTypeSymbol type, Action visitTypeDeclaration = null) - { - if (!IsPublic(type) || typeDefinitionMapping.ContainsKey(type.ToString())) - { - return; - } - - Logger.Log("defining type: " + type.TypeKind + " " + type.ToString()); - - var typeDeclaration = new TypeDeclaration(type); - currentTypeDeclaration = typeDeclaration; - - if (type.TypeKind == TypeKind.Class) - { - ProcessCustomMappedInterfaces(type); - } - - visitTypeDeclaration?.Invoke(); - CheckAndMarkSymbolForAttributes(type); - - bool isInterface = type.TypeKind == TypeKind.Interface; - bool hasConstructor = false; - bool hasDefaultConstructor = false; - foreach (var member in type.GetMembers()) - { - if (!IsPublic(member) || typeDeclaration.CustomMappedSymbols.Contains(member)) - { - Logger.Log(member.Kind + " member skipped " + member.Name); - continue; - } - - if (type.TypeKind == TypeKind.Struct || type.TypeKind == TypeKind.Enum) - { - if (member is IFieldSymbol field) - { - AddFieldDeclaration(field, type.TypeKind == TypeKind.Enum); - } - } - else - { - if (member is IMethodSymbol method && - (method.MethodKind == MethodKind.Ordinary || - method.MethodKind == MethodKind.ExplicitInterfaceImplementation || - method.MethodKind == MethodKind.Constructor)) - { - AddMethodDeclaration(method, isInterface); - - if (method.MethodKind == MethodKind.Constructor) - { - hasConstructor = true; - hasDefaultConstructor |= (method.Parameters.Length == 0); - } - } - else if (member is IPropertySymbol property) - { - AddPropertyDeclaration(property, isInterface); - } - else if (member is IEventSymbol @event) - { - AddEventDeclaration(@event, isInterface); - } - else - { - Logger.Log("member not recognized: " + member.Kind + " name: " + member.Name); - continue; - } - } - - CheckAndMarkSymbolForAttributes(member); - } - - // implicit constructor if none defined - if (!hasConstructor && type.TypeKind == TypeKind.Class && !type.IsStatic) - { - string constructorMethodName = ".ctor"; - var methodDefinitionHandle = AddMethodDefinition( - constructorMethodName, - new Parameter[0], - null, - false, - false, - true, - true, - true); - typeDeclaration.AddMethod(type, constructorMethodName, methodDefinitionHandle); - hasDefaultConstructor = true; - } - - TypeAttributes typeAttributes = - TypeAttributes.Public | - TypeAttributes.WindowsRuntime | - TypeAttributes.AutoLayout | - TypeAttributes.AnsiClass; - - if (type.IsSealed || - type.IsStatic || - type.TypeKind == TypeKind.Struct || - type.TypeKind == TypeKind.Enum) - { - typeAttributes |= TypeAttributes.Sealed; - } - - if (type.TypeKind == TypeKind.Class && type.IsStatic) - { - typeAttributes |= TypeAttributes.Abstract; - } - - EntityHandle baseType = default; - if (isInterface) - { - typeAttributes |= - TypeAttributes.Interface | - TypeAttributes.Abstract; - } - else if (type.TypeKind == TypeKind.Class) - { - typeAttributes |= - TypeAttributes.Class | - TypeAttributes.BeforeFieldInit; - - // extends - if (type.BaseType != null) - { - baseType = GetTypeReference(type.BaseType); - } - else - { - baseType = GetTypeReference("System", "Object", "mscorlib"); - } - } - else if (type.TypeKind == TypeKind.Struct) - { - typeAttributes |= TypeAttributes.SequentialLayout; - baseType = GetTypeReference("System", "ValueType", "mscorlib"); - } - else if (type.TypeKind == TypeKind.Enum) - { - baseType = GetTypeReference("System", "Enum", "mscorlib"); - } - - var typeDefinitionHandle = AddTypeDefinition( - typeAttributes, - type.ContainingNamespace.ToString(), - type.Name, - baseType); - typeDeclaration.Handle = typeDefinitionHandle; - - if (isInterface || type.TypeKind == TypeKind.Class) - { - // Interface implementations need to be added in order of class and then typespec. Given entries are added - // per class, that is already in order, but we need to sort by typespec. - List> implementedInterfaces = new List>(); - foreach (var implementedInterface in GetInterfaces(type)) - { - implementedInterfaces.Add(new KeyValuePair(implementedInterface, GetTypeSpecification(implementedInterface))); - } - implementedInterfaces.Sort((x, y) => CodedIndex.TypeDefOrRefOrSpec(x.Value).CompareTo(CodedIndex.TypeDefOrRefOrSpec(y.Value))); - - foreach (var implementedInterface in implementedInterfaces) - { - var interfaceImplHandle = metadataBuilder.AddInterfaceImplementation( - typeDefinitionHandle, - implementedInterface.Value); - typeDeclaration.AddInterfaceImpl(implementedInterface.Key, interfaceImplHandle); - } - } - - if (isInterface) - { - AddGuidAttribute(typeDefinitionHandle, type.ToString()); - AddOverloadAttributeForInterfaceMethods(typeDeclaration); - } - - typeDefinitionMapping[QualifiedName(type, true)] = typeDeclaration; - - if (type.TypeKind == TypeKind.Class) - { - if (hasDefaultConstructor) - { - AddActivatableAttribute( - typeDeclaration.Handle, - (uint)GetVersion(type, true), - null); - } - AddSynthesizedInterfaces(typeDeclaration); - - // No synthesized default interface generated - if (typeDeclaration.DefaultInterface == null && type.Interfaces.Length != 0) - { - AddDefaultInterfaceImplAttribute(typeDeclaration.InterfaceImplDefinitions[type.Interfaces[0]]); - } - } - } - - MemberReferenceHandle AddMethodReference( - string name, - Parameter[] parameters, - Symbol returnSymbol, - INamedTypeSymbol parentType, - bool isStatic) - { - var methodSignature = new BlobBuilder(); - new BlobEncoder(methodSignature) - .MethodSignature( - SignatureCallingConvention.Default, - 0, - !isStatic) - .Parameters( - parameters.Length, - returnType => EncodeReturnType(returnSymbol, returnType), - parametersEncoder => EncodeParameters(parameters, parametersEncoder) - ); - - var referenceHandle = metadataBuilder.AddMemberReference( - GetTypeSpecification(parentType), - metadataBuilder.GetOrAddString(name), - metadataBuilder.GetOrAddBlob(methodSignature) - ); - return referenceHandle; - } - - MemberReferenceHandle AddMethodReference(IMethodSymbol method) - { - Logger.Log("adding method reference: " + method.Name); - - bool isInterfaceParent = method.ContainingType.TypeKind == TypeKind.Interface; - string methodName = method.MethodKind == MethodKind.Constructor ? ".ctor" : method.Name; - Parameter[] parameters = Parameter.GetParameters(method); - var referenceHandle = AddMethodReference( - methodName, - parameters, - new Symbol(method.ReturnType), - method.ContainingType, - !isInterfaceParent && method.IsStatic); - currentTypeDeclaration.AddMethodReference(method, referenceHandle); - return referenceHandle; - } - - public void AddPropertyReference(string name, Symbol type, ISymbol symbol, INamedTypeSymbol parent, bool setMethod) - { - Logger.Log("adding property reference: " + name); - - if (setMethod) - { - var setMethodReference = AddMethodReference( - "put_" + name, - new Parameter[] { new Parameter(type, "value", ParameterAttributes.In) }, - null, - parent, - false); - currentTypeDeclaration.AddMethodReference(symbol, setMethodReference); - } - - var getMethodReference = AddMethodReference( - "get_" + name, - new Parameter[0], - type, - parent, - false); - currentTypeDeclaration.AddMethodReference(symbol, getMethodReference); - } - - public void AddPropertyReference(IPropertySymbol property) - { - AddPropertyReference( - property.Name, - new Symbol(property.Type), - property, - property.ContainingType, - property.SetMethod != null); - } - - public void AddEventReference(string eventName, ITypeSymbol eventType, ISymbol symbol, INamedTypeSymbol parent) - { - Logger.Log("adding event reference: " + eventName); - - EntityHandle eventRegistrationTokenTypeHandle = GetTypeReference("Windows.Foundation", "EventRegistrationToken", "Windows.Foundation.FoundationContract"); - Symbol eventRegistrationToken = new Symbol(eventRegistrationTokenTypeHandle); - - var addMethodReference = AddMethodReference( - "add_" + eventName, - new Parameter[] { new Parameter(eventType, "handler", ParameterAttributes.In) }, - eventRegistrationToken, - parent, - false); - currentTypeDeclaration.AddMethodReference(symbol, addMethodReference); - - var removeMethodReference = AddMethodReference( - "remove_" + eventName, - new Parameter[] { new Parameter(eventRegistrationToken, "token", ParameterAttributes.In) }, - null, - parent, - false); - currentTypeDeclaration.AddMethodReference(symbol, removeMethodReference); - } - - public void AddEventReference(IEventSymbol @event) - { - AddEventReference(@event.Name, @event.Type, @event, @event.ContainingType); - } - - void AddProjectedType(INamedTypeSymbol type, string projectedTypeOverride = null) - { - currentTypeDeclaration = new TypeDeclaration(type); - - foreach (var member in type.GetMembers()) - { - if (member is IMethodSymbol method && - (method.MethodKind == MethodKind.Ordinary || - method.MethodKind == MethodKind.ExplicitInterfaceImplementation || - method.MethodKind == MethodKind.Constructor)) - { - AddMethodReference(method); - } - else if (member is IPropertySymbol property) - { - AddPropertyReference(property); - } - else if (member is IEventSymbol @event) - { - AddEventReference(@event); - } - else - { - Logger.Log("member not recognized: " + member.Kind + " " + member.Name); - } - } - - typeDefinitionMapping[projectedTypeOverride ?? QualifiedName(type, true)] = currentTypeDeclaration; - } - - void AddMappedType(INamedTypeSymbol type) - { - currentTypeDeclaration = new TypeDeclaration(type); - WriteCustomMappedTypeMembers(type, false); - typeDefinitionMapping[QualifiedName(type, true)] = currentTypeDeclaration; - } - - enum SynthesizedInterfaceType - { - Static, - Factory, - Default - } - - string GetSynthesizedInterfaceName(string className, SynthesizedInterfaceType type) - { - // TODO: handle existing types by appending number suffix - return "I" + className + - type switch - { - SynthesizedInterfaceType.Default => "Class", - SynthesizedInterfaceType.Factory => "Factory", - SynthesizedInterfaceType.Static => "Static", - _ => "", - }; - } - - void AddSynthesizedInterfaces(TypeDeclaration classDeclaration) - { - HashSet classMembersFromInterfaces = new HashSet(); - INamedTypeSymbol classSymbol = classDeclaration.Node as INamedTypeSymbol; - foreach (var @interface in classSymbol.AllInterfaces) - { - foreach (var interfaceMember in @interface.GetMembers()) - { - var classMember = classSymbol.FindImplementationForInterfaceMember(interfaceMember); - if (classMember == null || !classDeclaration.MethodDefinitions.ContainsKey(classMember)) - { - continue; - } - - classMembersFromInterfaces.Add(classMember); - classDeclaration.ClassInterfaceMemberMapping[classMember] = interfaceMember; - - // Mark class members whose interface declaration has attributes - // so that we can propagate them later. - if (interfaceMember.GetAttributes().Any()) - { - classDeclaration.SymbolsWithAttributes.Add(classMember); - } - } - } - - AddSynthesizedInterface( - classDeclaration, - SynthesizedInterfaceType.Static, - classMembersFromInterfaces); - - AddSynthesizedInterface( - classDeclaration, - SynthesizedInterfaceType.Factory, - classMembersFromInterfaces); - - AddSynthesizedInterface( - classDeclaration, - SynthesizedInterfaceType.Default, - classMembersFromInterfaces); - - // TODO: address overridable and composable interfaces. - } - - void CheckAndMarkSynthesizedInterfaceSymbolForAttributes(ISymbol symbol, TypeDeclaration classDeclaration) - { - // Check the class declaration if the symbol had any attributes marked for it, - // and if so propagate it to the synthesized interface. - if (classDeclaration.SymbolsWithAttributes.Contains(symbol)) - { - currentTypeDeclaration.SymbolsWithAttributes.Add(symbol); - } - } - - void CheckAndMarkSymbolForAttributes(ISymbol symbol) - { - if (symbol.GetAttributes().Any()) - { - currentTypeDeclaration.SymbolsWithAttributes.Add(symbol); - } - } - - void AddSynthesizedInterface( - TypeDeclaration classDeclaration, - SynthesizedInterfaceType interfaceType, - HashSet classMembersFromInterfaces) - { - var typeDeclaration = new TypeDeclaration(); - currentTypeDeclaration = typeDeclaration; - - bool hasTypes = false; - INamedTypeSymbol classSymbol = classDeclaration.Node as INamedTypeSymbol; - - // Each class member results in some form of method definition, - // so using that to our advantage to get public members. - foreach (var classMember in classDeclaration.MethodDefinitions) - { - if (interfaceType == SynthesizedInterfaceType.Factory && - classMember.Key is IMethodSymbol constructorMethod && - constructorMethod.MethodKind == MethodKind.Constructor && - constructorMethod.Parameters.Length != 0) - { - hasTypes = true; - AddFactoryMethod(classSymbol, constructorMethod); - CheckAndMarkSynthesizedInterfaceSymbolForAttributes(classMember.Key, classDeclaration); - } - else if ((interfaceType == SynthesizedInterfaceType.Default && !classMember.Key.IsStatic && - !classMembersFromInterfaces.Contains(classMember.Key)) || - (interfaceType == SynthesizedInterfaceType.Static && classMember.Key.IsStatic)) - { - if (classMember.Key is IMethodSymbol method && method.MethodKind == MethodKind.Ordinary) - { - AddMethodDeclaration(method, true); - } - else if (classMember.Key is IPropertySymbol property) - { - AddPropertyDeclaration(property, true); - } - else if (classMember.Key is IEventSymbol @event) - { - AddEventDeclaration(@event, true); - } - else - { - Logger.Log("member for synthesized interface not recognized: " + classMember.Key.Kind + " " + classMember.Key.Name); - continue; - } - - CheckAndMarkSynthesizedInterfaceSymbolForAttributes(classMember.Key, classDeclaration); - hasTypes = true; - } - } - - TypeAttributes typeAttributes = - TypeAttributes.NotPublic | - TypeAttributes.WindowsRuntime | - TypeAttributes.AutoLayout | - TypeAttributes.AnsiClass | - TypeAttributes.Interface | - TypeAttributes.Abstract; - - if (hasTypes || (interfaceType == SynthesizedInterfaceType.Default && classSymbol.Interfaces.Length == 0)) - { - Logger.Log("writing generated interface " + interfaceType); - var interfaceName = GetSynthesizedInterfaceName(classDeclaration.Node.Name, interfaceType); - var typeDefinitionHandle = AddTypeDefinition( - typeAttributes, - classDeclaration.Node.ContainingNamespace.ToString(), - interfaceName, - default); - typeDeclaration.Handle = typeDefinitionHandle; - - string qualifiedInterfaceName = QualifiedName(classDeclaration.Node.ContainingNamespace.ToString(), interfaceName); - typeDefinitionMapping[qualifiedInterfaceName] = typeDeclaration; - - if (interfaceType == SynthesizedInterfaceType.Default) - { - classDeclaration.DefaultInterface = qualifiedInterfaceName; - var interfaceImplHandle = metadataBuilder.AddInterfaceImplementation( - classDeclaration.Handle, - GetTypeReference(classDeclaration.Node.ContainingNamespace.ToString(), interfaceName, assembly)); - classDeclaration.AddInterfaceImpl(classSymbol, interfaceImplHandle); - AddDefaultInterfaceImplAttribute(interfaceImplHandle); - } - - AddDefaultVersionAttribute(typeDefinitionHandle, GetVersion(classSymbol, true)); - AddGuidAttribute(typeDefinitionHandle, interfaceName); - AddExclusiveToAttribute(typeDefinitionHandle, classSymbol.ToString()); - AddOverloadAttributeForInterfaceMethods(typeDeclaration); - - if (interfaceType == SynthesizedInterfaceType.Factory) - { - AddActivatableAttribute(classDeclaration.Handle, (uint)GetVersion(classSymbol, true), qualifiedInterfaceName); - } - else if (interfaceType == SynthesizedInterfaceType.Static) - { - classDeclaration.StaticInterface = qualifiedInterfaceName; - AddStaticAttribute(classDeclaration.Handle, (uint)GetVersion(classSymbol, true), qualifiedInterfaceName); - } - } - } - - private int GetVersion(INamedTypeSymbol type, bool setDefaultIfNotSet = false) - { - var versionAttribute = type.GetAttributes(). - Where(attribute => string.CompareOrdinal(attribute.AttributeClass.Name, "VersionAttribute") == 0); - if (!versionAttribute.Any()) - { - return setDefaultIfNotSet ? Version.Parse(this.version).Major : -1; - } - - uint version = (uint)versionAttribute.First().ConstructorArguments[0].Value; - return (int)version; - } - - void AddType(INamedTypeSymbol type, bool treatAsProjectedType = false) - { - Logger.Log("add type: " + type.ToString()); - bool isProjectedType = type.GetAttributes(). - Any(attribute => string.CompareOrdinal(attribute.AttributeClass.Name, "WindowsRuntimeTypeAttribute") == 0); - var qualifiedName = QualifiedName(type); - if (isProjectedType) +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.Reflection; +using System.Reflection.Metadata; +using System.Reflection.Metadata.Ecma335; +using System.Security.Cryptography; +using System.Text; + +namespace Generator +{ + class Parameter + { + public Symbol Type; + public string Name; + public ParameterAttributes Attributes; + public bool ByRef; + + public Parameter(Symbol type, string name, ParameterAttributes attributes) + : this(type, name, attributes, attributes == ParameterAttributes.Out) + { + } + + public Parameter(Symbol type, string name, ParameterAttributes attributes, bool byRef) + { + Type = type; + Name = name; + Attributes = attributes; + ByRef = byRef; + } + + public Parameter(ITypeSymbol type, string name, ParameterAttributes attributes) + : this(new Symbol(type), name, attributes) + { + } + + public Parameter(EntityHandle type, string name, ParameterAttributes attributes) + : this(new Symbol(type), name, attributes) + { + } + + public Parameter(IParameterSymbol parameterSymbol) + { + // Set out parameter attribute if write only array. + bool isWriteOnlyArray = parameterSymbol.Type is IArrayTypeSymbol && + parameterSymbol.GetAttributes().Where( + attr => string.CompareOrdinal(attr.AttributeClass.ToString(), "System.Runtime.InteropServices.WindowsRuntime.WriteOnlyArrayAttribute") == 0 + ).Count() != 0; + + Type = new Symbol(parameterSymbol.Type); + Name = parameterSymbol.Name; + Attributes = (parameterSymbol.RefKind == RefKind.Out || isWriteOnlyArray) ? ParameterAttributes.Out : ParameterAttributes.In; + ByRef = parameterSymbol.RefKind == RefKind.Out; + } + + public static Parameter[] GetParameters(ParameterListSyntax parameterList, SemanticModel model) + { + int numParameters = parameterList.Parameters.Count; + Parameter[] parameters = new Parameter[numParameters]; + for (int idx = 0; idx < numParameters; idx++) + { + parameters[idx] = new Parameter(model.GetDeclaredSymbol(parameterList.Parameters[idx])); + } + + return parameters; + } + + public static Parameter[] GetParameters(IMethodSymbol method) + { + int numParameters = method.Parameters.Count(); + Parameter[] parameters = new Parameter[numParameters]; + for (int idx = 0; idx < numParameters; idx++) + { + parameters[idx] = new Parameter(method.Parameters[idx]); + } + + return parameters; + } + } + + class Symbol + { + public ITypeSymbol Type; + public EntityHandle Handle; + public int GenericIndex; + public bool IsArray; + + public Symbol(ITypeSymbol type, bool isArray = false) + { + Type = type; + Handle = default; + GenericIndex = -1; + IsArray = isArray; + } + + public Symbol(EntityHandle handle) + { + Type = default; + Handle = handle; + GenericIndex = -1; + } + + public Symbol(int genericIndex, bool isArray) + { + Type = default; + Handle = default; + GenericIndex = genericIndex; + IsArray = isArray; + } + + public bool IsHandle() + { + return Handle != default; + } + + public bool IsGeneric() + { + return GenericIndex != -1; + } + } + + class TypeDeclaration + { + public readonly ISymbol Node; + public TypeDefinitionHandle Handle; + public string DefaultInterface; + public string StaticInterface; + public bool IsSynthesizedInterface; + public bool IsComponentType; + + public Dictionary> MethodDefinitions = new(SymbolEqualityComparer.Default); + public Dictionary> MethodReferences = new(SymbolEqualityComparer.Default); + public Dictionary FieldDefinitions = new(SymbolEqualityComparer.Default); + public Dictionary PropertyDefinitions = new(SymbolEqualityComparer.Default); + public Dictionary EventDefinitions = new(SymbolEqualityComparer.Default); + public Dictionary InterfaceImplDefinitions = new(SymbolEqualityComparer.Default); + public Dictionary> MethodsByName = new Dictionary>(StringComparer.Ordinal); + public Dictionary OverloadedMethods = new(SymbolEqualityComparer.Default); + public List CustomMappedSymbols = new(); + public HashSet SymbolsWithAttributes = new(SymbolEqualityComparer.Default); + public Dictionary ClassInterfaceMemberMapping = new(SymbolEqualityComparer.Default); + + public TypeDeclaration() + : this(null) + { + IsSynthesizedInterface = true; + } + + public TypeDeclaration(ISymbol node, bool isComponentType = false) + { + Node = node; + Handle = default; + IsSynthesizedInterface = false; + IsComponentType = isComponentType; + } + + public override string ToString() + { + return Node.ToString(); + } + + public void AddMethod(ISymbol node, string name, MethodDefinitionHandle handle) + { + if (!MethodDefinitions.ContainsKey(node)) + { + MethodDefinitions[node] = new List(); + MethodReferences[node] = new List(); + } + + if (!MethodsByName.ContainsKey(name)) + { + MethodsByName[name] = new List(); + } + + MethodDefinitions[node].Add(handle); + MethodReferences[node].Add(handle); + MethodsByName[name].Add(node); + } + + public void AddMethodReference(ISymbol node, MemberReferenceHandle handle) + { + if (!MethodReferences.ContainsKey(node)) + { + MethodReferences[node] = new List(); + } + + MethodReferences[node].Add(handle); + } + + public void AddMethodOverload(ISymbol node, string overloadedMethodName) + { + OverloadedMethods[node] = overloadedMethodName; + } + + public List GetMethodDefinitions() + { + return MethodDefinitions.Values.SelectMany(list => list).ToList(); + } + + public List GetMethodReferences() + { + return MethodReferences.Values.SelectMany(list => list).ToList(); + } + + public void AddField(ISymbol node, FieldDefinitionHandle handle) + { + FieldDefinitions[node] = handle; + } + + public List GetFieldDefinitions() + { + return FieldDefinitions.Values.ToList(); + } + + public void AddProperty(ISymbol node, PropertyDefinitionHandle handle) + { + PropertyDefinitions[node] = handle; + } + + public List GetPropertyDefinitions() + { + return PropertyDefinitions.Values.ToList(); + } + + public void AddEvent(ISymbol node, EventDefinitionHandle handle) + { + EventDefinitions[node] = handle; + } + + public List GetEventDefinitions() + { + return EventDefinitions.Values.ToList(); + } + + public void AddInterfaceImpl(ISymbol node, InterfaceImplementationHandle handle) + { + InterfaceImplDefinitions[node] = handle; + } + + } + + class WinRTTypeWriter : CSharpSyntaxWalker + { + internal static readonly List ImplementedInterfacesWithoutMapping = new List() + { + "System.Collections.Generic.ICollection`1", + "System.Collections.Generic.IReadOnlyCollection`1", + "System.Collections.ICollection", + "System.Collections.IEnumerator", + "System.IEquatable`1", + "System.Runtime.InteropServices.ICustomQueryInterface", + "System.Runtime.InteropServices.IDynamicInterfaceCastable", + "WinRT.IWinRTObject" + }; + + public SemanticModel Model; + + private readonly string assembly; + private readonly string version; + + private readonly Dictionary typeReferenceMapping; + private readonly Dictionary assemblyReferenceMapping; + private readonly MetadataBuilder metadataBuilder; + private readonly TypeMapper mapper; + private readonly Dictionary typeDefinitionMapping; + private TypeDeclaration currentTypeDeclaration; + + private readonly Dictionary overridedRuntimeClassNameMappings; + + private Logger Logger { get; } + + public WinRTTypeWriter( + string assembly, + string version, + MetadataBuilder metadataBuilder, + Logger logger, + TypeMapper mapper) + { + this.assembly = assembly; + this.version = version; + this.metadataBuilder = metadataBuilder; + Logger = logger; + this.mapper = mapper; + typeReferenceMapping = new Dictionary(StringComparer.Ordinal); + assemblyReferenceMapping = new Dictionary(StringComparer.Ordinal); + typeDefinitionMapping = new Dictionary(StringComparer.Ordinal); + overridedRuntimeClassNameMappings = new Dictionary(StringComparer.Ordinal); + + CreteAssembly(); + } + + private void CreteAssembly() + { + Logger.Log("Generating assembly " + assembly + " version " + version); + metadataBuilder.AddAssembly( + metadataBuilder.GetOrAddString(assembly), + new Version(version), + default, + default, + AssemblyFlags.WindowsRuntime, + AssemblyHashAlgorithm.Sha1); + + var moduleDefinition = metadataBuilder.AddModule( + 0, + metadataBuilder.GetOrAddString(assembly + ".winmd"), + metadataBuilder.GetOrAddGuid(Guid.NewGuid()), + default, + default); + assemblyReferenceMapping[assembly] = moduleDefinition; + + metadataBuilder.AddTypeDefinition( + default, + default, + metadataBuilder.GetOrAddString(""), + default, + MetadataTokens.FieldDefinitionHandle(1), + MetadataTokens.MethodDefinitionHandle(1)); + } + + private bool IsEncodableAsSpecialType(SpecialType specialType) + { + return specialType != SpecialType.None && specialType <= SpecialType.System_Array; + } + + private void EncodeSpecialType(SpecialType specialType, SignatureTypeEncoder typeEncoder) + { + switch (specialType) + { + case SpecialType.System_Boolean: + typeEncoder.Boolean(); + break; + case SpecialType.System_Byte: + typeEncoder.Byte(); + break; + case SpecialType.System_Int16: + typeEncoder.Int16(); + break; + case SpecialType.System_Int32: + typeEncoder.Int32(); + break; + case SpecialType.System_Int64: + typeEncoder.Int64(); + break; + case SpecialType.System_UInt16: + typeEncoder.UInt16(); + break; + case SpecialType.System_UInt32: + typeEncoder.UInt32(); + break; + case SpecialType.System_UInt64: + typeEncoder.UInt64(); + break; + case SpecialType.System_Single: + typeEncoder.Single(); + break; + case SpecialType.System_Double: + typeEncoder.Double(); + break; + case SpecialType.System_Char: + typeEncoder.Char(); + break; + case SpecialType.System_String: + typeEncoder.String(); + break; + case SpecialType.System_Object: + typeEncoder.Object(); + break; + case SpecialType.System_IntPtr: + typeEncoder.IntPtr(); + break; + default: + Logger.Log("TODO special type: " + specialType); + break; + } + + // TODO: handle C# interface mappings for special types + } + + private BlobHandle GetStrongNameKey(string assembly) + { + if (string.CompareOrdinal(assembly, "mscorlib") == 0) + { + byte[] mscorlibStrongName = { 0xb7, 0x7a, 0x5c, 0x56, 0x19, 0x34, 0xe0, 0x89 }; + return metadataBuilder.GetOrAddBlob(mscorlibStrongName); + } + + return default; + } + + private EntityHandle GetTypeReference(string @namespace, string name, string assembly) + { + string fullname = QualifiedName(@namespace, name); + if (typeReferenceMapping.ContainsKey(fullname)) + { + return typeReferenceMapping[fullname]; + } + + if (!assemblyReferenceMapping.ContainsKey(assembly)) + { + EntityHandle assemblyReference = metadataBuilder.AddAssemblyReference( + metadataBuilder.GetOrAddString(assembly), + new Version(0xff, 0xff, 0xff, 0xff), + default, + GetStrongNameKey(assembly), + string.CompareOrdinal(assembly, "mscorlib") == 0 ? default : AssemblyFlags.WindowsRuntime, + default); + assemblyReferenceMapping[assembly] = assemblyReference; + } + + var typeRef = metadataBuilder.AddTypeReference( + assemblyReferenceMapping[assembly], + metadataBuilder.GetOrAddString(@namespace), + metadataBuilder.GetOrAddString(name)); + typeReferenceMapping[fullname] = typeRef; + return typeRef; + } + + public string GetAssemblyForWinRTType(ISymbol type) + { + var winrtTypeAttribute = type.GetAttributes(). + Where(attribute => string.CompareOrdinal(attribute.AttributeClass.Name, "WindowsRuntimeTypeAttribute") == 0); + if (winrtTypeAttribute.Any()) + { + return (string)winrtTypeAttribute.First().ConstructorArguments[0].Value; + } + + return null; + } + + private EntityHandle GetTypeReference(ISymbol symbol) + { + string @namespace = symbol.ContainingNamespace.ToString(); + string name = GetGenericName(symbol); + + string fullType = QualifiedName(@namespace, name); + var assembly = GetAssemblyForWinRTType(symbol); + if (assembly == null) + { + if (mapper.HasMappingForType(fullType)) + { + (@namespace, name, assembly, _, _) = mapper.GetMappedType(fullType).GetMapping(currentTypeDeclaration.Node); + Logger.Log("custom mapping " + fullType + " to " + QualifiedName(@namespace, name) + " from " + assembly); + } + else + { + assembly = symbol.ContainingAssembly.Name; + } + } + + return GetTypeReference(@namespace, name, assembly); + } + + private EntityHandle GetTypeSpecification(INamedTypeSymbol symbol) + { + if (symbol.IsGenericType) + { + Logger.Log("Adding TypeSpec for " + symbol.ToString()); + var typeSpecSignature = new BlobBuilder(); + var genericType = new BlobEncoder(typeSpecSignature) + .TypeSpecificationSignature() + .GenericInstantiation(GetTypeReference(symbol), symbol.TypeArguments.Length, false); + foreach (var typeArgument in symbol.TypeArguments) + { + EncodeSymbol(new Symbol(typeArgument), genericType.AddArgument()); + } + + return metadataBuilder.AddTypeSpecification(metadataBuilder.GetOrAddBlob(typeSpecSignature)); + } + else + { + return GetTypeReference(symbol); + } + } + + private void EncodeSymbol(Symbol symbol, SignatureTypeEncoder typeEncoder) + { + if (symbol.IsHandle()) + { + typeEncoder.Type(symbol.Handle, false); + } + else if (symbol.IsGeneric()) + { + if (symbol.IsArray) + { + typeEncoder.SZArray().GenericTypeParameter(symbol.GenericIndex); + } + else + { + typeEncoder.GenericTypeParameter(symbol.GenericIndex); + } + } + else if (symbol.IsArray) + { + EncodeSymbol(new Symbol(symbol.Type), typeEncoder.SZArray()); + } + else if (symbol.Type is IArrayTypeSymbol arrayType) + { + EncodeSymbol(new Symbol(arrayType.ElementType), typeEncoder.SZArray()); + } + else if (symbol.Type is INamedTypeSymbol namedType && namedType.TypeArguments.Length != 0) + { + var genericType = typeEncoder.GenericInstantiation(GetTypeReference(symbol.Type), namedType.TypeArguments.Length, false); + int parameterIndex = 0; + foreach (var typeArgument in namedType.TypeArguments) + { + if (namedType.IsUnboundGenericType) + { + genericType.AddArgument().GenericTypeParameter(parameterIndex); + } + else + { + EncodeSymbol(new Symbol(typeArgument), genericType.AddArgument()); + } + parameterIndex++; + } + } + else if (IsEncodableAsSpecialType(symbol.Type.SpecialType)) + { + EncodeSpecialType(symbol.Type.SpecialType, typeEncoder); + } + else + { + bool isValueType = symbol.Type.TypeKind == TypeKind.Enum || symbol.Type.TypeKind == TypeKind.Struct; + if (mapper.HasMappingForType(QualifiedName(symbol.Type))) + { + (_, _, _, _, isValueType) = mapper.GetMappedType(QualifiedName(symbol.Type)).GetMapping(currentTypeDeclaration.Node); + } + typeEncoder.Type(GetTypeReference(symbol.Type), isValueType); + } + } + + private void EncodeReturnType(Symbol symbol, ReturnTypeEncoder returnTypeEncoder) + { + if (symbol == null) + { + returnTypeEncoder.Void(); + } + else if (symbol.IsHandle() || symbol.IsGeneric() || !IsEncodableAsSpecialType(symbol.Type.SpecialType)) + { + EncodeSymbol(symbol, returnTypeEncoder.Type()); + } + else if (symbol.Type.SpecialType == SpecialType.System_Void) + { + returnTypeEncoder.Void(); + } + else + { + EncodeSpecialType(symbol.Type.SpecialType, returnTypeEncoder.Type()); + } + } + + private void EncodeParameters(Parameter[] parameters, ParametersEncoder parametersEncoder) + { + foreach (var parameter in parameters) + { + var parameterType = parameter.Type; + var parameterTypeEncoder = parametersEncoder.AddParameter(); + + if (!parameterType.IsHandle() && !parameterType.IsGeneric() && IsEncodableAsSpecialType(parameterType.Type.SpecialType)) + { + EncodeSpecialType(parameterType.Type.SpecialType, parameterTypeEncoder.Type(parameter.ByRef)); + } + else + { + EncodeSymbol(parameterType, parameterTypeEncoder.Type(parameter.ByRef)); + } + } + } + + public MethodDefinitionHandle AddMethodDefinition( + string name, + Parameter[] parameters, + Symbol returnSymbol, + bool isStatic, + bool isInterfaceParent, + bool isSpecialMethod = false, + bool isPublic = true, + bool isOverridable = false) + { + var methodSignature = new BlobBuilder(); + new BlobEncoder(methodSignature) + .MethodSignature( + SignatureCallingConvention.Default, + 0, + !isStatic) + .Parameters( + parameters.Length, + returnType => EncodeReturnType(returnSymbol, returnType), + parametersEncoder => EncodeParameters(parameters, parametersEncoder) + ); + + List parameterHandles = new List(); + for (int idx = 0; idx < parameters.Length; idx++) + { + parameterHandles.Add(metadataBuilder.AddParameter( + parameters[idx].Attributes, + metadataBuilder.GetOrAddString(parameters[idx].Name), + idx + 1)); + } + + var methodAttributes = + (isPublic ? MethodAttributes.Public : MethodAttributes.Private) | + MethodAttributes.HideBySig; + + var methodImplAttributes = MethodImplAttributes.Managed; + + if (isInterfaceParent) + { + methodAttributes |= MethodAttributes.Abstract; + } + else + { + methodImplAttributes |= MethodImplAttributes.Runtime; + } + + if (isSpecialMethod && string.CompareOrdinal(name, ".ctor") == 0) + { + methodAttributes |= MethodAttributes.RTSpecialName; + } + else if (isStatic) + { + methodAttributes |= MethodAttributes.Static; + } + else + { + methodAttributes |= + MethodAttributes.Virtual | + MethodAttributes.NewSlot; + + if (!isOverridable && !isInterfaceParent) + { + methodAttributes |= MethodAttributes.Final; + } + } + + if (isSpecialMethod) + { + methodAttributes |= MethodAttributes.SpecialName; + } + + var methodDefinitionHandle = metadataBuilder.AddMethodDefinition( + methodAttributes, + methodImplAttributes, + metadataBuilder.GetOrAddString(name), + metadataBuilder.GetOrAddBlob(methodSignature), + -1, + parameterHandles.Count == 0 ? + MetadataTokens.ParameterHandle(metadataBuilder.GetRowCount(TableIndex.Param) + 1) : + parameterHandles[0]); + return methodDefinitionHandle; + } + + public void AddFieldDeclaration(IFieldSymbol field, bool isEnum) + { + Logger.Log("defining field " + field.Name + " with type " + field.Type.ToString()); + + var fieldSignature = new BlobBuilder(); + var encoder = new BlobEncoder(fieldSignature); + EncodeSymbol(new Symbol(field.Type), encoder.FieldSignature()); + + var fieldAttributes = FieldAttributes.Public; + if (isEnum) + { + fieldAttributes |= + FieldAttributes.Static | + FieldAttributes.Literal | + FieldAttributes.HasDefault; + } + + var fieldDefinitionHandle = metadataBuilder.AddFieldDefinition( + fieldAttributes, + metadataBuilder.GetOrAddString(field.Name), + metadataBuilder.GetOrAddBlob(fieldSignature)); + currentTypeDeclaration.AddField(field, fieldDefinitionHandle); + + if (isEnum && field.HasConstantValue) + { + metadataBuilder.AddConstant(fieldDefinitionHandle, field.ConstantValue); + } + } + + public void AddPropertyDefinition( + string propertyName, + Symbol type, + ISymbol symbol, + bool isStatic, + bool hasSetMethod, + bool isInterfaceParent, + bool isPublic = true) + { + Logger.Log("defining property " + propertyName); + GetNamespaceAndTypename(propertyName, out var @namespace, out var typename); + + var propertySignature = new BlobBuilder(); + new BlobEncoder(propertySignature) + .PropertySignature(!isStatic) + .Parameters( + 0, + returnType => EncodeReturnType(type, returnType), + parameters => { } + ); + + var propertyDefinitonHandle = metadataBuilder.AddProperty( + PropertyAttributes.None, + metadataBuilder.GetOrAddString(propertyName), + metadataBuilder.GetOrAddBlob(propertySignature)); + currentTypeDeclaration.AddProperty(symbol, propertyDefinitonHandle); + + if (hasSetMethod) + { + string setMethodName = QualifiedName(@namespace, "put_" + typename); + var setMethod = AddMethodDefinition( + setMethodName, + new Parameter[] { new Parameter(type, "value", ParameterAttributes.In) }, + null, + !isInterfaceParent && isStatic, + isInterfaceParent, + true, + isPublic); + currentTypeDeclaration.AddMethod(symbol, setMethodName, setMethod); + + metadataBuilder.AddMethodSemantics( + propertyDefinitonHandle, + MethodSemanticsAttributes.Setter, + setMethod); + } + + string getMethodName = QualifiedName(@namespace, "get_" + typename); + var getMethod = AddMethodDefinition( + getMethodName, + new Parameter[0], + type, + !isInterfaceParent && isStatic, + isInterfaceParent, + true, + isPublic); + currentTypeDeclaration.AddMethod(symbol, getMethodName, getMethod); + + metadataBuilder.AddMethodSemantics( + propertyDefinitonHandle, + MethodSemanticsAttributes.Getter, + getMethod); + } + + public void AddPropertyDeclaration(IPropertySymbol property, bool isInterfaceParent) + { + AddPropertyDefinition( + property.Name, + new Symbol(property.Type), + property, + property.IsStatic, + property.SetMethod != null && + (property.SetMethod.DeclaredAccessibility == Accessibility.Public || + !property.SetMethod.ExplicitInterfaceImplementations.IsDefaultOrEmpty), + isInterfaceParent, + property.ExplicitInterfaceImplementations.IsDefaultOrEmpty); + } + + private TypeDefinitionHandle AddTypeDefinition( + TypeAttributes typeAttributes, + string @namespace, + string identifier, + EntityHandle baseType) + { + var fieldDefinitions = currentTypeDeclaration.GetFieldDefinitions(); + var methodDefinitions = currentTypeDeclaration.GetMethodDefinitions(); + + var typeDefinitionHandle = metadataBuilder.AddTypeDefinition( + typeAttributes, + metadataBuilder.GetOrAddString(@namespace), + metadataBuilder.GetOrAddString(identifier), + baseType, + fieldDefinitions.Count == 0 ? + MetadataTokens.FieldDefinitionHandle(metadataBuilder.GetRowCount(TableIndex.Field) + 1) : + fieldDefinitions[0], + methodDefinitions.Count == 0 ? + MetadataTokens.MethodDefinitionHandle(metadataBuilder.GetRowCount(TableIndex.MethodDef) + 1) : + methodDefinitions[0] + ); + + var propertyDefinitions = currentTypeDeclaration.GetPropertyDefinitions(); + if (propertyDefinitions.Count != 0) + { + metadataBuilder.AddPropertyMap( + typeDefinitionHandle, + propertyDefinitions[0]); + } + + var eventDefinitions = currentTypeDeclaration.GetEventDefinitions(); + if (eventDefinitions.Count != 0) + { + metadataBuilder.AddEventMap( + typeDefinitionHandle, + eventDefinitions[0]); + } + + return typeDefinitionHandle; + } + + private void ProcessCustomMappedInterfaces(INamedTypeSymbol classSymbol) + { + Logger.Log("writing custom mapped interfaces for " + QualifiedName(classSymbol)); + Dictionary isPublicImplementation = new(SymbolEqualityComparer.Default); + + // Mark custom mapped interface members for removal later. + // Note we want to also mark members from interfaces without mappings. + foreach (var implementedInterface in GetInterfaces(classSymbol, true). + Where(symbol => mapper.HasMappingForType(QualifiedName(symbol)) || + ImplementedInterfacesWithoutMapping.Contains(QualifiedName(symbol)))) + { + bool isPubliclyImplemented = false; + Logger.Log("custom mapped interface: " + QualifiedName(implementedInterface, true)); + foreach (var interfaceMember in implementedInterface.GetMembers()) + { + var classMember = classSymbol.FindImplementationForInterfaceMember(interfaceMember); + currentTypeDeclaration.CustomMappedSymbols.Add(classMember); + + // For custom mapped interfaces, we don't have 1 to 1 mapping of members between the mapped from + // and mapped to interface and due to that we need to decide if the mapped inteface as a whole + // is public or not (explicitly implemented). Due to that, as long as one member is not + // explicitly implemented (i.e accessible via the class), we treat the entire mapped interface + // also as accessible via the class. + isPubliclyImplemented |= (classMember.DeclaredAccessibility == Accessibility.Public); + } + isPublicImplementation[implementedInterface] = isPubliclyImplemented; + } + + foreach (var implementedInterface in GetInterfaces(classSymbol) + .Where(symbol => mapper.HasMappingForType(QualifiedName(symbol)))) + { + WriteCustomMappedTypeMembers(implementedInterface, true, isPublicImplementation[implementedInterface]); + } + } + + INamedTypeSymbol GetTypeByMetadataName(string metadataName) + { + var namedType = Model.Compilation.GetTypeByMetadataName(metadataName); + if (namedType != null) + { + return namedType; + } + + // Model.Compilation.GetTypeByMetadataName doesn't return a type if there is multiple references with the same type. + // So as a fallback, go through all the references and check each one filtering to public ones. + var types = Model.Compilation.References + .Select(Model.Compilation.GetAssemblyOrModuleSymbol) + .OfType() + .Select(assemblySymbol => assemblySymbol.GetTypeByMetadataName(metadataName)) + .Where(type => type != null && type.DeclaredAccessibility == Accessibility.Public); + return types.FirstOrDefault(); + } + + // Convert the entire type name including the generic types to WinMD format. + private string GetMappedQualifiedTypeName(ITypeSymbol symbol) + { + string qualifiedName = QualifiedName(symbol); + if (mapper.HasMappingForType(qualifiedName)) + { + var (@namespace, mappedTypeName, _, _, _) = mapper.GetMappedType(qualifiedName).GetMapping(currentTypeDeclaration.Node); + qualifiedName = QualifiedName(@namespace, mappedTypeName); + if (symbol is INamedTypeSymbol namedType && namedType.TypeArguments.Length > 0) + { + return string.Format("{0}<{1}>", qualifiedName, string.Join(", ", namedType.TypeArguments.Select(type => GetMappedQualifiedTypeName(type)))); + } + } + else if ((string.CompareOrdinal(symbol.ContainingNamespace.ToString(), "System") == 0 && + symbol.IsValueType) || string.CompareOrdinal(qualifiedName, "System.String") == 0) + { + // WinRT fundamental types + return symbol.Name; + } + + return qualifiedName; + } + + private void WriteCustomMappedTypeMembers(INamedTypeSymbol symbol, bool isDefinition, bool isPublic = true) + { + var (_, mappedTypeName, _, _, _) = mapper.GetMappedType(QualifiedName(symbol)).GetMapping(currentTypeDeclaration.Node); + string qualifiedName = GetMappedQualifiedTypeName(symbol); + + Logger.Log("writing custom mapped type members for " + mappedTypeName + " public: " + isPublic + " qualified name: " + qualifiedName); + void AddMethod(string name, Parameter[] parameters, Symbol returnType) + { + parameters ??= new Parameter[0]; + if (isDefinition) + { + var methodName = isPublic ? name : QualifiedName(qualifiedName, name); + var methodDefinitionHandle = AddMethodDefinition(methodName, parameters, returnType, false, false, false, isPublic); + currentTypeDeclaration.AddMethod(symbol, methodName, methodDefinitionHandle); + } + else + { + var memberReferenceHandle = AddMethodReference(name, parameters, returnType, symbol, false); + currentTypeDeclaration.AddMethodReference(symbol, memberReferenceHandle); + } + } + + void AddProperty(string name, Symbol type, bool setProperty) + { + if (isDefinition) + { + var propertyName = isPublic ? name : QualifiedName(qualifiedName, name); + AddPropertyDefinition(propertyName, type, symbol, false, setProperty, false, isPublic); + } + else + { + AddPropertyReference(name, type, symbol, symbol, setProperty); + } + } + + void AddEvent(string name, Symbol eventType) + { + if (isDefinition) + { + var eventName = isPublic ? name : QualifiedName(qualifiedName, name); + AddEventDeclaration(eventName, eventType.Type, symbol, false, false, isPublic); + } + else + { + AddEventReference(name, eventType.Type, symbol, symbol); + } + } + + Symbol GetType(string type, bool isGeneric = false, int genericIndex = -1, bool isArray = false, ITypeSymbol[] genericTypes = null) + { + if (string.IsNullOrEmpty(type) && isGeneric) + { + return isDefinition ? new Symbol(symbol.TypeArguments[genericIndex], isArray) : new Symbol(genericIndex, isArray); + } + + var namedTypeSymbol = GetTypeByMetadataName(type); + if (!isGeneric) + { + return new Symbol(namedTypeSymbol, isArray); + } + + if (isDefinition) + { + var typeArguments = genericTypes ?? ((genericIndex == -1) ? + symbol.TypeArguments.ToArray() : new ITypeSymbol[] { symbol.TypeArguments[genericIndex] }); + return new Symbol(namedTypeSymbol.Construct(typeArguments), isArray); + } + else + { + return new Symbol(namedTypeSymbol.ConstructUnboundGenericType(), isArray); + } + } + + if (string.CompareOrdinal(mappedTypeName, "IClosable") == 0) + { + AddMethod("Close", null, null); + } + else if (string.CompareOrdinal(mappedTypeName, "IIterable`1") == 0) + { + AddMethod("First", null, GetType("System.Collections.Generic.IEnumerator`1", true)); + } + else if (string.CompareOrdinal(mappedTypeName, "IMap`2") == 0) + { + AddMethod("Clear", null, null); + AddMethod("GetView", null, GetType("System.Collections.Generic.IReadOnlyDictionary`2", true)); + AddMethod( + "HasKey", + new[] { new Parameter(GetType(null, true, 0), "key", ParameterAttributes.In) }, + GetType("System.Boolean") + ); + AddMethod( + "Insert", + new[] { + new Parameter(GetType(null, true, 0), "key", ParameterAttributes.In), + new Parameter(GetType(null, true, 1), "value", ParameterAttributes.In) + }, + GetType("System.Boolean") + ); + AddMethod( + "Lookup", + new[] { new Parameter(GetType(null, true, 0), "key", ParameterAttributes.In) }, + GetType(null, true, 1) + ); + AddMethod( + "Remove", + new[] { new Parameter(GetType(null, true, 0), "key", ParameterAttributes.In) }, + null + ); + AddProperty("Size", GetType("System.UInt32"), false); + } + else if (string.CompareOrdinal(mappedTypeName, "IMapView`2") == 0) + { + AddMethod( + "HasKey", + new[] { new Parameter(GetType(null, true, 0), "key", ParameterAttributes.In) }, + GetType("System.Boolean") + ); + AddMethod( + "Lookup", + new[] { new Parameter(GetType(null, true, 0), "key", ParameterAttributes.In) }, + GetType(null, true, 1) + ); + AddMethod( + "Split", + new[] { + new Parameter(GetType("System.Collections.Generic.IReadOnlyDictionary`2", true), "first", ParameterAttributes.Out), + new Parameter(GetType("System.Collections.Generic.IReadOnlyDictionary`2", true), "second", ParameterAttributes.Out) + }, + null + ); + AddProperty("Size", GetType("System.UInt32"), false); + } + else if (string.CompareOrdinal(mappedTypeName, "IIterator`1") == 0) + { + // make array + AddMethod( + "GetMany", + new[] { new Parameter(GetType(null, true, 0, true), "items", ParameterAttributes.In, false) }, + GetType("System.UInt32") + ); + AddMethod( + "MoveNext", + null, + GetType("System.Boolean") + ); + AddProperty("Current", GetType(null, true, 0), false); + AddProperty("HasCurrent", GetType("System.Boolean"), false); + } + else if (string.CompareOrdinal(mappedTypeName, "IVector`1") == 0) + { + AddMethod( + "Append", + new[] { new Parameter(GetType(null, true, 0), "value", ParameterAttributes.In) }, + null + ); + AddMethod("Clear", null, null); + AddMethod( + "GetAt", + new[] { new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In) }, + GetType(null, true, 0) + ); + AddMethod( + "GetMany", + new[] { + new Parameter(GetType("System.UInt32"), "startIndex", ParameterAttributes.In), + new Parameter(GetType(null, true, 0, true), "items", ParameterAttributes.In) + }, + GetType("System.UInt32") + ); + AddMethod("GetView", null, GetType("System.Collections.Generic.IReadOnlyList`1", true)); + AddMethod( + "IndexOf", + new[] { + new Parameter(GetType(null, true, 0), "value", ParameterAttributes.In), + new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.Out) + }, + GetType("System.Boolean") + ); + AddMethod( + "InsertAt", + new[] { + new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In), + new Parameter(GetType(null, true, 0), "value", ParameterAttributes.In), + }, + null + ); + AddMethod( + "RemoveAt", + new[] { new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In) }, + null + ); + AddMethod("RemoveAtEnd", null, null); + AddMethod( + "ReplaceAll", + new[] { + new Parameter(GetType(null, true, 0, true), "items", ParameterAttributes.In) + }, + null + ); + AddMethod( + "SetAt", + new[] { + new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In), + new Parameter(GetType(null, true, 0), "value", ParameterAttributes.In), + }, + null + ); + AddProperty("Size", GetType("System.UInt32"), false); + } + else if (string.CompareOrdinal(mappedTypeName, "IVectorView`1") == 0) + { + AddMethod( + "GetAt", + new[] { new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In) }, + GetType(null, true, 0) + ); + AddMethod( + "GetMany", + new[] { + new Parameter(GetType("System.UInt32"), "startIndex", ParameterAttributes.In), + new Parameter(GetType(null, true, 0, true), "items", ParameterAttributes.In) + }, + GetType("System.UInt32") + ); + AddMethod( + "IndexOf", + new[] { + new Parameter(GetType(null, true, 0), "value", ParameterAttributes.In), + new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.Out) + }, + GetType("System.Boolean") + ); + AddProperty("Size", GetType("System.UInt32"), false); + } + else if (string.CompareOrdinal(mappedTypeName, "IXamlServiceProvider") == 0) + { + AddMethod( + "GetService", + new[] { new Parameter(GetType("System.Type"), "type", ParameterAttributes.In) }, + GetType("System.Object") + ); + } + else if (string.CompareOrdinal(mappedTypeName, "INotifyDataErrorInfo") == 0) + { + AddProperty("HasErrors", GetType("System.Boolean"), false); + AddEvent( + "ErrorsChanged", + GetType("System.EventHandler`1", true, -1, false, new[] { GetType("System.ComponentModel.DataErrorsChangedEventArgs").Type })); + AddMethod( + "GetErrors", + new[] { new Parameter(GetType("System.String"), "propertyName", ParameterAttributes.In) }, + GetType("System.Collections.Generic.IEnumerable`1", true, -1, false, new[] { GetType("System.Object").Type }) + ); + } + else if (string.CompareOrdinal(mappedTypeName, "INotifyPropertyChanged") == 0) + { + AddEvent("PropertyChanged", GetType("System.ComponentModel.PropertyChangedEventHandler")); + } + else if (string.CompareOrdinal(mappedTypeName, "ICommand") == 0) + { + AddEvent( + "CanExecuteChanged", + GetType("System.EventHandler`1", true, -1, false, new[] { GetType("System.Object").Type })); + AddMethod( + "CanExecute", + new[] { new Parameter(GetType("System.Object"), "parameter", ParameterAttributes.In) }, + GetType("System.Boolean") + ); + AddMethod( + "Execute", + new[] { new Parameter(GetType("System.Object"), "parameter", ParameterAttributes.In) }, + null + ); + } + else if (string.CompareOrdinal(mappedTypeName, "IBindableIterable") == 0) + { + AddMethod("First", null, GetType("Microsoft.UI.Xaml.Interop.IBindableIterator")); + } + else if (string.CompareOrdinal(mappedTypeName, "IBindableVector") == 0) + { + AddMethod( + "Append", + new[] { new Parameter(GetType("System.Object"), "value", ParameterAttributes.In) }, + null + ); + AddMethod("Clear", null, null); + AddMethod( + "GetAt", + new[] { new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In) }, + GetType("System.Object") + ); + AddMethod("GetView", null, GetType("Microsoft.UI.Xaml.Interop.IBindableVectorView")); + AddMethod( + "IndexOf", + new[] { + new Parameter(GetType("System.Object"), "value", ParameterAttributes.In), + new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.Out) + }, + GetType("System.Boolean") + ); + AddMethod( + "InsertAt", + new[] { + new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In), + new Parameter(GetType("System.Object"), "value", ParameterAttributes.In), + }, + null + ); + AddMethod( + "RemoveAt", + new[] { new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In) }, + null + ); + AddMethod("RemoveAtEnd", null, null); + AddMethod( + "SetAt", + new[] { + new Parameter(GetType("System.UInt32"), "index", ParameterAttributes.In), + new Parameter(GetType("System.Object"), "value", ParameterAttributes.In), + }, + null + ); + AddProperty("Size", GetType("System.UInt32"), false); + } + else if (string.CompareOrdinal(mappedTypeName, "INotifyCollectionChanged") == 0) + { + AddEvent("CollectionChanged", GetType("System.Collections.Specialized.NotifyCollectionChangedEventHandler")); + } + } + + private IEnumerable GetInterfaces(INamedTypeSymbol symbol, bool includeInterfacesWithoutMappings = false) + { + HashSet interfaces = new(SymbolEqualityComparer.Default); + + // Gather all interfaces that are publicly accessible. We specifically need to exclude interfaces + // that are not public, as eg. those might be used for additional cloaked WinRT/COM interfaces. + // Ignoring them here makes sure that they're not processed to be part of the .winmd file. + void GatherPubliclyAccessibleInterfaces(ITypeSymbol symbol) + { + foreach (var @interface in symbol.Interfaces) + { + if (@interface.IsPubliclyAccessible()) + { + _ = interfaces.Add(@interface); + } + + // We're not using AllInterfaces on purpose: we only want to gather all interfaces but not + // from the base type. That's handled below to skip types that are already WinRT projections. + foreach (var @interface2 in @interface.AllInterfaces) + { + if (@interface2.IsPubliclyAccessible()) + { + _ = interfaces.Add(@interface2); + } + } + } + } + + GatherPubliclyAccessibleInterfaces(symbol); + + var baseType = symbol.BaseType; + while (baseType != null && !GeneratorHelper.IsWinRTType(baseType, mapper)) + { + GatherPubliclyAccessibleInterfaces(baseType); + + baseType = baseType.BaseType; + } + + // If the generic enumerable is implemented, don't implement the non generic one to prevent issues + // with the interface members being implemented multiple times. + if (!includeInterfacesWithoutMappings && + interfaces.Any(@interface => string.CompareOrdinal(QualifiedName(@interface), "System.Collections.Generic.IEnumerable`1") == 0)) + { + interfaces.Remove(GetTypeByMetadataName("System.Collections.IEnumerable")); + } + + return interfaces.Where(@interface => + includeInterfacesWithoutMappings || + !ImplementedInterfacesWithoutMapping.Contains(QualifiedName(@interface))) + .OrderBy(implementedInterface => implementedInterface.ToString()); + } + + public override void VisitInterfaceDeclaration(InterfaceDeclarationSyntax node) + { + AddComponentType(Model.GetDeclaredSymbol(node), () => base.VisitInterfaceDeclaration(node)); + } + + public override void VisitClassDeclaration(ClassDeclarationSyntax node) + { + AddComponentType(Model.GetDeclaredSymbol(node), () => base.VisitClassDeclaration(node)); + } + + public override void VisitStructDeclaration(StructDeclarationSyntax node) + { + AddComponentType(Model.GetDeclaredSymbol(node), () => base.VisitStructDeclaration(node)); + } + + private void EncodeTypedConstant(TypedConstant constant, LiteralEncoder encoder) + { + Logger.Log("typed constant kind: " + constant.Kind); + Logger.Log("typed constant type: " + constant.Type); + Logger.Log("typed constant value: " + constant.Value); + + switch (constant.Kind) + { + case TypedConstantKind.Primitive: + encoder.Scalar().Constant(constant.Value); + break; + case TypedConstantKind.Enum: + encoder.Scalar().Constant(constant.Value); + + // This looks more correct, but the code above matches what the C# compiler produces. + // Eg. win32metadata does the same (see 'MetadataUtils.EncodeHelpers' in https://github.com/microsoft/win32metadata). + // encoder.TaggedScalar( + // type => type.Enum(constant.Type.ToString()), + // scalar => scalar.Constant(constant.Value) + // ); + + break; + case TypedConstantKind.Type: + encoder.Scalar().SystemType(constant.Value.ToString()); + break; + case TypedConstantKind.Array: + { + LiteralsEncoder arrayEncoder = encoder.Vector().Count(constant.Values.Length); + foreach (var arrayConstant in constant.Values) + { + EncodeTypedConstant(arrayConstant, arrayEncoder.AddLiteral()); + } + break; + } + } + } + + private void EncodeFixedArguments(IList arguments, FixedArgumentsEncoder argumentsEncoder) + { + foreach (var argument in arguments) + { + EncodeTypedConstant(argument, argumentsEncoder.AddArgument()); + } + } + + private void EncodeCustomElementType(ITypeSymbol type, CustomAttributeElementTypeEncoder typeEncoder) + { + switch (type.SpecialType) + { + case SpecialType.System_Boolean: + typeEncoder.Boolean(); + break; + case SpecialType.System_Byte: + typeEncoder.Byte(); + break; + case SpecialType.System_Int16: + typeEncoder.Int16(); + break; + case SpecialType.System_Int32: + typeEncoder.Int32(); + break; + case SpecialType.System_Int64: + typeEncoder.Int64(); + break; + case SpecialType.System_UInt16: + typeEncoder.UInt16(); + break; + case SpecialType.System_UInt32: + typeEncoder.UInt32(); + break; + case SpecialType.System_UInt64: + typeEncoder.UInt64(); + break; + case SpecialType.System_Single: + typeEncoder.Single(); + break; + case SpecialType.System_Double: + typeEncoder.Double(); + break; + case SpecialType.System_Char: + typeEncoder.Char(); + break; + case SpecialType.System_String: + typeEncoder.String(); + break; + case SpecialType.System_Enum: + typeEncoder.Enum(type.ToString()); + break; + case SpecialType.System_SByte: + typeEncoder.SByte(); + break; + default: + Logger.Log("TODO special type: " + type.SpecialType); + break; + } + } + + private void EncodeNamedArgumentType(ITypeSymbol type, NamedArgumentTypeEncoder encoder) + { + Logger.Log("encoding named type"); + if (type.SpecialType == SpecialType.System_Object) + { + encoder.Object(); + } + else if (type.SpecialType == SpecialType.System_Array) + { + // TODO array type encoder + encoder.SZArray(); + } + else + { + EncodeCustomElementType(type, encoder.ScalarType()); + } + } + + private ISymbol GetMember(INamedTypeSymbol type, string member) + { + var foundMembers = type.GetMembers(member); + var baseType = type.BaseType; + while (foundMembers.Count() == 0 && baseType != null) + { + foundMembers = baseType.GetMembers(member); + baseType = baseType.BaseType; + } + + Logger.Log("# members found: " + foundMembers.Count()); + var foundMember = foundMembers.First(); + Logger.Log("found member: " + foundMember); + return foundMember; + } + + private void EncodeNamedArguments( + INamedTypeSymbol attributeType, + IList> namedArguments, + CustomAttributeNamedArgumentsEncoder argumentsEncoder) + { + var encoder = argumentsEncoder.Count(namedArguments.Count); + foreach (var argument in namedArguments) + { + Logger.Log("named argument: " + argument.Key); + Logger.Log("value " + argument.Value); + + ITypeSymbol argumentType = null; + var attributeClassMember = GetMember(attributeType, argument.Key); + if (attributeClassMember is IFieldSymbol field) + { + argumentType = field.Type; + } + else if (attributeClassMember is IPropertySymbol property) + { + argumentType = property.Type; + } + else + { + Logger.Log("unexpected member: " + attributeClassMember.Name + " " + attributeClassMember.GetType()); + throw new InvalidOperationException(); + } + + encoder.AddArgument( + attributeClassMember is IFieldSymbol, + type => EncodeNamedArgumentType(argumentType, type), + name => name.Name(argument.Key), + literal => EncodeTypedConstant(argument.Value, literal) + ); + } + } + + private void EncodeFixedArguments(IList primitiveArguments, FixedArgumentsEncoder argumentsEncoder) + { + foreach (var argument in primitiveArguments) + { + var encoder = argumentsEncoder.AddArgument().Scalar(); + if (argument is string type) + { + encoder.SystemType(type); + } + else if (argument is INamedTypeSymbol namedTypeSymbol) + { + var typeEntity = GetTypeReference(namedTypeSymbol); + encoder.Builder.WriteReference(CodedIndex.TypeDefOrRef(typeEntity), false); + } + else + { + encoder.Constant(argument); + } + } + } + + private void AddDefaultVersionAttribute(EntityHandle parentHandle, int version = -1) + { + if (version == -1) + { + version = Version.Parse(this.version).Major; + } + + List types = new List + { + Model.Compilation.GetTypeByMetadataName("System.UInt32") + }; + + List arguments = new List + { + (UInt32) version + }; + + AddCustomAttributes("Windows.Foundation.Metadata.VersionAttribute", types, arguments, parentHandle); + } + + private void AddActivatableAttribute(EntityHandle parentHandle, UInt32 version, string factoryInterface) + { + List types = new List(2); + List arguments = new List(2); + + if (factoryInterface != null) + { + types.Add(Model.Compilation.GetTypeByMetadataName("System.Type")); + arguments.Add(factoryInterface); + } + types.Add(Model.Compilation.GetTypeByMetadataName("System.UInt32")); + arguments.Add(version); + + AddCustomAttributes("Windows.Foundation.Metadata.ActivatableAttribute", types, arguments, parentHandle); + } + + private void AddExclusiveToAttribute(EntityHandle interfaceHandle, string className) + { + List types = new List + { + Model.Compilation.GetTypeByMetadataName("System.Type") + }; + + List arguments = new List + { + className + }; + + AddCustomAttributes("Windows.Foundation.Metadata.ExclusiveToAttribute", types, arguments, interfaceHandle); + } + + private void AddStaticAttribute(EntityHandle parentHandle, UInt32 version, string staticInterface) + { + List types = new List + { + Model.Compilation.GetTypeByMetadataName("System.Type"), + Model.Compilation.GetTypeByMetadataName("System.UInt32") + }; + + List arguments = new List + { + staticInterface, + version + }; + + AddCustomAttributes("Windows.Foundation.Metadata.StaticAttribute", types, arguments, parentHandle); + } + + private void AddDefaultInterfaceImplAttribute(EntityHandle interfaceImplHandle) + { + AddCustomAttributes("Windows.Foundation.Metadata.DefaultAttribute", Array.Empty(), Array.Empty(), interfaceImplHandle); + } + + private void AddOverloadAttribute(EntityHandle methodHandle, string methodName) + { + List types = new List + { + Model.Compilation.GetTypeByMetadataName("System.String") + }; + + List arguments = new List + { + methodName + }; + + AddCustomAttributes("Windows.Foundation.Metadata.OverloadAttribute", types, arguments, methodHandle); + } + + private void AddOverloadAttributeForInterfaceMethods(TypeDeclaration interfaceTypeDeclaration) + { + // Generate unique names for any overloaded methods + foreach (var methodName in interfaceTypeDeclaration.MethodsByName.Where(symbol => symbol.Value.Count > 1)) + { + var methodSymbols = methodName.Value.Where(symbol => symbol is IMethodSymbol); + // Other members that are not methods such as properties and events can generate reserved methods + // which for the purposes of overloading are considered to be the non overloaded version. If there is no + // such function, then we consider the first encountered method to be the non overloaded version. + var skipFirstMethod = methodName.Value.Count == methodSymbols.Count(); + var lastSuffix = 1; + foreach (var method in methodSymbols) + { + if (skipFirstMethod) + { + skipFirstMethod = false; + continue; + } + + string overloadedMethodName = methodName.Key + (++lastSuffix); + while (interfaceTypeDeclaration.MethodsByName.ContainsKey(overloadedMethodName)) + { + overloadedMethodName = methodName.Key + (++lastSuffix); + } + + Logger.Log("Overloading " + methodName.Key + " with " + overloadedMethodName); + AddOverloadAttribute(interfaceTypeDeclaration.MethodDefinitions[method].First(), overloadedMethodName); + interfaceTypeDeclaration.AddMethodOverload(method, overloadedMethodName); + } + } + } + + private void AddGuidAttribute(EntityHandle parentHandle, Guid guid) + { + var uint32Type = Model.Compilation.GetTypeByMetadataName("System.UInt32"); + var uint16Type = Model.Compilation.GetTypeByMetadataName("System.UInt16"); + var byteType = Model.Compilation.GetTypeByMetadataName("System.Byte"); + List types = new List + { + uint32Type, + uint16Type, + uint16Type, + byteType, + byteType, + byteType, + byteType, + byteType, + byteType, + byteType, + byteType + }; + + var byteArray = guid.ToByteArray(); + List arguments = new List + { + BitConverter.ToUInt32(byteArray, 0), + BitConverter.ToUInt16(byteArray, 4), + BitConverter.ToUInt16(byteArray, 6), + byteArray[8], + byteArray[9], + byteArray[10], + byteArray[11], + byteArray[12], + byteArray[13], + byteArray[14], + byteArray[15] + }; + + AddCustomAttributes("Windows.Foundation.Metadata.GuidAttribute", types, arguments, parentHandle); + } + + private void AddGuidAttribute(EntityHandle parentHandle, string name) + { + Guid guid; + // CodeQL [SM02196] WinRT uses UUID v5 SHA1 to generate Guids for parameterized types. Not used for authentication and must not change. + using (SHA1 sha = new SHA1CryptoServiceProvider()) + { + var hash = sha.ComputeHash(Encoding.UTF8.GetBytes(name)); + guid = Helper.EncodeGuid(hash); + } + + AddGuidAttribute(parentHandle, guid); + } + + private void AddGuidAttribute(EntityHandle parentHandle, ISymbol symbol) + { + if (symbol.TryGetAttributeWithType(Model.Compilation.GetTypeByMetadataName("System.Runtime.InteropServices.GuidAttribute"), out AttributeData guidAttribute) + && guidAttribute is { ConstructorArguments.IsDefaultOrEmpty: false } + && guidAttribute.ConstructorArguments[0].Value is string guidString + && Guid.TryParse(guidString, out Guid guid)) + { + AddGuidAttribute(parentHandle, guid); + } + else + { + AddGuidAttribute(parentHandle, symbol.ToString()); + } + } + + private void RecordWinRTRuntimeClassNameAttribute(ISymbol symbol) + { + string qualifiedName = QualifiedName(symbol); + if (symbol.TryGetAttributeWithType(Model.Compilation.GetTypeByMetadataName("WinRT.WinRTRuntimeClassNameAttribute"), out AttributeData attribute) + && attribute is { ConstructorArguments.IsDefaultOrEmpty: false } + && attribute.ConstructorArguments[0].Value is string runtimeClassName + && !overridedRuntimeClassNameMappings.ContainsKey(qualifiedName)) + { + overridedRuntimeClassNameMappings[qualifiedName] = runtimeClassName; + } + } + + private void AddCustomAttributes( + string attributeTypeName, + IList primitiveTypes, + IList primitiveValues, + EntityHandle parentHandle) + { + var attributeType = Model.Compilation.GetTypeByMetadataName(attributeTypeName); + Logger.Log("attribute type found " + attributeType); + if (!typeDefinitionMapping.ContainsKey(attributeTypeName)) + { + // Even if the attribute is an external non WinRT type, treat it as a projected type. + Logger.Log("adding attribute type"); + AddType(attributeType, true); + } + + Logger.Log("# constructor found: " + attributeType.Constructors.Length); + var matchingConstructor = attributeType.Constructors.Where(constructor => + constructor.Parameters.Length == primitiveValues.Count && + constructor.Parameters.Select(param => (param.Type is IErrorTypeSymbol) ? + Model.Compilation.GetTypeByMetadataName(param.Type.ToDisplayString()) : param.Type) + .SequenceEqual(primitiveTypes, SymbolEqualityComparer.Default)); + + Logger.Log("# matching constructor found: " + matchingConstructor.Count()); + Logger.Log("matching constructor found: " + matchingConstructor.First()); + + var constructorReference = typeDefinitionMapping[attributeTypeName].MethodReferences[matchingConstructor.First()]; + Logger.Log("found constructor handle: " + constructorReference.Count); + + var attributeSignature = new BlobBuilder(); + new BlobEncoder(attributeSignature) + .CustomAttributeSignature( + fixedArguments => EncodeFixedArguments(primitiveValues, fixedArguments), + namedArguments => namedArguments.Count(0) + ); + + metadataBuilder.AddCustomAttribute( + parentHandle, + constructorReference.First(), + metadataBuilder.GetOrAddBlob(attributeSignature)); + } + + private void AddCustomAttributes(IEnumerable attributes, EntityHandle parentHandle) + { + foreach (var attribute in attributes) + { + var attributeType = attribute.AttributeClass; + if (attributeType.DeclaredAccessibility != Accessibility.Public) + { + continue; + } + + if (attributeType.ToString() == "System.Runtime.InteropServices.GuidAttribute") + { + continue; + } + + // Skip the [GeneratedBindableCustomProperty] attribute. It is valid to add this on types in WinRT + // components (if they need to be exposed and implement ICustomPropertyProvider), but the attribute + // should never show up in the .winmd file (it would also cause build errors in the projections). + if (attributeType.ToString() == "WinRT.GeneratedBindableCustomPropertyAttribute") + { + continue; + } + + Logger.Log("attribute: " + attribute); + Logger.Log("attribute type: " + attributeType); + Logger.Log("attribute constructor: " + attribute.AttributeConstructor); + Logger.Log("atttribute # constructor arguments: " + attribute.ConstructorArguments.Length); + Logger.Log("atttribute # named arguments: " + attribute.NamedArguments.Length); + + if (!typeDefinitionMapping.ContainsKey(attributeType.ToString())) + { + // Even if the attribute is an external non WinRT type, treat it as a projected type. + AddType(attributeType, true); + } + + var constructorReference = typeDefinitionMapping[attributeType.ToString()].MethodReferences[attribute.AttributeConstructor]; + Logger.Log("found # constructors: " + constructorReference.Count); + + var attributeSignature = new BlobBuilder(); + new BlobEncoder(attributeSignature) + .CustomAttributeSignature( + fixedArguments => EncodeFixedArguments(attribute.ConstructorArguments, fixedArguments), + namedArguments => EncodeNamedArguments(attributeType, attribute.NamedArguments, namedArguments) + ); + + metadataBuilder.AddCustomAttribute( + parentHandle, + constructorReference.First(), + metadataBuilder.GetOrAddBlob(attributeSignature)); + } + } + + public override void VisitEnumDeclaration(EnumDeclarationSyntax node) + { + var symbol = Model.GetDeclaredSymbol(node); + + void processEnumDeclaration() + { + var enumTypeFieldAttributes = + FieldAttributes.Private | + FieldAttributes.SpecialName | + FieldAttributes.RTSpecialName; + + var enumTypeSymbol = symbol.EnumUnderlyingType; + var fieldSignature = new BlobBuilder(); + var encoder = new BlobEncoder(fieldSignature); + // TODO: special type enforcement for 64 bit + EncodeSpecialType(enumTypeSymbol.SpecialType, encoder.FieldSignature()); + + var fieldDefinitionHandle = metadataBuilder.AddFieldDefinition( + enumTypeFieldAttributes, + metadataBuilder.GetOrAddString("value__"), + metadataBuilder.GetOrAddBlob(fieldSignature)); + currentTypeDeclaration.AddField(symbol, fieldDefinitionHandle); + + base.VisitEnumDeclaration(node); + } + + AddComponentType(symbol, processEnumDeclaration); + } + + public override void VisitDelegateDeclaration(DelegateDeclarationSyntax node) + { + var symbol = Model.GetDeclaredSymbol(node); + if (!IsPublic(symbol)) + { + return; + } + + Logger.Log("defining delegate " + symbol.Name); + currentTypeDeclaration = new TypeDeclaration(symbol, true); + + base.VisitDelegateDeclaration(node); + CheckAndMarkSymbolForAttributes(symbol); + + var objType = Model.Compilation.GetTypeByMetadataName(typeof(object).FullName); + var nativeIntType = Model.Compilation.GetTypeByMetadataName(typeof(IntPtr).FullName); + + currentTypeDeclaration.AddMethod( + symbol, + ".ctor", + AddMethodDefinition( + ".ctor", + new Parameter[] { + new Parameter(objType, "object", ParameterAttributes.None), + new Parameter(nativeIntType, "method", ParameterAttributes.None) + }, + null, + false, + false, + true, + false, + true + ) + ); + + Parameter[] parameters = Parameter.GetParameters(node.ParameterList, Model); + currentTypeDeclaration.AddMethod( + symbol, + "Invoke", + AddMethodDefinition( + "Invoke", + parameters, + new Symbol(symbol.DelegateInvokeMethod.ReturnType), + false, + false, + true, + true, + true + ) + ); + + TypeAttributes typeAttributes = + TypeAttributes.Public | + TypeAttributes.WindowsRuntime | + TypeAttributes.AutoLayout | + TypeAttributes.AnsiClass | + TypeAttributes.Sealed; + + var typeDefinitionHandle = AddTypeDefinition( + typeAttributes, + symbol.ContainingNamespace.ToString(), + symbol.Name, + GetTypeReference("System", "MulticastDelegate", "mscorlib")); + currentTypeDeclaration.Handle = typeDefinitionHandle; + typeDefinitionMapping[QualifiedName(symbol, true)] = currentTypeDeclaration; + + AddGuidAttribute(typeDefinitionHandle, symbol); + } + + public void AddEventDeclaration(string eventName, ITypeSymbol eventType, ISymbol symbol, bool isStatic, bool isInterfaceParent, bool isPublic = true) + { + Logger.Log("defining event " + eventName + " with type " + eventType.ToString()); + + GetNamespaceAndTypename(eventName, out var @namespace, out var typename); + + var delegateSymbolType = eventType as INamedTypeSymbol; + EntityHandle typeReferenceHandle = GetTypeSpecification(delegateSymbolType); + EntityHandle eventRegistrationTokenTypeHandle = GetTypeReference("Windows.Foundation", "EventRegistrationToken", "Windows.Foundation.FoundationContract"); + Symbol eventRegistrationToken = new Symbol(eventRegistrationTokenTypeHandle); + + var eventDefinitionHandle = metadataBuilder.AddEvent( + EventAttributes.None, + metadataBuilder.GetOrAddString(eventName), + typeReferenceHandle); + currentTypeDeclaration.AddEvent(symbol, eventDefinitionHandle); + + string addMethodName = QualifiedName(@namespace, "add_" + typename); + var addMethod = AddMethodDefinition( + addMethodName, + new Parameter[] { new Parameter(delegateSymbolType, "handler", ParameterAttributes.In) }, + eventRegistrationToken, + !isInterfaceParent && isStatic, + isInterfaceParent, + true, + isPublic); + currentTypeDeclaration.AddMethod(symbol, addMethodName, addMethod); + + metadataBuilder.AddMethodSemantics( + eventDefinitionHandle, + MethodSemanticsAttributes.Adder, + addMethod); + + string removeMethodName = QualifiedName(@namespace, "remove_" + typename); + var removeMethod = AddMethodDefinition( + removeMethodName, + new Parameter[] { new Parameter(eventRegistrationToken, "token", ParameterAttributes.In) }, + null, + !isInterfaceParent && isStatic, + isInterfaceParent, + true, + isPublic); + currentTypeDeclaration.AddMethod(symbol, removeMethodName, removeMethod); + + metadataBuilder.AddMethodSemantics( + eventDefinitionHandle, + MethodSemanticsAttributes.Remover, + removeMethod); + } + + public void AddEventDeclaration(IEventSymbol @event, bool isInterfaceParent) + { + AddEventDeclaration(@event.Name, @event.Type, @event, @event.IsStatic, isInterfaceParent, @event.ExplicitInterfaceImplementations.IsDefaultOrEmpty); + } + + void AddMethodDeclaration(IMethodSymbol method, bool isInterfaceParent) + { + Logger.Log("add method from symbol: " + method.Name); + + bool isConstructor = method.MethodKind == MethodKind.Constructor; + string methodName = isConstructor ? ".ctor" : method.Name; + var returnType = isConstructor ? null : new Symbol(method.ReturnType); + Parameter[] parameters = Parameter.GetParameters(method); + var methodDefinitionHandle = AddMethodDefinition( + methodName, + parameters, + returnType, + !isInterfaceParent && method.IsStatic, + isInterfaceParent, + isConstructor, + method.ExplicitInterfaceImplementations.IsDefaultOrEmpty); + currentTypeDeclaration.AddMethod(method, methodName, methodDefinitionHandle); + } + + void AddFactoryMethod(INamedTypeSymbol classSymbol, IMethodSymbol method) + { + Logger.Log("adding factory method: " + method.Name); + + string methodName = "Create" + classSymbol.Name; + Parameter[] parameters = Parameter.GetParameters(method); + var methodDefinitionHandle = AddMethodDefinition( + methodName, + parameters, + new Symbol(classSymbol), + false, + true, + false, + true, + true); + currentTypeDeclaration.AddMethod(method, methodName, methodDefinitionHandle); + } + + void AddComponentType(INamedTypeSymbol type, Action visitTypeDeclaration = null) + { + if (!IsPublic(type) || typeDefinitionMapping.ContainsKey(type.ToString())) + { + return; + } + + Logger.Log("defining type: " + type.TypeKind + " " + type.ToString()); + + var typeDeclaration = new TypeDeclaration(type, true); + currentTypeDeclaration = typeDeclaration; + + if (type.TypeKind == TypeKind.Class) + { + ProcessCustomMappedInterfaces(type); + } + + visitTypeDeclaration?.Invoke(); + CheckAndMarkSymbolForAttributes(type); + + bool isInterface = type.TypeKind == TypeKind.Interface; + bool hasConstructor = false; + bool hasAtLeastOneNonPublicConstructor = false; + bool hasDefaultConstructor = false; + foreach (var member in type.GetMembers()) + { + if (!IsPublic(member) || typeDeclaration.CustomMappedSymbols.Contains(member)) + { + Logger.Log(member.Kind + " member skipped " + member.Name); + + // We want to track whether a given public class has at least one non-public constructor. + // In this case, the class is still exposed to WinRT, but it's not activatable. This is + // different than a class with no explicit constructor, where we do want to generate that + // in the .winmd, and make the class activatable. But we want to avoid always emitting a + // default constructor for classes that only have non-public ones. + if (type.TypeKind == TypeKind.Class && + member is IMethodSymbol { MethodKind: MethodKind.Constructor }) + { + hasAtLeastOneNonPublicConstructor = true; + } + + continue; + } + + if (type.TypeKind == TypeKind.Struct || type.TypeKind == TypeKind.Enum) + { + if (member is IFieldSymbol field) + { + AddFieldDeclaration(field, type.TypeKind == TypeKind.Enum); + } + } + else + { + // Special case: skip members that are explicitly implementing internal interfaces. + // This allows implementing classic COM internal interfaces with non-WinRT signatures. + if (member.IsExplicitInterfaceImplementationOfInternalInterfaces()) + { + continue; + } + + if (member is IMethodSymbol method && + (method.MethodKind == MethodKind.Ordinary || + method.MethodKind == MethodKind.ExplicitInterfaceImplementation || + method.MethodKind == MethodKind.Constructor)) + { + AddMethodDeclaration(method, isInterface); + + if (method.MethodKind == MethodKind.Constructor) + { + hasConstructor = true; + hasDefaultConstructor |= (method.Parameters.Length == 0); + } + } + else if (member is IPropertySymbol property) + { + AddPropertyDeclaration(property, isInterface); + } + else if (member is IEventSymbol @event) + { + AddEventDeclaration(@event, isInterface); + } + else + { + Logger.Log("member not recognized: " + member.Kind + " name: " + member.Name); + continue; + } + } + + CheckAndMarkSymbolForAttributes(member); + } + + // Implicit constructor if none defined, but only if the type doesn't already have some non-public constructor + if (!hasConstructor && !hasAtLeastOneNonPublicConstructor && type.TypeKind == TypeKind.Class && !type.IsStatic) + { + string constructorMethodName = ".ctor"; + var methodDefinitionHandle = AddMethodDefinition( + constructorMethodName, + new Parameter[0], + null, + false, + false, + true, + true, + true); + typeDeclaration.AddMethod(type, constructorMethodName, methodDefinitionHandle); + hasDefaultConstructor = true; + } + + TypeAttributes typeAttributes = + TypeAttributes.Public | + TypeAttributes.WindowsRuntime | + TypeAttributes.AutoLayout | + TypeAttributes.AnsiClass; + + if (type.IsSealed || + type.IsStatic || + type.TypeKind == TypeKind.Struct || + type.TypeKind == TypeKind.Enum) + { + typeAttributes |= TypeAttributes.Sealed; + } + + if (type.TypeKind == TypeKind.Class && type.IsStatic) + { + typeAttributes |= TypeAttributes.Abstract; + } + + EntityHandle baseType = default; + if (isInterface) + { + typeAttributes |= + TypeAttributes.Interface | + TypeAttributes.Abstract; + } + else if (type.TypeKind == TypeKind.Class) + { + typeAttributes |= + TypeAttributes.Class | + TypeAttributes.BeforeFieldInit; + + // extends + // WinRT doesn't support projecting abstract classes. + // If the base class is one, ignore it. + if (type.BaseType != null && !type.BaseType.IsAbstract) + { + baseType = GetTypeReference(type.BaseType); + } + else + { + baseType = GetTypeReference("System", "Object", "mscorlib"); + } + } + else if (type.TypeKind == TypeKind.Struct) + { + typeAttributes |= TypeAttributes.SequentialLayout; + baseType = GetTypeReference("System", "ValueType", "mscorlib"); + } + else if (type.TypeKind == TypeKind.Enum) + { + baseType = GetTypeReference("System", "Enum", "mscorlib"); + } + + var typeDefinitionHandle = AddTypeDefinition( + typeAttributes, + type.ContainingNamespace.ToString(), + type.Name, + baseType); + typeDeclaration.Handle = typeDefinitionHandle; + + if (isInterface || type.TypeKind == TypeKind.Class) + { + // Interface implementations need to be added in order of class and then typespec. Given entries are added + // per class, that is already in order, but we need to sort by typespec. + List> implementedInterfaces = new List>(); + foreach (var implementedInterface in GetInterfaces(type)) + { + implementedInterfaces.Add(new KeyValuePair(implementedInterface, GetTypeSpecification(implementedInterface))); + } + implementedInterfaces.Sort((x, y) => CodedIndex.TypeDefOrRefOrSpec(x.Value).CompareTo(CodedIndex.TypeDefOrRefOrSpec(y.Value))); + + foreach (var implementedInterface in implementedInterfaces) + { + var interfaceImplHandle = metadataBuilder.AddInterfaceImplementation( + typeDefinitionHandle, + implementedInterface.Value); + typeDeclaration.AddInterfaceImpl(implementedInterface.Key, interfaceImplHandle); + } + } + + if (isInterface) + { + AddGuidAttribute(typeDefinitionHandle, type); + AddOverloadAttributeForInterfaceMethods(typeDeclaration); + } + + typeDefinitionMapping[QualifiedName(type, true)] = typeDeclaration; + + if (type.TypeKind == TypeKind.Class) + { + if (hasDefaultConstructor) + { + AddActivatableAttribute( + typeDeclaration.Handle, + (uint)GetVersion(type, true), + null); + RecordWinRTRuntimeClassNameAttribute(type); + } + AddSynthesizedInterfaces(typeDeclaration); + + // No synthesized default interface generated + if (typeDeclaration.DefaultInterface == null && type.Interfaces.Length != 0) + { + AddDefaultInterfaceImplAttribute(typeDeclaration.InterfaceImplDefinitions[type.Interfaces[0]]); + } + } + } + + MemberReferenceHandle AddMethodReference( + string name, + Parameter[] parameters, + Symbol returnSymbol, + INamedTypeSymbol parentType, + bool isStatic) + { + var methodSignature = new BlobBuilder(); + new BlobEncoder(methodSignature) + .MethodSignature( + SignatureCallingConvention.Default, + 0, + !isStatic) + .Parameters( + parameters.Length, + returnType => EncodeReturnType(returnSymbol, returnType), + parametersEncoder => EncodeParameters(parameters, parametersEncoder) + ); + + var referenceHandle = metadataBuilder.AddMemberReference( + GetTypeSpecification(parentType), + metadataBuilder.GetOrAddString(name), + metadataBuilder.GetOrAddBlob(methodSignature) + ); + return referenceHandle; + } + + MemberReferenceHandle AddMethodReference(IMethodSymbol method) + { + Logger.Log("adding method reference: " + method.Name); + + bool isInterfaceParent = method.ContainingType.TypeKind == TypeKind.Interface; + string methodName = method.MethodKind == MethodKind.Constructor ? ".ctor" : method.Name; + Parameter[] parameters = Parameter.GetParameters(method); + var referenceHandle = AddMethodReference( + methodName, + parameters, + new Symbol(method.ReturnType), + method.ContainingType, + !isInterfaceParent && method.IsStatic); + currentTypeDeclaration.AddMethodReference(method, referenceHandle); + return referenceHandle; + } + + public void AddPropertyReference(string name, Symbol type, ISymbol symbol, INamedTypeSymbol parent, bool setMethod) + { + Logger.Log("adding property reference: " + name); + + if (setMethod) + { + var setMethodReference = AddMethodReference( + "put_" + name, + new Parameter[] { new Parameter(type, "value", ParameterAttributes.In) }, + null, + parent, + false); + currentTypeDeclaration.AddMethodReference(symbol, setMethodReference); + } + + var getMethodReference = AddMethodReference( + "get_" + name, + new Parameter[0], + type, + parent, + false); + currentTypeDeclaration.AddMethodReference(symbol, getMethodReference); + } + + public void AddPropertyReference(IPropertySymbol property) + { + AddPropertyReference( + property.Name, + new Symbol(property.Type), + property, + property.ContainingType, + property.SetMethod != null); + } + + public void AddEventReference(string eventName, ITypeSymbol eventType, ISymbol symbol, INamedTypeSymbol parent) + { + Logger.Log("adding event reference: " + eventName); + + EntityHandle eventRegistrationTokenTypeHandle = GetTypeReference("Windows.Foundation", "EventRegistrationToken", "Windows.Foundation.FoundationContract"); + Symbol eventRegistrationToken = new Symbol(eventRegistrationTokenTypeHandle); + + var addMethodReference = AddMethodReference( + "add_" + eventName, + new Parameter[] { new Parameter(eventType, "handler", ParameterAttributes.In) }, + eventRegistrationToken, + parent, + false); + currentTypeDeclaration.AddMethodReference(symbol, addMethodReference); + + var removeMethodReference = AddMethodReference( + "remove_" + eventName, + new Parameter[] { new Parameter(eventRegistrationToken, "token", ParameterAttributes.In) }, + null, + parent, + false); + currentTypeDeclaration.AddMethodReference(symbol, removeMethodReference); + } + + public void AddEventReference(IEventSymbol @event) + { + AddEventReference(@event.Name, @event.Type, @event, @event.ContainingType); + } + + void AddProjectedType(INamedTypeSymbol type, string projectedTypeOverride = null) + { + currentTypeDeclaration = new TypeDeclaration(type); + + foreach (var member in type.GetMembers()) + { + if (member is IMethodSymbol method && + (method.MethodKind == MethodKind.Ordinary || + method.MethodKind == MethodKind.ExplicitInterfaceImplementation || + method.MethodKind == MethodKind.Constructor)) + { + AddMethodReference(method); + } + else if (member is IPropertySymbol property) + { + AddPropertyReference(property); + } + else if (member is IEventSymbol @event) + { + AddEventReference(@event); + } + else + { + Logger.Log("member not recognized: " + member.Kind + " " + member.Name); + } + } + + typeDefinitionMapping[projectedTypeOverride ?? QualifiedName(type, true)] = currentTypeDeclaration; + } + + void AddMappedType(INamedTypeSymbol type) + { + currentTypeDeclaration = new TypeDeclaration(type); + WriteCustomMappedTypeMembers(type, false); + typeDefinitionMapping[QualifiedName(type, true)] = currentTypeDeclaration; + } + + enum SynthesizedInterfaceType + { + Static, + Factory, + Default + } + + string GetSynthesizedInterfaceName(string className, SynthesizedInterfaceType type) + { + // TODO: handle existing types by appending number suffix + return "I" + className + + type switch + { + SynthesizedInterfaceType.Default => "Class", + SynthesizedInterfaceType.Factory => "Factory", + SynthesizedInterfaceType.Static => "Static", + _ => "", + }; + } + + void AddSynthesizedInterfaces(TypeDeclaration classDeclaration) + { + HashSet classMembersFromInterfaces = new(SymbolEqualityComparer.Default); + INamedTypeSymbol classSymbol = classDeclaration.Node as INamedTypeSymbol; + foreach (var @interface in classSymbol.AllInterfaces) + { + foreach (var interfaceMember in @interface.GetMembers()) + { + var classMember = classSymbol.FindImplementationForInterfaceMember(interfaceMember); + if (classMember == null || !classDeclaration.MethodDefinitions.ContainsKey(classMember)) + { + continue; + } + + classMembersFromInterfaces.Add(classMember); + classDeclaration.ClassInterfaceMemberMapping[classMember] = interfaceMember; + + // Mark class members whose interface declaration has attributes + // so that we can propagate them later. + if (interfaceMember.GetAttributes().Any()) + { + classDeclaration.SymbolsWithAttributes.Add(classMember); + } + } + } + + AddSynthesizedInterface( + classDeclaration, + SynthesizedInterfaceType.Static, + classMembersFromInterfaces); + + AddSynthesizedInterface( + classDeclaration, + SynthesizedInterfaceType.Factory, + classMembersFromInterfaces); + + AddSynthesizedInterface( + classDeclaration, + SynthesizedInterfaceType.Default, + classMembersFromInterfaces); + + // TODO: address overridable and composable interfaces. + } + + void CheckAndMarkSynthesizedInterfaceSymbolForAttributes(ISymbol symbol, TypeDeclaration classDeclaration) + { + // Check the class declaration if the symbol had any attributes marked for it, + // and if so propagate it to the synthesized interface. + if (classDeclaration.SymbolsWithAttributes.Contains(symbol)) + { + currentTypeDeclaration.SymbolsWithAttributes.Add(symbol); + } + } + + void CheckAndMarkSymbolForAttributes(ISymbol symbol) + { + if (symbol.GetAttributes().Any()) + { + currentTypeDeclaration.SymbolsWithAttributes.Add(symbol); + } + } + + void AddSynthesizedInterface( + TypeDeclaration classDeclaration, + SynthesizedInterfaceType interfaceType, + HashSet classMembersFromInterfaces) + { + var typeDeclaration = new TypeDeclaration(); + currentTypeDeclaration = typeDeclaration; + + bool hasTypes = false; + INamedTypeSymbol classSymbol = classDeclaration.Node as INamedTypeSymbol; + + // Each class member results in some form of method definition, + // so using that to our advantage to get public members. + foreach (var classMember in classDeclaration.MethodDefinitions) + { + if (interfaceType == SynthesizedInterfaceType.Factory && + classMember.Key is IMethodSymbol constructorMethod && + constructorMethod.MethodKind == MethodKind.Constructor && + constructorMethod.Parameters.Length != 0) + { + hasTypes = true; + AddFactoryMethod(classSymbol, constructorMethod); + CheckAndMarkSynthesizedInterfaceSymbolForAttributes(classMember.Key, classDeclaration); + } + else if ((interfaceType == SynthesizedInterfaceType.Default && !classMember.Key.IsStatic && + !classMembersFromInterfaces.Contains(classMember.Key)) || + (interfaceType == SynthesizedInterfaceType.Static && classMember.Key.IsStatic)) + { + if (classMember.Key is IMethodSymbol method && method.MethodKind == MethodKind.Ordinary) + { + AddMethodDeclaration(method, true); + } + else if (classMember.Key is IPropertySymbol property) + { + AddPropertyDeclaration(property, true); + } + else if (classMember.Key is IEventSymbol @event) + { + AddEventDeclaration(@event, true); + } + else + { + Logger.Log("member for synthesized interface not recognized: " + classMember.Key.Kind + " " + classMember.Key.Name); + continue; + } + + CheckAndMarkSynthesizedInterfaceSymbolForAttributes(classMember.Key, classDeclaration); + hasTypes = true; + } + } + + TypeAttributes typeAttributes = + TypeAttributes.NotPublic | + TypeAttributes.WindowsRuntime | + TypeAttributes.AutoLayout | + TypeAttributes.AnsiClass | + TypeAttributes.Interface | + TypeAttributes.Abstract; + + if (hasTypes || (interfaceType == SynthesizedInterfaceType.Default && classSymbol.Interfaces.Length == 0)) + { + Logger.Log("writing generated interface " + interfaceType); + var interfaceName = GetSynthesizedInterfaceName(classDeclaration.Node.Name, interfaceType); + var typeDefinitionHandle = AddTypeDefinition( + typeAttributes, + classDeclaration.Node.ContainingNamespace.ToString(), + interfaceName, + default); + typeDeclaration.Handle = typeDefinitionHandle; + + string qualifiedInterfaceName = QualifiedName(classDeclaration.Node.ContainingNamespace.ToString(), interfaceName); + typeDefinitionMapping[qualifiedInterfaceName] = typeDeclaration; + + if (interfaceType == SynthesizedInterfaceType.Default) + { + classDeclaration.DefaultInterface = qualifiedInterfaceName; + var interfaceImplHandle = metadataBuilder.AddInterfaceImplementation( + classDeclaration.Handle, + GetTypeReference(classDeclaration.Node.ContainingNamespace.ToString(), interfaceName, assembly)); + classDeclaration.AddInterfaceImpl(classSymbol, interfaceImplHandle); + AddDefaultInterfaceImplAttribute(interfaceImplHandle); + } + + AddDefaultVersionAttribute(typeDefinitionHandle, GetVersion(classSymbol, true)); + AddGuidAttribute(typeDefinitionHandle, interfaceName); + AddExclusiveToAttribute(typeDefinitionHandle, classSymbol.ToString()); + AddOverloadAttributeForInterfaceMethods(typeDeclaration); + + if (interfaceType == SynthesizedInterfaceType.Factory) + { + AddActivatableAttribute(classDeclaration.Handle, (uint)GetVersion(classSymbol, true), qualifiedInterfaceName); + RecordWinRTRuntimeClassNameAttribute(classSymbol); + } + else if (interfaceType == SynthesizedInterfaceType.Static) + { + classDeclaration.StaticInterface = qualifiedInterfaceName; + AddStaticAttribute(classDeclaration.Handle, (uint)GetVersion(classSymbol, true), qualifiedInterfaceName); + RecordWinRTRuntimeClassNameAttribute(classSymbol); + } + } + } + + private int GetVersion(INamedTypeSymbol type, bool setDefaultIfNotSet = false) + { + var versionAttribute = type.GetAttributes(). + Where(attribute => string.CompareOrdinal(attribute.AttributeClass.Name, "VersionAttribute") == 0); + if (!versionAttribute.Any()) + { + return setDefaultIfNotSet ? Version.Parse(this.version).Major : -1; + } + + uint version = (uint)versionAttribute.First().ConstructorArguments[0].Value; + return (int)version; + } + + void AddType(INamedTypeSymbol type, bool treatAsProjectedType = false) + { + Logger.Log("add type: " + type.ToString()); + bool isProjectedType = type.GetAttributes(). + Any(attribute => string.CompareOrdinal(attribute.AttributeClass.Name, "WindowsRuntimeTypeAttribute") == 0); + var qualifiedName = QualifiedName(type); + if (isProjectedType) + { + AddProjectedType(type); + } + else if (mapper.HasMappingForType(qualifiedName)) + { + var (@namespace, name, assembly, isSystemType, _) = mapper.GetMappedType(qualifiedName).GetMapping(); + if (isSystemType) + { + var projectedType = Model.Compilation.GetTypeByMetadataName(QualifiedName(@namespace, name)); + AddProjectedType(projectedType); + } + else + { + AddMappedType(type); + } + } + else if (treatAsProjectedType) + { + // Prioritize any mapped types before treating an attribute as a projected type. + AddProjectedType(type); + } + // Check if CsWinRT component projected type from another project. + else if (Model.Compilation.GetTypeByMetadataName(GeneratorHelper.GetAuthoringMetadataTypeName(qualifiedName)) != null) { AddProjectedType(type); - } - else if (MappedCSharpTypes.ContainsKey(qualifiedName)) - { - var (@namespace, name, assembly, isSystemType, _) = MappedCSharpTypes[qualifiedName].GetMapping(); - if (isSystemType) - { - var projectedType = Model.Compilation.GetTypeByMetadataName(QualifiedName(@namespace, name)); - AddProjectedType(projectedType); - } - else - { - AddMappedType(type); - } - } - else if (treatAsProjectedType) - { - // Prioritize any mapped types before treating an attribute as a projected type. - AddProjectedType(type); - } - else - { - AddComponentType(type); - } - } - - void AddCustomAttributes(TypeDeclaration typeDeclaration, string interfaceName = null) - { - foreach (var node in typeDeclaration.SymbolsWithAttributes) - { - EntityHandle parentHandle; - if (node is INamedTypeSymbol namedType) - { - // Attributes on classes don't propagate to synthesized interfaces. - if (interfaceName != null) - { - continue; - } - - parentHandle = typeDefinitionMapping[namedType.ToString()].Handle; - } - else - { - var typeName = interfaceName ?? node.ContainingType.ToString(); - - if (node is IMethodSymbol method) - { - parentHandle = typeDefinitionMapping[typeName].MethodDefinitions[method][0]; - } - else if (node is IPropertySymbol property) - { - parentHandle = typeDefinitionMapping[typeName].PropertyDefinitions[property]; - } - else if (node is IEventSymbol @event) - { - parentHandle = typeDefinitionMapping[typeName].EventDefinitions[@event]; - } - else - { - Logger.Log("node not recognized " + node.Kind + " name: " + node.Name); - continue; - } - } - - // Add attributes from both the class member declaration and its interface member declaration. - HashSet attributes = new HashSet(node.GetAttributes(), new AttributeDataComparer()); - if (typeDeclaration.ClassInterfaceMemberMapping.ContainsKey(node)) - { - attributes.UnionWith(typeDeclaration.ClassInterfaceMemberMapping[node].GetAttributes()); - } - AddCustomAttributes(attributes, parentHandle); - } - } - - public void FinalizeGeneration() - { - Logger.Log("finalizing"); - var classTypeDeclarations = typeDefinitionMapping.Values - .Where(declaration => declaration.Node is INamedTypeSymbol symbol && symbol.TypeKind == TypeKind.Class) - .ToList(); - - foreach (var classTypeDeclaration in classTypeDeclarations) - { - INamedTypeSymbol classSymbol = classTypeDeclaration.Node as INamedTypeSymbol; - - Logger.Log("finalizing class " + QualifiedName(classSymbol)); - foreach (var implementedInterface in GetInterfaces(classSymbol)) - { - var implementedInterfaceQualifiedNameWithGenerics = QualifiedName(implementedInterface, true); - if (!typeDefinitionMapping.ContainsKey(implementedInterfaceQualifiedNameWithGenerics)) - { - AddType(implementedInterface); - } - - Logger.Log("finalizing interface " + implementedInterfaceQualifiedNameWithGenerics); - var interfaceTypeDeclaration = typeDefinitionMapping[implementedInterfaceQualifiedNameWithGenerics]; - if (MappedCSharpTypes.ContainsKey(QualifiedName(implementedInterface))) - { - Logger.Log("adding MethodImpls for custom mapped interface"); - foreach (var interfaceMember in interfaceTypeDeclaration.MethodReferences) - { - var interfaceMemberMethodDefinitions = interfaceMember.Value; - var classMemberMethodDefinitions = classTypeDeclaration.MethodDefinitions[implementedInterface]; - for (int idx = 0; idx < interfaceMemberMethodDefinitions.Count; idx++) - { - metadataBuilder.AddMethodImplementation( - classTypeDeclaration.Handle, - classMemberMethodDefinitions[idx], - interfaceMemberMethodDefinitions[idx]); - } - } - } - else - { - Logger.Log("adding MethodImpls for interface"); - foreach (var interfaceMember in interfaceTypeDeclaration.MethodReferences) - { - var classMember = classSymbol.FindImplementationForInterfaceMember(interfaceMember.Key); - if (classTypeDeclaration.MethodDefinitions.ContainsKey(classMember)) - { - var interfaceMemberMethodDefinitions = interfaceMember.Value; - var classMemberMethodDefinitions = classTypeDeclaration.MethodDefinitions[classMember]; - for (int idx = 0; idx < interfaceMemberMethodDefinitions.Count; idx++) - { - metadataBuilder.AddMethodImplementation( - classTypeDeclaration.Handle, - classMemberMethodDefinitions[idx], - interfaceMemberMethodDefinitions[idx]); - } - - // If method overloaded in interface, overload in class too. - if (interfaceTypeDeclaration.OverloadedMethods.ContainsKey(interfaceMember.Key)) - { - AddOverloadAttribute(classMemberMethodDefinitions.First(), interfaceTypeDeclaration.OverloadedMethods[interfaceMember.Key]); - } - } - } - } - } - - if (classTypeDeclaration.DefaultInterface != null) - { - Logger.Log("finalizing default interface " + classTypeDeclaration.DefaultInterface); - var defaultInterfaceTypeDeclaration = typeDefinitionMapping[classTypeDeclaration.DefaultInterface]; - foreach (var interfaceMember in defaultInterfaceTypeDeclaration.MethodReferences) - { - if (classTypeDeclaration.MethodDefinitions.ContainsKey(interfaceMember.Key)) - { - var interfaceMemberMethodDefinitions = interfaceMember.Value; - var classMemberMethodDefinitions = classTypeDeclaration.MethodDefinitions[interfaceMember.Key]; - for (int idx = 0; idx < interfaceMemberMethodDefinitions.Count; idx++) - { - metadataBuilder.AddMethodImplementation( - classTypeDeclaration.Handle, - classMemberMethodDefinitions[idx], - interfaceMemberMethodDefinitions[idx]); - } - - // If method overloaded in interface, overload in class too. - if (defaultInterfaceTypeDeclaration.OverloadedMethods.ContainsKey(interfaceMember.Key)) - { - AddOverloadAttribute(classMemberMethodDefinitions.First(), defaultInterfaceTypeDeclaration.OverloadedMethods[interfaceMember.Key]); - } - } - } - } - - if (classTypeDeclaration.StaticInterface != null) - { - Logger.Log("finalizing static interface " + classTypeDeclaration.StaticInterface); - var staticInterfaceTypeDeclaration = typeDefinitionMapping[classTypeDeclaration.StaticInterface]; - foreach (var interfaceMember in staticInterfaceTypeDeclaration.MethodReferences) - { - // If method overloaded in static interface, overload in class too. - if (classTypeDeclaration.MethodDefinitions.ContainsKey(interfaceMember.Key) && - staticInterfaceTypeDeclaration.OverloadedMethods.ContainsKey(interfaceMember.Key)) - { - AddOverloadAttribute( - classTypeDeclaration.MethodDefinitions[interfaceMember.Key].First(), - staticInterfaceTypeDeclaration.OverloadedMethods[interfaceMember.Key] - ); - } - } - } - } - - Logger.Log("adding default version attributes"); - var declarations = typeDefinitionMapping.Values - .Where(declaration => declaration.Node != null) - .ToList(); - foreach (var declaration in declarations) - { - INamedTypeSymbol namedType = declaration.Node as INamedTypeSymbol; - string qualifiedNameWithGenerics = QualifiedName(namedType, true); - if (typeDefinitionMapping[qualifiedNameWithGenerics].Handle != default && GetVersion(namedType) == -1) - { - AddDefaultVersionAttribute(typeDefinitionMapping[qualifiedNameWithGenerics].Handle); - } - } - - Logger.Log("adding custom attributes"); - var typeDeclarationsWithAttributes = typeDefinitionMapping.Values - .Where(declaration => !declaration.IsSynthesizedInterface && declaration.SymbolsWithAttributes.Any()) - .ToList(); - foreach (var typeDeclaration in typeDeclarationsWithAttributes) - { - AddCustomAttributes(typeDeclaration); - - if (typeDeclaration.Node is INamedTypeSymbol symbol && symbol.TypeKind == TypeKind.Class) - { - if (!string.IsNullOrEmpty(typeDeclaration.DefaultInterface)) - { - Logger.Log("adding attributes for default interface " + typeDeclaration.DefaultInterface); - AddCustomAttributes(typeDefinitionMapping[typeDeclaration.DefaultInterface], typeDeclaration.DefaultInterface); - } - - if (!string.IsNullOrEmpty(typeDeclaration.StaticInterface)) - { - Logger.Log("adding attributes for static interface " + typeDeclaration.StaticInterface); - AddCustomAttributes(typeDefinitionMapping[typeDeclaration.StaticInterface], typeDeclaration.StaticInterface); - } - } - } - } - - public bool IsPublic(ISymbol type) - { - return type.DeclaredAccessibility == Accessibility.Public || - type is IMethodSymbol method && !method.ExplicitInterfaceImplementations.IsDefaultOrEmpty || - type is IPropertySymbol property && !property.ExplicitInterfaceImplementations.IsDefaultOrEmpty || - type is IEventSymbol @event && !@event.ExplicitInterfaceImplementations.IsDefaultOrEmpty; - } - - public void GetNamespaceAndTypename(string qualifiedName, out string @namespace, out string typename) - { - var idx = qualifiedName.LastIndexOf('.'); - if (idx == -1) - { - @namespace = ""; - typename = qualifiedName; - } - else - { - @namespace = qualifiedName.Substring(0, idx); - typename = qualifiedName.Substring(idx + 1); - } - } - - public string QualifiedName(string @namespace, string identifier) - { - if (string.IsNullOrEmpty(@namespace)) - { - return identifier; - } - return string.Join(".", @namespace, identifier); - } - - public static string GetGenericName(ISymbol symbol, bool includeGenerics = false) - { - string name = symbol.Name; - if (symbol is INamedTypeSymbol namedType && namedType.TypeArguments.Length != 0) - { - name += "`" + namedType.TypeArguments.Length; - if (includeGenerics) - { - name += string.Format("<{0}>", string.Join(", ", namedType.TypeArguments)); - } - } - return name; - } - - public string QualifiedName(ISymbol symbol, bool includeGenerics = false) - { - return QualifiedName(symbol.ContainingNamespace.ToString(), GetGenericName(symbol, includeGenerics)); - } - } -} \ No newline at end of file + } + else + { + AddComponentType(type); + } + } + + void AddCustomAttributes(TypeDeclaration typeDeclaration, string interfaceName = null) + { + foreach (var node in typeDeclaration.SymbolsWithAttributes) + { + EntityHandle parentHandle; + if (node is INamedTypeSymbol namedType) + { + // Attributes on classes don't propagate to synthesized interfaces. + if (interfaceName != null) + { + continue; + } + + parentHandle = typeDefinitionMapping[namedType.ToString()].Handle; + } + else + { + var typeName = interfaceName ?? node.ContainingType.ToString(); + + if (node is IMethodSymbol method) + { + parentHandle = typeDefinitionMapping[typeName].MethodDefinitions[method][0]; + } + else if (node is IPropertySymbol property) + { + parentHandle = typeDefinitionMapping[typeName].PropertyDefinitions[property]; + } + else if (node is IEventSymbol @event) + { + parentHandle = typeDefinitionMapping[typeName].EventDefinitions[@event]; + } + else + { + Logger.Log("node not recognized " + node.Kind + " name: " + node.Name); + continue; + } + } + + // Add attributes from both the class member declaration and its interface member declaration. + HashSet attributes = new HashSet(node.GetAttributes(), new AttributeDataComparer()); + if (typeDeclaration.ClassInterfaceMemberMapping.ContainsKey(node)) + { + attributes.UnionWith(typeDeclaration.ClassInterfaceMemberMapping[node].GetAttributes()); + } + AddCustomAttributes(attributes, parentHandle); + } + } + + public void FinalizeGeneration() + { + Logger.Log("finalizing"); + var classTypeDeclarations = typeDefinitionMapping.Values + .Where(declaration => declaration.Node is INamedTypeSymbol symbol && symbol.TypeKind == TypeKind.Class) + .ToList(); + + foreach (var classTypeDeclaration in classTypeDeclarations) + { + INamedTypeSymbol classSymbol = classTypeDeclaration.Node as INamedTypeSymbol; + + Logger.Log("finalizing class " + QualifiedName(classSymbol)); + foreach (var implementedInterface in GetInterfaces(classSymbol)) + { + var implementedInterfaceQualifiedNameWithGenerics = QualifiedName(implementedInterface, true); + if (!typeDefinitionMapping.ContainsKey(implementedInterfaceQualifiedNameWithGenerics)) + { + AddType(implementedInterface); + } + + Logger.Log("finalizing interface " + implementedInterfaceQualifiedNameWithGenerics); + var interfaceTypeDeclaration = typeDefinitionMapping[implementedInterfaceQualifiedNameWithGenerics]; + if (mapper.HasMappingForType(QualifiedName(implementedInterface))) + { + Logger.Log("adding MethodImpls for custom mapped interface"); + foreach (var interfaceMember in interfaceTypeDeclaration.MethodReferences) + { + var interfaceMemberMethodDefinitions = interfaceMember.Value; + var classMemberMethodDefinitions = classTypeDeclaration.MethodDefinitions[implementedInterface]; + for (int idx = 0; idx < interfaceMemberMethodDefinitions.Count; idx++) + { + metadataBuilder.AddMethodImplementation( + classTypeDeclaration.Handle, + classMemberMethodDefinitions[idx], + interfaceMemberMethodDefinitions[idx]); + } + } + } + else + { + Logger.Log("adding MethodImpls for interface"); + foreach (var interfaceMember in interfaceTypeDeclaration.MethodReferences) + { + var classMember = classSymbol.FindImplementationForInterfaceMember(interfaceMember.Key); + if (classTypeDeclaration.MethodDefinitions.ContainsKey(classMember)) + { + var interfaceMemberMethodDefinitions = interfaceMember.Value; + var classMemberMethodDefinitions = classTypeDeclaration.MethodDefinitions[classMember]; + for (int idx = 0; idx < interfaceMemberMethodDefinitions.Count; idx++) + { + metadataBuilder.AddMethodImplementation( + classTypeDeclaration.Handle, + classMemberMethodDefinitions[idx], + interfaceMemberMethodDefinitions[idx]); + } + + // If method overloaded in interface, overload in class too. + if (interfaceTypeDeclaration.OverloadedMethods.ContainsKey(interfaceMember.Key)) + { + AddOverloadAttribute(classMemberMethodDefinitions.First(), interfaceTypeDeclaration.OverloadedMethods[interfaceMember.Key]); + } + } + } + } + } + + if (classTypeDeclaration.DefaultInterface != null) + { + Logger.Log("finalizing default interface " + classTypeDeclaration.DefaultInterface); + var defaultInterfaceTypeDeclaration = typeDefinitionMapping[classTypeDeclaration.DefaultInterface]; + foreach (var interfaceMember in defaultInterfaceTypeDeclaration.MethodReferences) + { + if (classTypeDeclaration.MethodDefinitions.ContainsKey(interfaceMember.Key)) + { + var interfaceMemberMethodDefinitions = interfaceMember.Value; + var classMemberMethodDefinitions = classTypeDeclaration.MethodDefinitions[interfaceMember.Key]; + for (int idx = 0; idx < interfaceMemberMethodDefinitions.Count; idx++) + { + metadataBuilder.AddMethodImplementation( + classTypeDeclaration.Handle, + classMemberMethodDefinitions[idx], + interfaceMemberMethodDefinitions[idx]); + } + + // If method overloaded in interface, overload in class too. + if (defaultInterfaceTypeDeclaration.OverloadedMethods.ContainsKey(interfaceMember.Key)) + { + AddOverloadAttribute(classMemberMethodDefinitions.First(), defaultInterfaceTypeDeclaration.OverloadedMethods[interfaceMember.Key]); + } + } + } + } + + if (classTypeDeclaration.StaticInterface != null) + { + Logger.Log("finalizing static interface " + classTypeDeclaration.StaticInterface); + var staticInterfaceTypeDeclaration = typeDefinitionMapping[classTypeDeclaration.StaticInterface]; + foreach (var interfaceMember in staticInterfaceTypeDeclaration.MethodReferences) + { + // If method overloaded in static interface, overload in class too. + if (classTypeDeclaration.MethodDefinitions.ContainsKey(interfaceMember.Key) && + staticInterfaceTypeDeclaration.OverloadedMethods.ContainsKey(interfaceMember.Key)) + { + AddOverloadAttribute( + classTypeDeclaration.MethodDefinitions[interfaceMember.Key].First(), + staticInterfaceTypeDeclaration.OverloadedMethods[interfaceMember.Key] + ); + } + } + } + } + + Logger.Log("adding default version attributes"); + var declarations = typeDefinitionMapping.Values + .Where(declaration => declaration.Node != null) + .ToList(); + foreach (var declaration in declarations) + { + INamedTypeSymbol namedType = declaration.Node as INamedTypeSymbol; + string qualifiedNameWithGenerics = QualifiedName(namedType, true); + if (typeDefinitionMapping[qualifiedNameWithGenerics].Handle != default && GetVersion(namedType) == -1) + { + AddDefaultVersionAttribute(typeDefinitionMapping[qualifiedNameWithGenerics].Handle); + } + } + + Logger.Log("adding custom attributes"); + var typeDeclarationsWithAttributes = typeDefinitionMapping.Values + .Where(declaration => !declaration.IsSynthesizedInterface && declaration.SymbolsWithAttributes.Any()) + .ToList(); + foreach (var typeDeclaration in typeDeclarationsWithAttributes) + { + AddCustomAttributes(typeDeclaration); + + if (typeDeclaration.Node is INamedTypeSymbol symbol && symbol.TypeKind == TypeKind.Class) + { + if (!string.IsNullOrEmpty(typeDeclaration.DefaultInterface)) + { + Logger.Log("adding attributes for default interface " + typeDeclaration.DefaultInterface); + AddCustomAttributes(typeDefinitionMapping[typeDeclaration.DefaultInterface], typeDeclaration.DefaultInterface); + } + + if (!string.IsNullOrEmpty(typeDeclaration.StaticInterface)) + { + Logger.Log("adding attributes for static interface " + typeDeclaration.StaticInterface); + AddCustomAttributes(typeDefinitionMapping[typeDeclaration.StaticInterface], typeDeclaration.StaticInterface); + } + } + } + } + + public void GenerateWinRTExposedClassAttributes(GeneratorExecutionContext context) + { + bool IsWinRTType(ISymbol symbol, TypeMapper mapper) + { + if (!SymbolEqualityComparer.Default.Equals(symbol.ContainingAssembly, context.Compilation.Assembly)) + { + return GeneratorHelper.IsWinRTType(symbol, (symbol, mapper) => IsWinRTType(symbol, mapper), mapper); + } + + if (symbol is INamedTypeSymbol namedType) + { + if (namedType.TypeKind == TypeKind.Interface) + { + // Interfaces which are allowed to be implemented on authored types but + // aren't WinRT interfaces. + return !ImplementedInterfacesWithoutMapping.Contains(QualifiedName(namedType)); + } + + return namedType.SpecialType != SpecialType.System_Object && + namedType.SpecialType != SpecialType.System_Enum && + namedType.SpecialType != SpecialType.System_ValueType && + namedType.SpecialType != SpecialType.System_Delegate && + namedType.SpecialType != SpecialType.System_MulticastDelegate; + } + + // In an authoring component, diagnostics prevents you from using non-WinRT types + // by the time we get to here. + return true; + } + + // 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; + } + + List vtableAttributesToAdd = new(); + HashSet vtableAttributesToAddOnLookupTable = new(); + + Func isManagedOnlyTypeFunc = GeneratorHelper.IsManagedOnlyType(context.Compilation); + + foreach (var typeDeclaration in typeDefinitionMapping.Values) + { + if (typeDeclaration.IsComponentType && + typeDeclaration.Node is INamedTypeSymbol symbol && + symbol.TypeKind == TypeKind.Class && + !symbol.IsStatic) + { + var vtableAttribute = WinRTAotSourceGenerator.GetVtableAttributeToAdd(symbol, isManagedOnlyTypeFunc, IsWinRTType, mapper, context.Compilation, true, typeDeclaration.DefaultInterface); + if (vtableAttribute != default) + { + vtableAttributesToAdd.Add(vtableAttribute); + } + WinRTAotSourceGenerator.AddVtableAdapterTypeForKnownInterface(symbol, context.Compilation, isManagedOnlyTypeFunc, IsWinRTType, mapper, vtableAttributesToAddOnLookupTable); + } + } + + string escapedAssemblyName = GeneratorHelper.EscapeTypeNameForIdentifier(context.Compilation.AssemblyName); + if (vtableAttributesToAdd.Any() || vtableAttributesToAddOnLookupTable.Any()) + { + WinRTAotSourceGenerator.GenerateCCWForGenericInstantiation( + context.AddSource, + vtableAttributesToAdd.SelectMany(static (vtableAttribute, _) => vtableAttribute.GenericInterfaces). + Union(vtableAttributesToAddOnLookupTable.SelectMany(static (vtableAttribute, _) => vtableAttribute.GenericInterfaces)). + Distinct(). + ToImmutableArray(), + escapedAssemblyName); + } + + if (vtableAttributesToAdd.Any()) + { + WinRTAotSourceGenerator.GenerateVtableAttributes(context.AddSource, vtableAttributesToAdd.ToImmutableArray(), false, escapedAssemblyName); + } + + if (vtableAttributesToAddOnLookupTable.Any()) + { + WinRTAotSourceGenerator.GenerateVtableLookupTable(context.AddSource, (vtableAttributesToAddOnLookupTable.ToImmutableArray(), (new CsWinRTAotOptimizerProperties(true, true, true, false), escapedAssemblyName)), true); + } + } + + /// + /// Generates the overrided class name activation factory for a WinRT component. + /// + /// The value to use to produce source files. + public void GenerateOverridedClassNameActivationFactory(GeneratorExecutionContext context) + { + if (!context.GetCsWinRTGenerateOverridedClassNameActivationFactory()) + { + return; + } + + string parameterType = context.IsNet7OrGreater() ? "ReadOnlySpan" : "string"; + + StringBuilder builder = new(); + + builder.AppendLine($$""" + // + #pragma warning disable + + namespace WinRT + { + using global::System; + + /// + unsafe partial class Module + { + + private static partial IntPtr GetActivationFactoryPartial({{parameterType}} runtimeClassId) + { + switch (runtimeClassId) + { + """); + + foreach (var mapping in overridedRuntimeClassNameMappings) + { + builder.AppendLine($$""" + case "{{mapping.Value}}": + return global::ABI.Impl.{{mapping.Key}}ServerActivationFactory.Make(); + """); + } + + builder.Append(""" + default: + return IntPtr.Zero; + } + } + } + } + """); + + context.AddSource("PartialActivationFactory.g.cs", builder.ToString()); + } + + public bool IsPublic(ISymbol symbol) + { + // Check that the type has either public accessibility, or is an explicit interface implementation + if (symbol.DeclaredAccessibility == Accessibility.Public || + symbol is IMethodSymbol method && !method.ExplicitInterfaceImplementations.IsDefaultOrEmpty || + symbol is IPropertySymbol property && !property.ExplicitInterfaceImplementations.IsDefaultOrEmpty || + symbol is IEventSymbol @event && !@event.ExplicitInterfaceImplementations.IsDefaultOrEmpty) + { + // If we have a containing type, we also check that it's publicly accessible + return symbol.ContainingType is not { } containingType || containingType.IsPubliclyAccessible(); + } + + return false; + } + + public void GetNamespaceAndTypename(string qualifiedName, out string @namespace, out string typename) + { + var idx = qualifiedName.LastIndexOf('.'); + if (idx == -1) + { + @namespace = ""; + typename = qualifiedName; + } + else + { + @namespace = qualifiedName.Substring(0, idx); + typename = qualifiedName.Substring(idx + 1); + } + } + + public static string QualifiedName(string @namespace, string identifier) + { + if (string.IsNullOrEmpty(@namespace)) + { + return identifier; + } + return string.Join(".", @namespace, identifier); + } + + public static string GetGenericName(ISymbol symbol, bool includeGenerics = false) + { + string name = symbol.Name; + if (symbol is INamedTypeSymbol namedType && namedType.TypeArguments.Length != 0) + { + name += "`" + namedType.TypeArguments.Length; + if (includeGenerics) + { + name += string.Format("<{0}>", string.Join(", ", namedType.TypeArguments)); + } + } + return name; + } + + public static string QualifiedName(ISymbol symbol, bool includeGenerics = false) + { + return QualifiedName(symbol.ContainingNamespace.ToString(), GetGenericName(symbol, includeGenerics)); + } + } +} diff --git a/src/Authoring/cswinmd/CsWinMD.csproj b/src/Authoring/cswinmd/CsWinMD.csproj index bc34bfbe1..fde9ac185 100644 --- a/src/Authoring/cswinmd/CsWinMD.csproj +++ b/src/Authoring/cswinmd/CsWinMD.csproj @@ -2,9 +2,10 @@ Exe - net6.0 + net8.0 Major preview + enable Microsoft Corporation Microsoft Corporation C#/WinRT @@ -23,13 +24,13 @@ - - - + + + - + diff --git a/src/Authoring/cswinmd/Program.cs b/src/Authoring/cswinmd/Program.cs index 2b56f2171..7d11033e8 100644 --- a/src/Authoring/cswinmd/Program.cs +++ b/src/Authoring/cswinmd/Program.cs @@ -15,11 +15,13 @@ namespace Generator class ConfigOptions : AnalyzerConfigOptions { public Dictionary Values { get; set; } = new(); - public override bool TryGetValue(string key, [NotNullWhen(true)] out string value) + + public override bool TryGetValue(string key, [NotNullWhen(true)] out string? value) { return Values.TryGetValue(key, out value); } } + class ConfigProvider : AnalyzerConfigOptionsProvider { public override AnalyzerConfigOptions GlobalOptions { get; } = new ConfigOptions(); @@ -34,9 +36,9 @@ public override AnalyzerConfigOptions GetOptions(AdditionalText textFile) return GlobalOptions; } } + class Program { -#nullable enable public static void Main(string[] args) { var i = new List(); @@ -44,7 +46,8 @@ public static void Main(string[] args) string? sdkVersion = null; bool? verbose = false; bool? nologo = false; - string a = null; + string? a = null; + for (int idx = 0; idx < args.Length; idx++) { if (args[idx] == "-i") @@ -79,6 +82,7 @@ public static void Main(string[] args) DoMain(i.ToArray(), o!, a!, sdkVersion, verbose, nologo); } + /// /// CSWinMD - Compile C# definitions to WinMD /// @@ -95,14 +99,18 @@ public static void DoMain(string[] i, string o, string a, string? sdkVersion, bo { Console.WriteLine($"CSWinMD {Assembly.GetExecutingAssembly().GetName().Version}"); } + var outFolder = string.IsNullOrEmpty(o) ? Environment.GetEnvironmentVariable("TEMP")! : o!; + try { if (i.Length == 0) { Console.Error.WriteLine("No C# source files specified"); + return; - } else if (i.Length > 1) + } + else if (i.Length > 1) { throw new NotImplementedException("Compiling more than one file is not yet implemented"); } @@ -151,8 +159,8 @@ public static void DoMain(string[] i, string o, string a, string? sdkVersion, bo ); var d = driver.RunGenerators(compilation); - var res = d.GetRunResult(); + if (!res.Diagnostics.IsEmpty) { Console.WriteLine(); @@ -162,7 +170,8 @@ public static void DoMain(string[] i, string o, string a, string? sdkVersion, bo Console.WriteLine($"\t\tIn {v.Location.GetLineSpan()}"); } Console.WriteLine(); - } else + } + else { Console.WriteLine($" => {outFolder}\\{componentName}.winmd"); } @@ -171,14 +180,18 @@ public static void DoMain(string[] i, string o, string a, string? sdkVersion, bo { Console.WriteLine(); Console.Error.WriteLine(e); + if (verbose.HasValue && verbose.Value) { var log_txt = Path.Join(outFolder, "log.txt"); + try { Console.Error.WriteLine(File.ReadAllText(log_txt)); } - catch { } + catch + { + } } } } @@ -196,6 +209,7 @@ private static string GetWindowsWinMdPath(string? sdkVersion) using var roots = hklm.OpenSubKey(@"SOFTWARE\Microsoft\Windows Kits\Installed Roots"); var kitsRoot10 = (string)roots!.GetValue("KitsRoot10")!; var unionMetadata = Path.Combine(kitsRoot10, "UnionMetadata"); + if (sdkVersion == null) { var dirs = Directory.EnumerateDirectories(unionMetadata); @@ -203,10 +217,10 @@ private static string GetWindowsWinMdPath(string? sdkVersion) versions.Sort(); sdkVersion = Path.GetFileName(versions.Last()); } + var path = Path.Combine(kitsRoot10, "UnionMetadata", sdkVersion, "Windows.winmd"); + return path; } - - #nullable restore } } \ No newline at end of file diff --git a/src/Benchmarks/AsyncPerf.cs b/src/Benchmarks/AsyncPerf.cs new file mode 100644 index 000000000..e233997d2 --- /dev/null +++ b/src/Benchmarks/AsyncPerf.cs @@ -0,0 +1,43 @@ +using System; +using System.Threading.Tasks; +using BenchmarkComponent; +using BenchmarkDotNet.Attributes; + +namespace Benchmarks +{ + [MemoryDiagnoser] + public class AsyncPerf + { + ClassWithAsync instance; + + [GlobalSetup] + public void Setup() + { + instance = new ClassWithAsync(); + } + + [Benchmark] + public async Task Complete() + { + await instance.Complete(); + } + + [Benchmark] + public async Task YieldComplete() + { + await instance.YieldComplete(); + } + + [Benchmark] + public async Task Return() + { + return await instance.Return(5); + } + + [Benchmark] + public async Task YieldReturn() + { + return await instance.YieldReturn(5); + } + } +} diff --git a/src/Benchmarks/Benchmarks.csproj b/src/Benchmarks/Benchmarks.csproj index bf67fec8d..072537510 100644 --- a/src/Benchmarks/Benchmarks.csproj +++ b/src/Benchmarks/Benchmarks.csproj @@ -17,10 +17,9 @@ - - - - + + + @@ -67,4 +66,4 @@ - + \ No newline at end of file diff --git a/src/Benchmarks/Program.cs b/src/Benchmarks/Program.cs index 30ddd6d47..ca15d7f5e 100644 --- a/src/Benchmarks/Program.cs +++ b/src/Benchmarks/Program.cs @@ -9,6 +9,8 @@ using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Characteristics; using System.IO; +using BenchmarkDotNet.Validators; +using System.Collections.Generic; #if NET [assembly: global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.10240.0")] @@ -80,11 +82,11 @@ public NetCore3ToolChainWithNativeExecution() CsProjCoreToolchain.NetCoreApp31.Builder, new Executor()) { - } - - public override bool IsSupported(BenchmarkCase benchmarkCase, ILogger logger, IResolver resolver) + } + + public override IEnumerable Validate(BenchmarkCase benchmarkCase, IResolver resolver) { - return CsProjCoreToolchain.NetCoreApp31.IsSupported(benchmarkCase, logger, resolver); + return CsProjCoreToolchain.NetCoreApp31.Validate(benchmarkCase, resolver); } } diff --git a/src/Directory.Build.props b/src/Directory.Build.props index c86c73731..68e75c941 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -7,9 +7,10 @@ 16.0 high high - - 3.0.0-preview4.210210.4 - full + 1.8.251105000 + 1.8.251104001 + 1.8.251104000 + embedded true preview 8305;0618 @@ -17,26 +18,34 @@ false $(GenerateTestProjection) $(WindowsSDKVersion.TrimEnd('\')) + sdk v143 v142 10.0 netcoreapp5.0 false true - netcoreapp3.1;net5.0;net6.0 - netcoreapp3.1;net5.0;net6.0;net7.0 - netstandard2.0;net5.0;net6.0 - netstandard2.0;net5.0;net6.0;net7.0 - net5.0-windows10.0.19041.0;net6.0-windows10.0.19041.0;net7.0-windows10.0.19041.0 - net5.0-windows10.0.19041.0;net6.0-windows10.0.19041.0;net7.0-windows10.0.19041.0 + false + true + netcoreapp3.1;net8.0 + netcoreapp3.1;net8.0 + netstandard2.0;net8.0;net9.0 + netstandard2.0;net8.0;net9.0 + net8.0;net9.0 + net8.0;net9.0 + net8.0-windows10.0.19041.0 + net8.0-windows10.0.19041.0 + net8.0;net9.0 + false + high - + $(DefineConstants);MANUAL_IUNKNOWN - $(MSBuildThisFileDirectory)Perf\IIDOptimizer\bin\$(Configuration)\net6.0\ + $(MSBuildThisFileDirectory)Perf\IIDOptimizer\bin\$(Configuration)\net8.0\ @@ -55,6 +64,11 @@ $([MSBuild]::NormalizeDirectory('$(BuildOutDir)', '$(MSBuildProjectName)', 'obj')) + + + SHA256 + + true true @@ -88,9 +102,23 @@ true MultiThreaded + + Guard + + + %(AdditionalOptions) /Qspectre /ZH:SHA_256 + DebugFull + + + + + %(AdditionalOptions) /Brepro /PDBALTPATH:$(TargetName).pdb + %(AdditionalOptions) /Brepro /PDBALTPATH:$(TargetName).pdb /CETCOMPAT + VERSION_NUMBER=\"$(VersionNumber)\";VERSION_COMMAS=$(VersionNumber.Replace('.', ','));FILE_DESCRIPTION=\"C#/WinRT v$(VersionString)\";%(PreprocessorDefinitions) diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 56cb6ce73..e43169312 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -2,10 +2,6 @@ preview - - https://api.nuget.org/v3/index.json; - https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet-eng/nuget/v3/index.json; - @@ -18,16 +14,18 @@ - + + + - + @@ -35,4 +33,31 @@ + + + + + + + + <_Pdb2PdbPath>$(PkgMicrosoft_DiaSymReader_Pdb2Pdb)\tools\Pdb2Pdb.exe + <_OutputPdbPath>$(OutputPath)$(AssemblyName).pdb + <_Pdb2PdbCommandLine>"$(TargetPath)" /out "$(_OutputPdbPath)" /extract + + + + + + + + + + + + diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props new file mode 100644 index 000000000..1f5964a3e --- /dev/null +++ b/src/Directory.Packages.props @@ -0,0 +1,52 @@ + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Perf/IIDOptimizer/GuidPatcher.cs b/src/Perf/IIDOptimizer/GuidPatcher.cs index 48d20b841..e12723150 100644 --- a/src/Perf/IIDOptimizer/GuidPatcher.cs +++ b/src/Perf/IIDOptimizer/GuidPatcher.cs @@ -72,12 +72,14 @@ public GuidPatcher(AssemblyDefinition winRTRuntime, AssemblyDefinition targetAss guidGeneratorType = null; TypeDefinition? typeExtensionsType = null; + TypeDefinition? wuxMuxProjectedInterfaceAttributeType = null; // Use the type definition if we are patching WinRT.Runtime, otherwise lookup the types as references if (string.CompareOrdinal(assembly.Name.Name, "WinRT.Runtime") == 0) { - guidGeneratorType = winRTRuntimeAssembly.MainModule.Types.Where(typeDef => string.CompareOrdinal(typeDef.Name, "GuidGenerator") == 0).First(); - typeExtensionsType = winRTRuntimeAssembly.MainModule.Types.Where(typeDef => string.CompareOrdinal(typeDef.Name, "TypeExtensions") == 0).First(); + guidGeneratorType = winRTRuntimeAssembly.MainModule.Types.Where(typeDef => string.Equals(typeDef.Name, "GuidGenerator", StringComparison.Ordinal)).First(); + typeExtensionsType = winRTRuntimeAssembly.MainModule.Types.Where(typeDef => string.Equals(typeDef.Name, "TypeExtensions", StringComparison.Ordinal)).First(); + wuxMuxProjectedInterfaceAttributeType = winRTRuntimeAssembly.MainModule.Types.Where(typedef => string.Equals(typedef.Name, "WuxMuxProjectedInterfaceAttribute", StringComparison.Ordinal)).First(); } foreach (var asm in assembly.MainModule.AssemblyReferences) @@ -87,6 +89,7 @@ public GuidPatcher(AssemblyDefinition winRTRuntime, AssemblyDefinition targetAss guidGeneratorType = new TypeReference("WinRT", "GuidGenerator", assembly.MainModule, asm).Resolve(); typeExtensionsType = new TypeReference("WinRT", "TypeExtensions", assembly.MainModule, asm).Resolve(); + wuxMuxProjectedInterfaceAttributeType = new TypeReference("WinRT", "WuxMuxProjectedInterfaceAttribute", assembly.MainModule, asm).Resolve(); } else if (string.CompareOrdinal(asm.Name, "System.Runtime.InteropServices") == 0) { @@ -101,7 +104,7 @@ public GuidPatcher(AssemblyDefinition winRTRuntime, AssemblyDefinition targetAss getHelperTypeMethod = typeExtensionsType.Methods.First(m => String.CompareOrdinal(m.Name, "GetHelperType") == 0); } - signatureGenerator = new SignatureGenerator(assembly, guidAttributeType!, winRTRuntimeAssembly); + signatureGenerator = new SignatureGenerator(assembly, guidAttributeType!, wuxMuxProjectedInterfaceAttributeType!, winRTRuntimeAssembly); methodCache = new Dictionary(); } @@ -292,6 +295,7 @@ private int ProcessMethodBody(MethodBody body, MethodDefinition getTypeFromHandl state = State.Ldtoken; type = typeMaybe; startIlIndex = i; + numberOfInstructionsToOverwrite = 3; } break; case State.Ldtoken: @@ -362,12 +366,11 @@ private int ProcessMethodBody(MethodBody body, MethodDefinition getTypeFromHandl Debug.WriteLine($"Exception thrown during patching {body.Method.FullName}: {ex}"); } } - else - { - state = State.Start; - type = null; - startIlIndex = -1; - } + + // Reset after patching or if we realized this is not the signature to patch. + state = State.Start; + type = null; + startIlIndex = -1; } break; default: diff --git a/src/Perf/IIDOptimizer/IIDOptimizer.csproj b/src/Perf/IIDOptimizer/IIDOptimizer.csproj index fc84f25ca..434781b4f 100644 --- a/src/Perf/IIDOptimizer/IIDOptimizer.csproj +++ b/src/Perf/IIDOptimizer/IIDOptimizer.csproj @@ -2,14 +2,14 @@ Exe - net6.0 + net8.0 enable Major - - + + diff --git a/src/Perf/IIDOptimizer/Program.cs b/src/Perf/IIDOptimizer/Program.cs index 0d595fcbd..c55367852 100644 --- a/src/Perf/IIDOptimizer/Program.cs +++ b/src/Perf/IIDOptimizer/Program.cs @@ -121,19 +121,28 @@ private static int GuidPatch(string targetAssembly, string outputDirectory, IEnu var winRTRuntimeAssembly = ResolveWinRTRuntime(targetAssemblyDefinition, resolver); if (winRTRuntimeAssembly is null) { - Console.WriteLine("Failed to resolve WinRT.Runtime.dll."); return -1; } var guidPatcher = new GuidPatcher(winRTRuntimeAssembly, targetAssemblyDefinition); - int numPatches = guidPatcher.ProcessAssembly(); + int numPatches = guidPatcher.ProcessAssembly(); + Console.WriteLine($"{numPatches} IID calculations/fetches patched"); - guidPatcher.SaveAssembly(outputDirectory); - - Console.WriteLine($"Saved patched .dll to {outputDirectory}"); - Console.WriteLine($"{numPatches} IID calculations/fetches patched"); - return 0; + // Only write assembly if we actually patched anything. + // Otherwise we would just write a type we use as part of our implementation + // when it is actually not needed. + if (numPatches > 0) + { + guidPatcher.SaveAssembly(outputDirectory); + Console.WriteLine($"Saved patched .dll to {outputDirectory}"); + return 0; + } + else + { + // Exit code is checked by caller to copy patched file over. + return -1; + } } catch (AssemblyResolutionException e) { diff --git a/src/Perf/IIDOptimizer/SignatureEmitter.cs b/src/Perf/IIDOptimizer/SignatureEmitter.cs index ff7a7be25..1bc21f005 100644 --- a/src/Perf/IIDOptimizer/SignatureEmitter.cs +++ b/src/Perf/IIDOptimizer/SignatureEmitter.cs @@ -35,6 +35,8 @@ sealed record RuntimeGenericSignatureStep(GenericParameter OriginalTypeParameter sealed record RuntimeCustomSignatureStep(MethodReference method) : SignatureStep; + sealed record FallbackSignatureStep(TypeReference type) : SignatureStep; + public SignatureEmitter(TypeReference describedType, MethodDefinition guidDataGetterMethod) { this.describedType = describedType; @@ -76,6 +78,16 @@ public void PushCustomSignature(MethodReference customSignatureMethod) signatureSteps.Add(new RuntimeCustomSignatureStep(customSignatureMethod)); } + public void PushFallback(TypeReference type) + { + if (currentStringBuilder is not null) + { + signatureSteps.Add(new StringStep(currentStringBuilder.ToString())); + currentStringBuilder = null; + } + signatureSteps.Add(new FallbackSignatureStep(type)); + } + public void EmitGuidGetter( TypeDefinition guidDataBlockType, TypeDefinition implementationDetailsType, @@ -109,9 +121,12 @@ private void GenerateGuidFromSimpleSignature(StringStep stringStep, TypeDefiniti var numBytes = Encoding.UTF8.GetBytes(stringStep.StaticSignatureString, data[Unsafe.SizeOf()..]); data = data[..(Unsafe.SizeOf() + numBytes)]; + // CodeQL [SM02196] WinRT uses UUID v5 SHA1 to generate Guids for parameterized types. Not used for authentication and must not change. Debug.Assert(SHA1.Create().HashSize == 160); Span hash = stackalloc byte[160]; + + // CodeQL [SM02196] WinRT uses UUID v5 SHA1 to generate Guids for parameterized types. Not used for authentication and must not change. SHA1.HashData(data, hash); if (BitConverter.IsLittleEndian) @@ -163,16 +178,24 @@ private void GenerateGuidFactoryFromComplexSignature(TypeDefinition implementati getterMethodGensToCacheTypeGens[guidDataGetterMethod.GenericParameters[i]] = cacheType.GenericParameters[i]; } - var instantiatedCacheType = new GenericInstanceType(cacheType); - foreach (var arg in guidDataGetterMethod.GenericParameters) - { - instantiatedCacheType.GenericArguments.Add(arg); - } + TypeReference instantiatedCacheType = cacheType; + TypeReference selfInstantiatedCacheType = cacheType; - var selfInstantiatedCacheType = new GenericInstanceType(cacheType); - foreach (var param in cacheType.GenericParameters) + if (cacheType.GenericParameters.Count != 0) { - selfInstantiatedCacheType.GenericArguments.Add(param); + var instantiatedCacheTypeTemp = new GenericInstanceType(cacheType); + foreach (var arg in guidDataGetterMethod.GenericParameters) + { + instantiatedCacheTypeTemp.GenericArguments.Add(arg); + } + instantiatedCacheType = instantiatedCacheTypeTemp; + + var selfInstantiatedCacheTypeTemp = new GenericInstanceType(cacheType); + foreach (var param in cacheType.GenericParameters) + { + selfInstantiatedCacheTypeTemp.GenericArguments.Add(param); + } + selfInstantiatedCacheType = selfInstantiatedCacheTypeTemp; } var cacheField = new FieldDefinition("iidData", FieldAttributes.Static | FieldAttributes.Assembly, new ArrayType(module.ImportReference(module.TypeSystem.Byte))); @@ -227,14 +250,14 @@ private void GenerateGuidFactoryFromComplexSignature(TypeDefinition implementati case StringStep(string str): { byte[] segmentBytes = Encoding.UTF8.GetBytes(str); - var staticDataField = new FieldDefinition($"", FieldAttributes.Private | FieldAttributes.InitOnly | FieldAttributes.Static | FieldAttributes.HasFieldRVA, CecilExtensions.GetOrCreateDataBlockType(implementationDetailsType, segmentBytes.Length)) + var staticDataField = new FieldDefinition($"{describedType.FullName}", FieldAttributes.Private | FieldAttributes.InitOnly | FieldAttributes.Static | FieldAttributes.HasFieldRVA, CecilExtensions.GetOrCreateDataBlockType(implementationDetailsType, segmentBytes.Length)) { InitialValue = segmentBytes }; - cacheType.Fields.Add(staticDataField); + implementationDetailsType.Fields.Add(staticDataField); // Load a ReadOnlySpan of the signature segment into the local for this step. - il.Emit(OpCodes.Ldsflda, new FieldReference(staticDataField.Name, staticDataField.FieldType, selfInstantiatedCacheType)); + il.Emit(OpCodes.Ldsflda, new FieldReference(staticDataField.Name, staticDataField.FieldType, implementationDetailsType)); il.Emit(OpCodes.Ldc_I4, segmentBytes.Length); il.Emit(OpCodes.Newobj, readOnlySpanOfBytePtrCtor); il.Emit(OpCodes.Stloc, signatureParts[i]); @@ -281,6 +304,25 @@ private void GenerateGuidFactoryFromComplexSignature(TypeDefinition implementati il.Emit(OpCodes.Stloc, fullSignatureLength); } break; + case FallbackSignatureStep(TypeReference type): + { + // byte[] bytes = Encoding.UTF8.GetBytes(GetSignature(typeof(type))) + il.Emit(OpCodes.Call, utf8EncodingGetter); + il.Emit(OpCodes.Ldtoken, type); + il.Emit(OpCodes.Call, getTypeFromHandleMethod); + il.Emit(OpCodes.Call, getSignatureMethod); + il.Emit(OpCodes.Callvirt, encodingGetBytes); + il.Emit(OpCodes.Dup); + // = new ReadOnlySpan(bytes); + il.Emit(OpCodes.Newobj, readOnlySpanOfByteArrayCtor); + il.Emit(OpCodes.Stloc, signatureParts[i]); + // signatureLength += bytes.Length + il.Emit(OpCodes.Ldlen); + il.Emit(OpCodes.Ldloc, fullSignatureLength); + il.Emit(OpCodes.Add_Ovf); + il.Emit(OpCodes.Stloc, fullSignatureLength); + } + break; default: il.Clear(); throw new InvalidOperationException(); @@ -415,7 +457,8 @@ private void GenerateGuidFactoryFromComplexSignature(TypeDefinition implementati il.Emit(OpCodes.Stloc, destination); // SHA1.HashData(fullSignatureBuffer, destination); - var sha1Type = CecilExtensions.FindTypeReference(module, "System.Security.Cryptography", "SHA1", "System.Security.Cryptography.Algorithms", false); + var sha1Type = module.ImportReference( + new TypeReference("System.Security.Cryptography", "SHA1", module, new AssemblyNameReference("System.Security.Cryptography.Algorithms", default), false).Resolve()); var hashDataMethod = module.ImportReference( new MethodReference("HashData", module.ImportReference(module.TypeSystem.Int32), sha1Type) { @@ -427,30 +470,89 @@ private void GenerateGuidFactoryFromComplexSignature(TypeDefinition implementati } }); - var spanToReadOnlySpan = module.ImportReference( - new MethodReference("op_Implicit", - new GenericInstanceType(module.ImportReference(readOnlySpanOfByte.Resolve())) + // HashData is not defined in .NET Standard + if (hashDataMethod.Resolve() is null) + { + var spanToArrayMethod = module.ImportReference( + new MethodReference("ToArray", new ArrayType(span.Resolve().GenericParameters[0]), spanOfByte) + { + HasThis = true, + }); + + // byte[] arrayToHash = data.ToArray(); + var arrayToHash = new VariableDefinition(new ArrayType(module.TypeSystem.Byte)); + staticCtor.Body.Variables.Add(arrayToHash); + il.Emit(OpCodes.Ldloca, fullSignatureBuffer); + il.Emit(OpCodes.Call, spanToArrayMethod); + il.Emit(OpCodes.Stloc, arrayToHash); + + // using (SHA1 sha = new SHA1CryptoServiceProvider()) + // destination = sha.ComputeHash(data); + var sha1CryptoServiceProvider = module.ImportReference( + new TypeReference("System.Security.Cryptography", "SHA1CryptoServiceProvider", module, new AssemblyNameReference("netstandard", default), false).Resolve()); + var sha = new VariableDefinition(sha1CryptoServiceProvider); + staticCtor.Body.Variables.Add(sha); + var sha1CryptoServiceProviderCtor = module.ImportReference( + new MethodReference(".ctor", module.TypeSystem.Void, sha1CryptoServiceProvider) { - GenericArguments = { span.Resolve().GenericParameters[0] } + HasThis = true }, - spanOfByte) - { - HasThis = false, - Parameters = + sha1CryptoServiceProvider); + il.Emit(OpCodes.Newobj, sha1CryptoServiceProviderCtor); + il.Emit(OpCodes.Stloc, sha); + + var computeHashMethod = module.ImportReference( + new MethodReference("ComputeHash", new ArrayType(module.TypeSystem.Byte), sha1CryptoServiceProvider) { + HasThis = true, + Parameters = + { + new ParameterDefinition(new ArrayType(module.TypeSystem.Byte)) + } + }); + il.Emit(OpCodes.Ldloc, sha); + il.Emit(OpCodes.Ldloc, arrayToHash); + il.Emit(OpCodes.Callvirt, computeHashMethod); + il.Emit(OpCodes.Newobj, spanOfByteArrayCtor); + il.Emit(OpCodes.Stloc, destination); + + var disposable = module.ImportReference( + new TypeReference("System", "IDisposable", module, new AssemblyNameReference("netstandard", default), false).Resolve()); + var disposeMethod = module.ImportReference( + new MethodReference("Dispose", module.TypeSystem.Void, disposable) + { + HasThis = true, + }); + il.Emit(OpCodes.Ldloc, sha); + il.Emit(OpCodes.Callvirt, disposeMethod); + } + else + { + var spanToReadOnlySpan = module.ImportReference( + new MethodReference("op_Implicit", + new GenericInstanceType(module.ImportReference(readOnlySpanOfByte.Resolve())) + { + GenericArguments = { span.Resolve().GenericParameters[0] } + }, + spanOfByte) + { + HasThis = false, + Parameters = + { new ParameterDefinition( new GenericInstanceType(span) { GenericArguments = { span.Resolve().GenericParameters[0] } }) - } - }); + } + }); - il.Emit(OpCodes.Ldloc, fullSignatureBuffer); - il.Emit(OpCodes.Call, spanToReadOnlySpan); - il.Emit(OpCodes.Ldloc, destination); - il.Emit(OpCodes.Call, hashDataMethod); - il.Emit(OpCodes.Pop); + il.Emit(OpCodes.Ldloc, fullSignatureBuffer); + il.Emit(OpCodes.Call, spanToReadOnlySpan); + il.Emit(OpCodes.Ldloc, destination); + il.Emit(OpCodes.Call, hashDataMethod); + il.Emit(OpCodes.Pop); + } // Fix endianness, bytes var memoryExtensions = CecilExtensions.FindTypeReference(module, "System", "MemoryExtensions", "System.Memory", false); diff --git a/src/Perf/IIDOptimizer/SignatureGenerator.cs b/src/Perf/IIDOptimizer/SignatureGenerator.cs index 7c9271254..ce0e7e2ef 100644 --- a/src/Perf/IIDOptimizer/SignatureGenerator.cs +++ b/src/Perf/IIDOptimizer/SignatureGenerator.cs @@ -55,12 +55,14 @@ sealed class SignatureGenerator { private readonly AssemblyDefinition assembly; private readonly TypeDefinition guidAttributeType; + private readonly TypeDefinition wuxMuxProjectedInterfaceAttributeType; private readonly AssemblyDefinition winRTRuntimeAssembly; - public SignatureGenerator(AssemblyDefinition assembly, TypeDefinition guidAttributeType, AssemblyDefinition runtimeAssembly) + public SignatureGenerator(AssemblyDefinition assembly, TypeDefinition guidAttributeType, TypeDefinition wuxMuxProjectedInterfaceAttributeType, AssemblyDefinition runtimeAssembly) { this.assembly = assembly; this.guidAttributeType = guidAttributeType; + this.wuxMuxProjectedInterfaceAttributeType = wuxMuxProjectedInterfaceAttributeType; this.winRTRuntimeAssembly = runtimeAssembly; } @@ -74,8 +76,11 @@ public SignaturePart GetSignatureParts(TypeReference type) var typeDef = type.Resolve(); var helperType = new TypeReference($"ABI.{typeDef.Namespace}", typeDef.Name, typeDef.Module, assembly.MainModule); - - if (helperType.Resolve() is not null) + if (helperType.Resolve() is not null || + // Handle custom mapped built-in structs such as System.Numerics.Vector3 which have their ABI type defined in WinRT.Runtime. + // This is handled separately due to the need for the is public check which isn't needed if in same module as in the initial case. + ((helperType = typeDef.GetCswinrtAbiTypeDefinition(winRTRuntimeAssembly)) is not null && + ((TypeDefinition)helperType).Attributes.HasFlag(TypeAttributes.Public))) { if (type.IsGenericInstance) { @@ -167,6 +172,25 @@ public SignaturePart GetSignatureParts(TypeReference type) return new RuntimeClassSignature(type, GetSignatureParts(iface)); } + // For types projected from WUX or MUX into .NET, we'll need to do a runtime lookup for the IID. + // TODO-WuxMux: We can instead take an option in the IID optimizer to hard-code the lookup for WUX or MUX when specified, which would be more efficient for scenarios where this is possible. + if (helperType?.Resolve().CustomAttributes.Any(attr => attr.AttributeType.Resolve() == wuxMuxProjectedInterfaceAttributeType) == true) + { + var getGuidSignatureMethod = new MethodReference("GetGuidSignature", assembly.MainModule.TypeSystem.String, helperType) + { + HasThis = false + }; + + if (getGuidSignatureMethod.Resolve() is not null) + { + return new CustomSignatureMethod(assembly.MainModule.ImportReference(getGuidSignatureMethod)); + } + else + { + throw new InvalidOperationException($"Unable to resolve GetGuidSignature method for type projected into .NET from WUX/MUX: {type.FullName}."); + } + } + Guid? guidAttributeValue = type.ReadGuidFromAttribute(guidAttributeType, winRTRuntimeAssembly); if (guidAttributeValue == null) { diff --git a/src/Perf/ResultsComparer/ResultsComparer.csproj b/src/Perf/ResultsComparer/ResultsComparer.csproj index 8f1786e16..ba1248fe4 100644 --- a/src/Perf/ResultsComparer/ResultsComparer.csproj +++ b/src/Perf/ResultsComparer/ResultsComparer.csproj @@ -2,13 +2,13 @@ Exe $(PERFLAB_TARGET_FRAMEWORKS) - net5.0 + net8.0 latest - - - - + + + + - + \ No newline at end of file diff --git a/src/Projections/Benchmark/Benchmark.csproj b/src/Projections/Benchmark/Benchmark.csproj index 4af45e6e3..aa9bb3556 100644 --- a/src/Projections/Benchmark/Benchmark.csproj +++ b/src/Projections/Benchmark/Benchmark.csproj @@ -6,7 +6,7 @@ - + @@ -17,4 +17,4 @@ BenchmarkComponent - + \ No newline at end of file diff --git a/src/Projections/Directory.Build.props b/src/Projections/Directory.Build.props index 4305d901a..56af3b07f 100644 --- a/src/Projections/Directory.Build.props +++ b/src/Projections/Directory.Build.props @@ -2,6 +2,26 @@ true + true + + + $(WarningsNotAsErrors);CS0108;CS0109;CS0114;CS0219;CS0628;CS0660;CA2257 + + + True + Auto @@ -9,5 +29,14 @@ true + + + true + true + + + + true + diff --git a/src/Projections/Reunion/Reunion.csproj b/src/Projections/Reunion/Reunion.csproj deleted file mode 100644 index 5fde4c56d..000000000 --- a/src/Projections/Reunion/Reunion.csproj +++ /dev/null @@ -1,61 +0,0 @@ - - - - $(LibBuildTFMs) - x64;x86 - Microsoft.WinUI - 9.9.9.9 - true - true - - - - - - - - - build; buildtransitive; compile; runtime - - - build; buildtransitive; compile; runtime - - - build; buildtransitive; compile; runtime - - - - - - --exclude Windows --include Microsoft -# The current WinUI nuget incorrectly references several Windows.* types that should be -# Microsoft.* types instead. Temporarily include these to enable the build --include Windows.UI.Xaml.Interop.Type --include Windows.UI.Xaml.Interop.NotifyCollectionChangedAction --include Windows.UI.Xaml.Markup.ContentPropertyAttribute --include Windows.UI.Xaml.StyleTypedPropertyAttribute --include Windows.UI.Xaml.TemplatePartAttribute --include Windows.UI.Xaml.TemplateVisualStateAttribute --include Windows.UI.Xaml.Data.BindableAttribute --include Windows.UI.Xaml.Markup.ContentPropertyAttribute --include Windows.UI.Xaml.Markup.FullXamlMetadataProviderAttribute --include Windows.UI.Xaml.Markup.MarkupExtensionReturnTypeAttribute --include Windows.UI.Xaml.Media.Animation.ConditionallyIndependentlyAnimatableAttribute --include Windows.UI.Xaml.Media.Animation.IndependentlyAnimatableAttribute --include Windows.UI.Xaml.Media.Animation.ConditionallyIndependentlyAnimatableAttribute - - - $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.projectreunion.winui', '0.5.7')) - $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.projectreunion.foundation', '0.5.7')) - true - key.snk - - - - - - - - diff --git a/src/Projections/Reunion/key.snk b/src/Projections/Reunion/key.snk deleted file mode 100644 index 2b6f17555..000000000 Binary files a/src/Projections/Reunion/key.snk and /dev/null differ diff --git a/src/Projections/Test/Test.csproj b/src/Projections/Test/Test.csproj index aeadb2198..e09376a0e 100644 --- a/src/Projections/Test/Test.csproj +++ b/src/Projections/Test/Test.csproj @@ -3,8 +3,6 @@ $(LibBuildTFMs) x64;x86 - true - true @@ -13,23 +11,26 @@ - + - + TestComponentCSharp.AnotherAssembly; TestComponent;TestComponentCSharp;test_component_base;test_component_derived;test_component_fast; - - $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.winui', '$(MicrosoftWinUIVersion)')) + + $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.windowsappsdk.foundation', '$(MicrosoftWinAppSDKFoundationVersion)')) + $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.windowsappsdk.interactiveexperiences', '$(MicrosoftWinAppSDKIxpVersion)')) + $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.windowsappsdk.winui', '$(MicrosoftWinAppSDKWinUIVersion)')) - - + + + \ No newline at end of file diff --git a/src/Projections/Test/TestHost.ProbeByHost.cs b/src/Projections/Test/TestHost.ProbeByHost.cs index ba95df96e..3dfb50aaa 100644 --- a/src/Projections/Test/TestHost.ProbeByHost.cs +++ b/src/Projections/Test/TestHost.ProbeByHost.cs @@ -1,118 +1,37 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using Windows.Foundation; using WinRT; - -#region Temporary, until authoring support generates activation factory support - -namespace Windows.Foundation -{ - [global::WinRT.WindowsRuntimeType] - [Guid("00000035-0000-0000-c000-000000000046")] - [WindowsRuntimeHelperType(typeof(global::ABI.Windows.Foundation.IActivationFactory))] - internal interface IActivationFactory - { - Object ActivateInstance(); - } -} - -namespace ABI.Windows.Foundation -{ - [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] - [Guid("00000035-0000-0000-c000-000000000046")] - internal class IActivationFactory : global::Windows.Foundation.IActivationFactory - { - public unsafe delegate int ActivateInstance_0(IntPtr thisPtr, out IntPtr instance); - - [Guid("00000035-0000-0000-c000-000000000046")] - public struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - public ActivateInstance_0 ActivateInstance_0; - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - ActivateInstance_0 = Do_Abi_ActivateInstance_0 - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - private static unsafe int Do_Abi_ActivateInstance_0(IntPtr thisPtr, out IntPtr instance) - { - object __instance = default; - instance = default; - try - { - __instance = global::WinRT.ComWrappersSupport.FindObject(thisPtr).ActivateInstance(); - instance = MarshalInspectable.FromManaged(__instance); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator IActivationFactory(IObjectReference obj) => (obj != null) ? new IActivationFactory(obj) : null; - protected readonly ObjectReference _obj; - public IObjectReference ObjRef { get => _obj; } - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public IActivationFactory(IObjectReference obj) : this(obj.As()) { } - internal IActivationFactory(ObjectReference obj) - { - _obj = obj; - } - - public unsafe object ActivateInstance() - { - IntPtr __retval = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.ActivateInstance_0(ThisPtr, out __retval)); - return MarshalInspectable.FromAbi(__retval); - } - finally - { - MarshalInspectable.DisposeAbi(__retval); - } - } - } -} - namespace WinRT.Host { - internal class ActivationFactory : IActivationFactory + internal partial class ActivationFactory : WinRT.Interop.IActivationFactory { public ConstructorInfo Constructor { get; private set; } public ActivationFactory(ConstructorInfo constructor) => Constructor = constructor; + + public IntPtr ActivateInstance() + { + return MarshalInspectable.FromManaged(Constructor.Invoke(null)); + } - public object ActivateInstance() => Constructor.Invoke(null); } } -#endregion - namespace WinRT { public static class Module - { + { +#if NET + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, typeof(TestHost.ProbeByHost))] + [UnconditionalSuppressMessage("Trimming", "IL2057", Justification = "We're manually keeping the target type around.")] + +#endif public static unsafe IntPtr GetActivationFactory(String runtimeClassId) { if (string.CompareOrdinal(runtimeClassId, "TestHost.ProbeByHost") == 0) @@ -135,8 +54,11 @@ public static unsafe IntPtr GetActivationFactory(String runtimeClassId) namespace TestHost { - public class ProbeByHost : IStringable - { + public partial class ProbeByHost : IStringable + { +#if NET + [UnconditionalSuppressMessage("SingleFile", "IL3000", Justification = "We're not publishing this test as single file.")] +#endif public override string ToString() { return new System.IO.FileInfo(Assembly.GetExecutingAssembly().Location).Name; diff --git a/src/Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.cs b/src/Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.cs index c6078c042..e717395ce 100644 --- a/src/Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.cs +++ b/src/Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.cs @@ -1,119 +1,37 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using Windows.Foundation; using WinRT; - -#region Temporary, until authoring support generates activation factory support - -namespace Windows.Foundation -{ - [global::WinRT.WindowsRuntimeType] - [Guid("00000035-0000-0000-c000-000000000046")] - [WindowsRuntimeHelperType(typeof(global::ABI.Windows.Foundation.IActivationFactory))] - internal interface IActivationFactory - { - Object ActivateInstance(); - } -} - -namespace ABI.Windows.Foundation -{ - [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] - [Guid("00000035-0000-0000-c000-000000000046")] - internal class IActivationFactory : global::Windows.Foundation.IActivationFactory - { - public unsafe delegate int ActivateInstance_0(IntPtr thisPtr, out IntPtr instance); - - [Guid("00000035-0000-0000-c000-000000000046")] - public struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - public ActivateInstance_0 ActivateInstance_0; - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - ActivateInstance_0 = Do_Abi_ActivateInstance_0 - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - private static unsafe int Do_Abi_ActivateInstance_0(IntPtr thisPtr, out IntPtr instance) - { - object __instance = default; - instance = default; - try - { - __instance = global::WinRT.ComWrappersSupport.FindObject(thisPtr).ActivateInstance(); - instance = MarshalInspectable.FromManaged(__instance); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator IActivationFactory(IObjectReference obj) => (obj != null) ? new IActivationFactory(obj) : null; - protected readonly ObjectReference _obj; - public IObjectReference ObjRef { get => _obj; } - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public IActivationFactory(IObjectReference obj) : this(obj.As()) { } - internal IActivationFactory(ObjectReference obj) - { - _obj = obj; - } - - public unsafe object ActivateInstance() - { - IntPtr __retval = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.ActivateInstance_0(ThisPtr, out __retval)); - return MarshalInspectable.FromAbi(__retval); - } - finally - { - MarshalInspectable.DisposeAbi(__retval); - } - } - } -} - namespace WinRT.Host { - internal class ActivationFactory : IActivationFactory + internal partial class ActivationFactory : WinRT.Interop.IActivationFactory { public ConstructorInfo Constructor { get; private set; } public ActivationFactory(ConstructorInfo constructor) => Constructor = constructor; - public object ActivateInstance() => Constructor.Invoke(null); + public IntPtr ActivateInstance() + { + return MarshalInspectable.FromManaged(Constructor.Invoke(null)); + } } } -#endregion - namespace WinRT { public static class Module - { - public static unsafe IntPtr GetActivationFactory(String runtimeClassId) + { +#if NET + [DynamicDependency(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor, typeof(TestHost.ProbeByClass))] + [UnconditionalSuppressMessage("Trimming", "IL2057", Justification = "We're manually keeping the target type around.")] + +#endif + public static unsafe IntPtr GetActivationFactory(string runtimeClassId) { if (string.CompareOrdinal(runtimeClassId, "TestHost.ProbeByClass") == 0) { @@ -135,8 +53,11 @@ public static unsafe IntPtr GetActivationFactory(String runtimeClassId) namespace TestHost { - public class ProbeByClass : IStringable - { + public partial class ProbeByClass : IStringable + { +#if NET + [UnconditionalSuppressMessage("SingleFile", "IL3000", Justification = "We're not publishing this test as single file.")] +#endif public override string ToString() { return new System.IO.FileInfo(Assembly.GetExecutingAssembly().Location).Name; diff --git a/src/Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.csproj b/src/Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.csproj index 4ef297369..d945c85e6 100644 --- a/src/Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.csproj +++ b/src/Projections/TestHost.ProbeByClass/TestHost.ProbeByClass.csproj @@ -8,22 +8,25 @@ - + - + TestComponent;TestComponentCSharp - - $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.winui', '$(MicrosoftWinUIVersion)')) + + $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.windowsappsdk.foundation', '$(MicrosoftWinAppSDKFoundationVersion)')) + $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.windowsappsdk.interactiveexperiences', '$(MicrosoftWinAppSDKIxpVersion)')) + $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.windowsappsdk.winui', '$(MicrosoftWinAppSDKWinUIVersion)')) - - + + + - + \ No newline at end of file diff --git a/src/Projections/TestPublicExclusiveTo/TestPublicExclusiveTo.csproj b/src/Projections/TestPublicExclusiveTo/TestPublicExclusiveTo.csproj new file mode 100644 index 000000000..9bd5c479b --- /dev/null +++ b/src/Projections/TestPublicExclusiveTo/TestPublicExclusiveTo.csproj @@ -0,0 +1,34 @@ + + + + $(LibBuildTFMs) + x64;x86 + + + + + + + + + + + + + + + + TestComponentCSharp.TestPublicExclusiveTo; + true + + $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.windowsappsdk.foundation', '$(MicrosoftWinAppSDKFoundationVersion)')) + $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.windowsappsdk.interactiveexperiences', '$(MicrosoftWinAppSDKIxpVersion)')) + $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.windowsappsdk.winui', '$(MicrosoftWinAppSDKWinUIVersion)')) + + + + + + + + \ No newline at end of file diff --git a/src/Projections/TestSubset/TestSubset.csproj b/src/Projections/TestSubset/TestSubset.csproj index 07384f019..4bd74a4ef 100644 --- a/src/Projections/TestSubset/TestSubset.csproj +++ b/src/Projections/TestSubset/TestSubset.csproj @@ -10,23 +10,26 @@ - + - + TestComponentCSharp.AnotherAssembly; - - $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.winui', '$(MicrosoftWinUIVersion)')) + + $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.windowsappsdk.foundation', '$(MicrosoftWinAppSDKFoundationVersion)')) + $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.windowsappsdk.interactiveexperiences', '$(MicrosoftWinAppSDKIxpVersion)')) + $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.windowsappsdk.winui', '$(MicrosoftWinAppSDKWinUIVersion)')) - - + + + \ No newline at end of file diff --git a/src/Projections/Reunion/Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext.cs b/src/Projections/WinAppSDK/Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext.cs similarity index 94% rename from src/Projections/Reunion/Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext.cs rename to src/Projections/WinAppSDK/Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext.cs index f1f5a7e5c..be413612b 100644 --- a/src/Projections/Reunion/Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext.cs +++ b/src/Projections/WinAppSDK/Microsoft.UI.Dispatching.DispatcherQueueSynchronizationContext.cs @@ -1,8 +1,8 @@ using System; using System.Threading; -using Microsoft.System; +using Microsoft.UI.Dispatching; -namespace Microsoft.System +namespace Microsoft.UI.Dispatching { /// /// DispatcherQueueSyncContext allows developers to await calls and get back onto the diff --git a/src/Projections/WinAppSDK/WinAppSDK.csproj b/src/Projections/WinAppSDK/WinAppSDK.csproj new file mode 100644 index 000000000..539ed8802 --- /dev/null +++ b/src/Projections/WinAppSDK/WinAppSDK.csproj @@ -0,0 +1,55 @@ + + + + $(LibBuildTFMs) + x64;x86 + Microsoft.WinUI + + + + + + + + + build; buildtransitive; compile; runtime + + + + + + -exclude Windows + -include Microsoft + # The current WinUI nuget incorrectly references several Windows.* types that should be + # Microsoft.* types instead. Temporarily include these to enable the build + -include Windows.UI.Xaml.Interop.Type + -include Windows.UI.Xaml.Interop.NotifyCollectionChangedAction + -include Windows.UI.Xaml.Markup.ContentPropertyAttribute + -include Windows.UI.Xaml.StyleTypedPropertyAttribute + -include Windows.UI.Xaml.TemplatePartAttribute + -include Windows.UI.Xaml.TemplateVisualStateAttribute + -include Windows.UI.Xaml.Data.BindableAttribute + -include Windows.UI.Xaml.Markup.ContentPropertyAttribute + -include Windows.UI.Xaml.Markup.FullXamlMetadataProviderAttribute + -include Windows.UI.Xaml.Markup.MarkupExtensionReturnTypeAttribute + -include Windows.UI.Xaml.Media.Animation.ConditionallyIndependentlyAnimatableAttribute + -include Windows.UI.Xaml.Media.Animation.IndependentlyAnimatableAttribute + -include Windows.UI.Xaml.Media.Animation.ConditionallyIndependentlyAnimatableAttribute + -exclude Microsoft.UI.Xaml.Controls.WebView2 + -exclude Microsoft.UI.Xaml.Controls.IWebView + -exclude Microsoft.UI.Xaml.Automation.Peers.IWebView + -exclude Microsoft.UI.Xaml.Automation.Peers.WebView + -addition_exclude Windows.UI.Xaml.Media.Animation + + + $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.windowsappsdk.foundation', '$(MicrosoftWinAppSDKFoundationVersion)')) + $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.windowsappsdk.interactiveexperiences', '$(MicrosoftWinAppSDKIxpVersion)')) + $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.windowsappsdk.winui', '$(MicrosoftWinAppSDKWinUIVersion)')) + + + + + + + + \ No newline at end of file diff --git a/src/Projections/WinUI/WinUI.csproj b/src/Projections/WinUI/WinUI.csproj deleted file mode 100644 index 74dc4932e..000000000 --- a/src/Projections/WinUI/WinUI.csproj +++ /dev/null @@ -1,48 +0,0 @@ - - - - $(LibBuildTFMs) - x64;x86 - true - true - - - - - - - - - build; buildtransitive; compile; runtime - - - - - --exclude Windows --include Microsoft -# The current WinUI nuget incorrectly references several Windows.* types that should be -# Microsoft.* types instead. Temporarily include these to enable the build --include Windows.UI.Xaml.Interop.Type --include Windows.UI.Xaml.Interop.NotifyCollectionChangedAction --include Windows.UI.Xaml.Markup.ContentPropertyAttribute --include Windows.UI.Xaml.StyleTypedPropertyAttribute --include Windows.UI.Xaml.TemplatePartAttribute --include Windows.UI.Xaml.TemplateVisualStateAttribute --include Windows.UI.Xaml.Data.BindableAttribute --include Windows.UI.Xaml.Markup.ContentPropertyAttribute --include Windows.UI.Xaml.Markup.FullXamlMetadataProviderAttribute --include Windows.UI.Xaml.Markup.MarkupExtensionReturnTypeAttribute --include Windows.UI.Xaml.Media.Animation.ConditionallyIndependentlyAnimatableAttribute --include Windows.UI.Xaml.Media.Animation.IndependentlyAnimatableAttribute --include Windows.UI.Xaml.Media.Animation.ConditionallyIndependentlyAnimatableAttribute - - - $([MSBuild]::NormalizeDirectory('$(NuGetPackageRoot)', 'microsoft.winui', '$(MicrosoftWinUIVersion)')) - - - - - - - diff --git a/src/Projections/Windows.UI.Xaml/ABI.Windows.System/DispatcherQueueProxyHandler.cs b/src/Projections/Windows.UI.Xaml/ABI.Windows.System/DispatcherQueueProxyHandler.cs new file mode 100644 index 000000000..04ccef611 --- /dev/null +++ b/src/Projections/Windows.UI.Xaml/ABI.Windows.System/DispatcherQueueProxyHandler.cs @@ -0,0 +1,223 @@ +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using WinRT; +using WinRT.Interop; + +#nullable enable + +namespace ABI.Windows.System; + +/// +/// A custom IDispatcherQueueHandler object, that internally stores a captured instance and the +/// input captured state. This allows consumers to enqueue a state and a cached stateless delegate without any managed allocations. +/// +internal unsafe struct DispatcherQueueProxyHandler +{ + /// + /// The shared vtable pointer for instances. + /// + private static readonly void** Vtbl = InitVtbl(); + + /// + /// Setups the vtable pointer for . + /// + /// The initialized vtable pointer for . + /// + /// The vtable itself is allocated with , + /// which allocates memory in the high frequency heap associated with the input runtime type. This will be + /// automatically cleaned up when the type is unloaded, so there is no need to ever manually free this memory. + /// + private static void** InitVtbl() + { + void** vtbl = (void**)RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(DispatcherQueueProxyHandler), sizeof(void*) * 4); + + vtbl[0] = (delegate* unmanaged)&Impl.QueryInterface; + vtbl[1] = (delegate* unmanaged)&Impl.AddRef; + vtbl[2] = (delegate* unmanaged)&Impl.Release; + vtbl[3] = (delegate* unmanaged)&Impl.Invoke; + + return vtbl; + } + + /// + /// The vtable pointer for the current instance. + /// + private void** vtbl; + + /// + /// The to the captured . + /// + private GCHandle callbackHandle; + + /// + /// The to the captured state (if present, or a handle otherwise). + /// + private GCHandle stateHandle; + + /// + /// The current reference count for the object (from IUnknown). + /// + private volatile uint referenceCount; + + /// + /// Creates a new instance for the input callback and state. + /// + /// The input callback to enqueue. + /// The input state to capture and pass to the callback. + /// A pointer to the newly initialized instance. + public static DispatcherQueueProxyHandler* Create(SendOrPostCallback handler, object? state) + { + DispatcherQueueProxyHandler* @this = (DispatcherQueueProxyHandler*)NativeMemory.Alloc((nuint)sizeof(DispatcherQueueProxyHandler)); + + @this->vtbl = Vtbl; + @this->callbackHandle = GCHandle.Alloc(handler); + @this->stateHandle = state is not null ? GCHandle.Alloc(state) : default; + @this->referenceCount = 1; + + return @this; + } + + /// + /// Devirtualized API for IUnknown.Release(). + /// + /// The updated reference count for the current instance. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public uint Release() + { + uint referenceCount = Interlocked.Decrement(ref this.referenceCount); + + if (referenceCount == 0) + { + callbackHandle.Free(); + + if (stateHandle.IsAllocated) + { + stateHandle.Free(); + } + + NativeMemory.Free(Unsafe.AsPointer(ref this)); + } + + return referenceCount; + } + + /// + /// A private type with the implementation of the unmanaged methods for . + /// These methods will be set into the shared vtable and invoked by WinRT from the object passed to it as an interface. + /// + private static class Impl + { + /// + /// The HRESULT for a successful operation. + /// + private const int S_OK = 0; + + /// + /// The HRESULT for an invalid cast from IUnknown.QueryInterface. + /// + private const int E_NOINTERFACE = unchecked((int)0x80004002); + + /// The IID for IDispatcherQueueHandler (2E0872A9-4E29-5F14-B688-FB96D5F9D5F8). + private static ref readonly Guid IID_IDispatcherQueueHandler + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = + [ + 0xA9, 0x72, 0x08, 0x2E, + 0x29, 0x4E, + 0x14, 0x5F, + 0xB6, + 0x88, + 0xFB, + 0x96, + 0xD5, + 0xF9, + 0xD5, + 0xF8 + ]; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// + /// Implements IUnknown.QueryInterface(REFIID, void**). + /// + [UnmanagedCallersOnly] + public static int QueryInterface(DispatcherQueueProxyHandler* @this, Guid* riid, void** ppvObject) + { + if (riid->Equals(IID.IID_IUnknown) || + riid->Equals(IID.IID_IAgileObject) || + riid->Equals(IID_IDispatcherQueueHandler)) + { + Interlocked.Increment(ref @this->referenceCount); + + *ppvObject = @this; + + return S_OK; + } + + return E_NOINTERFACE; + } + + /// + /// Implements IUnknown.AddRef(). + /// + [UnmanagedCallersOnly] + public static uint AddRef(DispatcherQueueProxyHandler* @this) + { + return Interlocked.Increment(ref @this->referenceCount); + } + + /// + /// Implements IUnknown.Release(). + /// + [UnmanagedCallersOnly] + public static uint Release(DispatcherQueueProxyHandler* @this) + { + uint referenceCount = Interlocked.Decrement(ref @this->referenceCount); + + if (referenceCount == 0) + { + @this->callbackHandle.Free(); + + if (@this->stateHandle.IsAllocated) + { + @this->stateHandle.Free(); + } + + NativeMemory.Free(@this); + } + + return referenceCount; + } + + /// + /// Implements IDispatcherQueueHandler.Invoke(). + /// + [UnmanagedCallersOnly] + public static int Invoke(DispatcherQueueProxyHandler* @this) + { + object callback = @this->callbackHandle.Target!; + object? state = @this->stateHandle.IsAllocated ? @this->stateHandle.Target! : null; + + try + { + Unsafe.As(callback)(state); + } + catch (Exception e) + { + // Register the exception with the global error handler. The failfast behavior + // is governed by the state of the 'UnhandledExceptionEventArgs.Handled' property. + // If 'Handled' is true the app continues running, else it failfasts. + ExceptionHelpers.ReportUnhandledError(e); + } + + return S_OK; + } + } +} diff --git a/src/Projections/Windows.UI.Xaml/DispatcherQueueSynchronizationContext.cs b/src/Projections/Windows.UI.Xaml/DispatcherQueueSynchronizationContext.cs new file mode 100644 index 000000000..dbb8f0fd5 --- /dev/null +++ b/src/Projections/Windows.UI.Xaml/DispatcherQueueSynchronizationContext.cs @@ -0,0 +1,82 @@ +using System; +using System.Threading; +using WinRT; + +#nullable enable + +namespace Windows.System; + +/// +/// The type allows developers to await calls and get back onto +/// the UI thread. Needs to be installed on the UI thread through . +/// +public sealed partial class DispatcherQueueSynchronizationContext : SynchronizationContext +{ + /// + /// The instance for the target dispatcher queue. + /// + private readonly IObjectReference _objectReference; + + /// + /// Creates a new instance with the specified parameters. + /// + /// The target instance. + /// Thrown if is . + public DispatcherQueueSynchronizationContext(global::Windows.System.DispatcherQueue dispatcherQueue) + { + ArgumentNullException.ThrowIfNull(dispatcherQueue); + + _objectReference = ((IWinRTObject)dispatcherQueue).NativeObject; + } + + /// + /// Creates a new instance with the specified parameters. + /// + /// The instance for the target dispatcher queue. + /// Thrown if is . + private DispatcherQueueSynchronizationContext(IObjectReference objectReference) + { + ArgumentNullException.ThrowIfNull(objectReference); + + _objectReference = objectReference; + } + + /// + public override unsafe void Post(SendOrPostCallback d, object? state) + { + ArgumentNullException.ThrowIfNull(d); + + global::ABI.Windows.System.DispatcherQueueProxyHandler* dispatcherQueueProxyHandler = global::ABI.Windows.System.DispatcherQueueProxyHandler.Create(d, state); + int hresult; + + try + { + void* thisPtr = (void*)_objectReference.ThisPtr; + bool success; + + // Note: we're intentionally ignoring the retval for 'DispatcherQueue::TryEnqueue'. + // This matches the behavior for the equivalent type on WinUI 3 as well. + hresult = ((delegate* unmanaged)(*(void***)thisPtr)[7])(thisPtr, dispatcherQueueProxyHandler, (byte*)&success); + + GC.KeepAlive(_objectReference); + } + finally + { + dispatcherQueueProxyHandler->Release(); + } + + ExceptionHelpers.ThrowExceptionForHR(hresult); + } + + /// + public override void Send(SendOrPostCallback d, object? state) + { + throw new NotSupportedException("'SynchronizationContext.Send' is not supported."); + } + + /// + public override SynchronizationContext CreateCopy() + { + return new DispatcherQueueSynchronizationContext(_objectReference); + } +} \ No newline at end of file diff --git a/src/Projections/WinUI/Module.cs b/src/Projections/Windows.UI.Xaml/Module.cs similarity index 76% rename from src/Projections/WinUI/Module.cs rename to src/Projections/Windows.UI.Xaml/Module.cs index 83b22e6fb..636144eed 100644 --- a/src/Projections/WinUI/Module.cs +++ b/src/Projections/Windows.UI.Xaml/Module.cs @@ -1,3 +1,3 @@ #if NET -[assembly: global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.10240.0")] +[assembly: global::System.Runtime.Versioning.SupportedOSPlatform("Windows")] #endif diff --git a/src/Projections/Windows.UI.Xaml/Windows.UI.Xaml.csproj b/src/Projections/Windows.UI.Xaml/Windows.UI.Xaml.csproj new file mode 100644 index 000000000..aca82eba3 --- /dev/null +++ b/src/Projections/Windows.UI.Xaml/Windows.UI.Xaml.csproj @@ -0,0 +1,28 @@ + + + + $(LibBuildTFMsNoNetStandard) + x64;x86 + Microsoft.Windows.UI.Xaml + true + + + + + + + + + + + + + +-exclude Windows +-include Windows.UI.Xaml +-exclude Windows.UI.Xaml.Media.Animation.ConditionallyIndependentlyAnimatableAttribute +-addition_exclude Windows.UI.Xaml.Media.Animation + + + + \ No newline at end of file diff --git a/src/Projections/Windows/Windows.csproj b/src/Projections/Windows/Windows.csproj index 774ec562b..86cb675d0 100644 --- a/src/Projections/Windows/Windows.csproj +++ b/src/Projections/Windows/Windows.csproj @@ -4,15 +4,15 @@ $(LibBuildTFMs) x64;x86 Microsoft.Windows.SDK.NET - true - true + true - + + @@ -24,6 +24,10 @@ -exclude Windows.UI.IColors -exclude Windows.UI.ColorHelper -exclude Windows.UI.IColorHelper +-exclude Windows.UI.ColorHelper +-exclude Windows.UI.IColorHelper +-exclude Windows.UI.IColorHelperStatics +-exclude Windows.UI.IColorHelperStatics2 #-exclude Windows.UI.Text (must include Windows.UI.Text to work around WinUI nuget issues) -exclude Windows.UI.Xaml -exclude Windows.ApplicationModel.Store.Preview diff --git a/src/Samples/AuthoringDemo/AuthoringDemo.sln b/src/Samples/AuthoringDemo/AuthoringDemo.sln index 1d989d219..9b1dd02a7 100644 --- a/src/Samples/AuthoringDemo/AuthoringDemo.sln +++ b/src/Samples/AuthoringDemo/AuthoringDemo.sln @@ -1,80 +1,79 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.1.31911.260 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CppConsoleApp", "CppConsoleApp\CppConsoleApp.vcxproj", "{96752AD7-C344-4D4D-9E0B-39BED88B191A}" - ProjectSection(ProjectDependencies) = postProject - {7E449DC2-5334-464D-B478-1715B96C8050} = {7E449DC2-5334-464D-B478-1715B96C8050} - EndProjectSection -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthoringDemo", "AuthoringDemo\AuthoringDemo.csproj", "{7E449DC2-5334-464D-B478-1715B96C8050}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1452D9DE-635B-46F9-9C0E-B2FEB4F0A9EB}" - ProjectSection(SolutionItems) = preProject - Directory.Build.props = Directory.Build.props - Directory.Build.targets = Directory.Build.targets - nuget.config = nuget.config - EndProjectSection -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinUI3CppApp", "WinUI3CppApp\WinUI3CppApp.vcxproj", "{CA5A86BC-C0FB-472D-B1B9-3C5221595606}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|ARM64 = Debug|ARM64 - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|ARM64 = Release|ARM64 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Debug|ARM64.Build.0 = Debug|ARM64 - {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Debug|x64.ActiveCfg = Debug|x64 - {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Debug|x64.Build.0 = Debug|x64 - {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Debug|x86.ActiveCfg = Debug|Win32 - {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Debug|x86.Build.0 = Debug|Win32 - {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Release|ARM64.ActiveCfg = Release|ARM64 - {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Release|ARM64.Build.0 = Release|ARM64 - {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Release|x64.ActiveCfg = Release|x64 - {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Release|x64.Build.0 = Release|x64 - {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Release|x86.ActiveCfg = Release|Win32 - {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Release|x86.Build.0 = Release|Win32 - {7E449DC2-5334-464D-B478-1715B96C8050}.Debug|ARM64.ActiveCfg = Debug|arm64 - {7E449DC2-5334-464D-B478-1715B96C8050}.Debug|ARM64.Build.0 = Debug|arm64 - {7E449DC2-5334-464D-B478-1715B96C8050}.Debug|x64.ActiveCfg = Debug|x64 - {7E449DC2-5334-464D-B478-1715B96C8050}.Debug|x64.Build.0 = Debug|x64 - {7E449DC2-5334-464D-B478-1715B96C8050}.Debug|x86.ActiveCfg = Debug|x86 - {7E449DC2-5334-464D-B478-1715B96C8050}.Debug|x86.Build.0 = Debug|x86 - {7E449DC2-5334-464D-B478-1715B96C8050}.Release|ARM64.ActiveCfg = Release|arm64 - {7E449DC2-5334-464D-B478-1715B96C8050}.Release|ARM64.Build.0 = Release|arm64 - {7E449DC2-5334-464D-B478-1715B96C8050}.Release|x64.ActiveCfg = Release|x64 - {7E449DC2-5334-464D-B478-1715B96C8050}.Release|x64.Build.0 = Release|x64 - {7E449DC2-5334-464D-B478-1715B96C8050}.Release|x86.ActiveCfg = Release|x64 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|ARM64.ActiveCfg = Debug|arm64 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|ARM64.Build.0 = Debug|arm64 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|ARM64.Deploy.0 = Debug|arm64 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|x64.ActiveCfg = Debug|x64 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|x64.Build.0 = Debug|x64 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|x64.Deploy.0 = Debug|x64 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|x86.ActiveCfg = Debug|Win32 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|x86.Build.0 = Debug|Win32 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|x86.Deploy.0 = Debug|Win32 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|ARM64.ActiveCfg = Release|arm64 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|ARM64.Build.0 = Release|arm64 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|ARM64.Deploy.0 = Release|arm64 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|x64.ActiveCfg = Release|x64 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|x64.Build.0 = Release|x64 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|x64.Deploy.0 = Release|x64 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|x86.ActiveCfg = Release|Win32 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|x86.Build.0 = Release|Win32 - {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|x86.Deploy.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {EBDA7BDA-5188-4C95-B3FF-19AADD03A4C1} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.31911.260 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CppConsoleApp", "CppConsoleApp\CppConsoleApp.vcxproj", "{96752AD7-C344-4D4D-9E0B-39BED88B191A}" + ProjectSection(ProjectDependencies) = postProject + {7E449DC2-5334-464D-B478-1715B96C8050} = {7E449DC2-5334-464D-B478-1715B96C8050} + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthoringDemo", "AuthoringDemo\AuthoringDemo.csproj", "{7E449DC2-5334-464D-B478-1715B96C8050}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{1452D9DE-635B-46F9-9C0E-B2FEB4F0A9EB}" + ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "WinUI3CppApp", "WinUI3CppApp\WinUI3CppApp.vcxproj", "{CA5A86BC-C0FB-472D-B1B9-3C5221595606}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Debug|ARM64.Build.0 = Debug|ARM64 + {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Debug|x64.ActiveCfg = Debug|x64 + {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Debug|x64.Build.0 = Debug|x64 + {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Debug|x86.ActiveCfg = Debug|Win32 + {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Debug|x86.Build.0 = Debug|Win32 + {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Release|ARM64.ActiveCfg = Release|ARM64 + {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Release|ARM64.Build.0 = Release|ARM64 + {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Release|x64.ActiveCfg = Release|x64 + {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Release|x64.Build.0 = Release|x64 + {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Release|x86.ActiveCfg = Release|Win32 + {96752AD7-C344-4D4D-9E0B-39BED88B191A}.Release|x86.Build.0 = Release|Win32 + {7E449DC2-5334-464D-B478-1715B96C8050}.Debug|ARM64.ActiveCfg = Debug|arm64 + {7E449DC2-5334-464D-B478-1715B96C8050}.Debug|ARM64.Build.0 = Debug|arm64 + {7E449DC2-5334-464D-B478-1715B96C8050}.Debug|x64.ActiveCfg = Debug|x64 + {7E449DC2-5334-464D-B478-1715B96C8050}.Debug|x64.Build.0 = Debug|x64 + {7E449DC2-5334-464D-B478-1715B96C8050}.Debug|x86.ActiveCfg = Debug|x86 + {7E449DC2-5334-464D-B478-1715B96C8050}.Debug|x86.Build.0 = Debug|x86 + {7E449DC2-5334-464D-B478-1715B96C8050}.Release|ARM64.ActiveCfg = Release|arm64 + {7E449DC2-5334-464D-B478-1715B96C8050}.Release|ARM64.Build.0 = Release|arm64 + {7E449DC2-5334-464D-B478-1715B96C8050}.Release|x64.ActiveCfg = Release|x64 + {7E449DC2-5334-464D-B478-1715B96C8050}.Release|x64.Build.0 = Release|x64 + {7E449DC2-5334-464D-B478-1715B96C8050}.Release|x86.ActiveCfg = Release|x64 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|ARM64.ActiveCfg = Debug|arm64 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|ARM64.Build.0 = Debug|arm64 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|ARM64.Deploy.0 = Debug|arm64 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|x64.ActiveCfg = Debug|x64 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|x64.Build.0 = Debug|x64 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|x64.Deploy.0 = Debug|x64 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|x86.ActiveCfg = Debug|Win32 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|x86.Build.0 = Debug|Win32 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Debug|x86.Deploy.0 = Debug|Win32 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|ARM64.ActiveCfg = Release|arm64 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|ARM64.Build.0 = Release|arm64 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|ARM64.Deploy.0 = Release|arm64 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|x64.ActiveCfg = Release|x64 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|x64.Build.0 = Release|x64 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|x64.Deploy.0 = Release|x64 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|x86.ActiveCfg = Release|Win32 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|x86.Build.0 = Release|Win32 + {CA5A86BC-C0FB-472D-B1B9-3C5221595606}.Release|x86.Deploy.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {EBDA7BDA-5188-4C95-B3FF-19AADD03A4C1} + EndGlobalSection +EndGlobal diff --git a/src/Samples/AuthoringDemo/AuthoringDemo/AuthoringDemo.csproj b/src/Samples/AuthoringDemo/AuthoringDemo/AuthoringDemo.csproj index ed2c818c0..439b3d69d 100644 --- a/src/Samples/AuthoringDemo/AuthoringDemo/AuthoringDemo.csproj +++ b/src/Samples/AuthoringDemo/AuthoringDemo/AuthoringDemo.csproj @@ -1,7 +1,7 @@  - net6.0-windows10.0.19041.0 + net8.0-windows10.0.19041.0 10.0.17763.0 x64;x86;arm64 @@ -15,7 +15,7 @@ - + @@ -25,4 +25,4 @@ 1.0.0 --> - + \ No newline at end of file diff --git a/src/Samples/AuthoringDemo/CppConsoleApp/CppConsoleApp.vcxproj b/src/Samples/AuthoringDemo/CppConsoleApp/CppConsoleApp.vcxproj index b40268a1e..678d02132 100644 --- a/src/Samples/AuthoringDemo/CppConsoleApp/CppConsoleApp.vcxproj +++ b/src/Samples/AuthoringDemo/CppConsoleApp/CppConsoleApp.vcxproj @@ -1,138 +1,136 @@ - - - - - true - true - true - true - 15.0 - {96752ad7-c344-4d4d-9e0b-39bed88b191a} - Win32Proj - CppConsoleApp - 10.0 - 10.0.17134.0 - - - - - Debug - ARM64 - - - Debug - Win32 - - - Release - ARM64 - - - Release - Win32 - - - Debug - x64 - - - Release - x64 - - - - Application - v143 - Unicode - - - true - true - - - false - true - false - - - - - - - - - - Use - pch.h - $(IntDir)pch.pch - _CONSOLE;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) - Level4 - %(AdditionalOptions) /permissive- /bigobj - - - - - Disabled - _DEBUG;%(PreprocessorDefinitions) - - - Console - false - - - - - WIN32;%(PreprocessorDefinitions) - - - - - MaxSpeed - true - true - NDEBUG;%(PreprocessorDefinitions) - - - Console - true - true - false - - - - - - - - - Create - - - - - - - - true - - - - - {7e449dc2-5334-464d-b478-1715b96c8050} - - - - - - - - - - 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}. - - - - - - + + + + + true + true + true + true + 15.0 + {96752ad7-c344-4d4d-9e0b-39bed88b191a} + Win32Proj + CppConsoleApp + 10.0 + 10.0.17134.0 + + + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + Application + v143 + Unicode + + + true + true + + + false + true + false + + + + + + + + + + Use + pch.h + $(IntDir)pch.pch + _CONSOLE;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) + Level4 + %(AdditionalOptions) /permissive- /bigobj + + + + + Disabled + _DEBUG;%(PreprocessorDefinitions) + + + Console + false + + + + + WIN32;%(PreprocessorDefinitions) + + + + + MaxSpeed + true + true + NDEBUG;%(PreprocessorDefinitions) + + + Console + true + true + false + + + + + + + + + Create + + + + + true + + + + + {7e449dc2-5334-464d-b478-1715b96c8050} + + + + + + + + + + + + + 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/Samples/AuthoringDemo/CppConsoleApp/CppConsoleApp.vcxproj.filters b/src/Samples/AuthoringDemo/CppConsoleApp/CppConsoleApp.vcxproj.filters index 15be01c05..a30a1a71e 100644 --- a/src/Samples/AuthoringDemo/CppConsoleApp/CppConsoleApp.vcxproj.filters +++ b/src/Samples/AuthoringDemo/CppConsoleApp/CppConsoleApp.vcxproj.filters @@ -1,36 +1,36 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms - - - - - Header Files - - - - - Source Files - - - Source Files - - - - - - - - + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + + + Source Files + + + Source Files + + + + + + + + \ No newline at end of file diff --git a/src/Samples/AuthoringDemo/CppConsoleApp/packages.config b/src/Samples/AuthoringDemo/CppConsoleApp/packages.config index 1c90c5bae..f229371b3 100644 --- a/src/Samples/AuthoringDemo/CppConsoleApp/packages.config +++ b/src/Samples/AuthoringDemo/CppConsoleApp/packages.config @@ -1,4 +1,4 @@ - - - + + + \ No newline at end of file diff --git a/src/Samples/AuthoringDemo/README.md b/src/Samples/AuthoringDemo/README.md index 7695f5860..f2d7e0645 100644 --- a/src/Samples/AuthoringDemo/README.md +++ b/src/Samples/AuthoringDemo/README.md @@ -14,7 +14,7 @@ The following apps demonstrate how to consume the C#/WinRT component **Authoring ## Prerequisites -* [.NET 6](https://dotnet.microsoft.com/download/dotnet/6.0) +* [.NET 8](https://dotnet.microsoft.com/download/dotnet/8.0) * Visual Studio 2022 **Note**: This sample can be modified to target .NET 5 and Visual Studio 2019. This involves editing the `TargetFramework` properties to target `net5.0-windows10.0.19041.0`. diff --git a/src/Samples/AuthoringDemo/WinUI3CppApp/WinUI3CppApp.vcxproj b/src/Samples/AuthoringDemo/WinUI3CppApp/WinUI3CppApp.vcxproj index ae5b76a1a..bc9a2ec3e 100644 --- a/src/Samples/AuthoringDemo/WinUI3CppApp/WinUI3CppApp.vcxproj +++ b/src/Samples/AuthoringDemo/WinUI3CppApp/WinUI3CppApp.vcxproj @@ -1,181 +1,199 @@ - - - - - - - true - true - true - {ca5a86bc-c0fb-472d-b1b9-3c5221595606} - WinUI3CppApp - WinUI3CppApp + + + + + + + + + + + true + true + true + {ca5a86bc-c0fb-472d-b1b9-3c5221595606} + WinUI3CppApp + WinUI3CppApp - $(RootNamespace) - en-US - 16.0 - false - true - Windows Store - 10.0 - 10.0 - 10.0.17763.0 - true - true - - - - - Debug - Win32 - - - Debug - x64 - - - Debug - arm64 - - - Release - Win32 - - - Release - x64 - - - Release - arm64 - - - - Application - v143 - Unicode - true - - - true - true - - - false - true - false - - - - - - - - - - - Use - pch.h - $(IntDir)pch.pch - Level4 - %(AdditionalOptions) /bigobj - - - - - _DEBUG;%(PreprocessorDefinitions) - - - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - - - - App.xaml - - - MainWindow.xaml - - - - - - - - - Create - - - App.xaml - - - MainWindow.xaml - - - - - - Code - App.xaml - - - Code - MainWindow.xaml - - - - - - - - - - - - - + --> + $(RootNamespace) + en-US + 16.0 + false + true + Windows Store + 10.0 + 10.0 + 10.0.17763.0 + true + true + + + + + Debug + Win32 + + + Debug + x64 + + + Debug + arm64 + + + Release + Win32 + + + Release + x64 + + + Release + arm64 + + + + Application + v143 + Unicode + true + + + true + true + + + false + true + false + + + + + + + + + + + Use + pch.h + $(IntDir)pch.pch + Level4 + %(AdditionalOptions) /bigobj + + + + + _DEBUG;%(PreprocessorDefinitions) + + + + + NDEBUG;%(PreprocessorDefinitions) + + + true + true + + + + + + + + + App.xaml + + + MainWindow.xaml + + + + + + + + + Create + + + App.xaml + + + MainWindow.xaml + + + + + + Code + App.xaml + + + Code + MainWindow.xaml + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - 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}. - - - - - - - - - + package has not yet been restored --> + + + + + + + + + + + + + + + + + + + + + + + + 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/Samples/AuthoringDemo/WinUI3CppApp/WinUI3CppApp.vcxproj.filters b/src/Samples/AuthoringDemo/WinUI3CppApp/WinUI3CppApp.vcxproj.filters index c76b13126..114a5744b 100644 --- a/src/Samples/AuthoringDemo/WinUI3CppApp/WinUI3CppApp.vcxproj.filters +++ b/src/Samples/AuthoringDemo/WinUI3CppApp/WinUI3CppApp.vcxproj.filters @@ -1,57 +1,57 @@ - - - - - - - - - - - - - - - - - - - - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - Assets - - - - - {ca5a86bc-c0fb-472d-b1b9-3c5221595606} - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + Assets + + + + + {ca5a86bc-c0fb-472d-b1b9-3c5221595606} + + + + + + + + + + + \ No newline at end of file diff --git a/src/Samples/AuthoringDemo/WinUI3CppApp/packages.config b/src/Samples/AuthoringDemo/WinUI3CppApp/packages.config index 7a9818f4d..cde0f5826 100644 --- a/src/Samples/AuthoringDemo/WinUI3CppApp/packages.config +++ b/src/Samples/AuthoringDemo/WinUI3CppApp/packages.config @@ -1,7 +1,12 @@ - - - - - - + + + + + + + + + + + \ No newline at end of file diff --git a/src/Samples/AuthoringDemo/nuget.config b/src/Samples/AuthoringDemo/nuget.config deleted file mode 100644 index 8716547ae..000000000 --- a/src/Samples/AuthoringDemo/nuget.config +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Samples/BgTaskComponent/BgTaskComponent/BgTaskComponent.csproj b/src/Samples/BgTaskComponent/BgTaskComponent/BgTaskComponent.csproj index bc1e09e48..bb9a77778 100644 --- a/src/Samples/BgTaskComponent/BgTaskComponent/BgTaskComponent.csproj +++ b/src/Samples/BgTaskComponent/BgTaskComponent/BgTaskComponent.csproj @@ -1,9 +1,9 @@  - net6.0-windows10.0.19041.0 + net8.0-windows10.0.19041.0 x64;x86 - win10-x86;win10-x64 + win-x86;win-x64 @@ -13,7 +13,7 @@ - + @@ -22,4 +22,4 @@ - + \ No newline at end of file diff --git a/src/Samples/BgTaskComponent/BgTaskComponent/WinRT.Host.runtimeconfig.json b/src/Samples/BgTaskComponent/BgTaskComponent/WinRT.Host.runtimeconfig.json index 2f2c17443..cd3f3d5ba 100644 --- a/src/Samples/BgTaskComponent/BgTaskComponent/WinRT.Host.runtimeconfig.json +++ b/src/Samples/BgTaskComponent/BgTaskComponent/WinRT.Host.runtimeconfig.json @@ -1,10 +1,10 @@ { "runtimeOptions": { - "tfm": "net6.0", + "tfm": "net8.0", "rollForward": "LatestMinor", "framework": { "name": "Microsoft.NETCore.App", - "version": "6.0.0" + "version": "8.0.0" } } } diff --git a/src/Samples/BgTaskComponent/README.md b/src/Samples/BgTaskComponent/README.md index da4a2a16e..dd45ab08c 100644 --- a/src/Samples/BgTaskComponent/README.md +++ b/src/Samples/BgTaskComponent/README.md @@ -14,7 +14,7 @@ This sample includes the following projects: ## Prerequisites -* [.NET 6](https://dotnet.microsoft.com/download/dotnet/6.0) +* [.NET 8](https://dotnet.microsoft.com/download/dotnet/8.0) * Visual Studio 2022 **Note**: This sample can be modified to target .NET 5 and Visual Studio 2019. This involves editing the `TargetFramework` properties to target `net5.0-windows10.0.19041.0`. diff --git a/src/Samples/BgTaskComponent/WpfApp/WpfApp.csproj b/src/Samples/BgTaskComponent/WpfApp/WpfApp.csproj index cfa8cdb19..23fa1cdf1 100644 --- a/src/Samples/BgTaskComponent/WpfApp/WpfApp.csproj +++ b/src/Samples/BgTaskComponent/WpfApp/WpfApp.csproj @@ -2,10 +2,10 @@ WinExe - net6.0-windows10.0.19041.0 + net8.0-windows10.0.19041.0 true x64;x86 - win10-x64;win10-x86 + win-x64;win-x86 diff --git a/src/Samples/NetProjectionSample/ConsoleAppSample/ConsoleAppSample.csproj b/src/Samples/NetProjectionSample/ConsoleAppSample/ConsoleAppSample.csproj index 177be4fec..02cd22f0f 100644 --- a/src/Samples/NetProjectionSample/ConsoleAppSample/ConsoleAppSample.csproj +++ b/src/Samples/NetProjectionSample/ConsoleAppSample/ConsoleAppSample.csproj @@ -2,20 +2,19 @@ Exe - net6.0-windows10.0.19041.0 + net8.0-windows10.0.19041.0 10.0.17763.0 x64;x86;ARM64 - https://api.nuget.org/v3/index.json; ../SimpleMathProjection/nuget - + - + \ No newline at end of file diff --git a/src/Samples/NetProjectionSample/CppWinRTComponentProjectionSample.sln b/src/Samples/NetProjectionSample/CppWinRTComponentProjectionSample.sln index 4bf86b277..83422be2e 100644 --- a/src/Samples/NetProjectionSample/CppWinRTComponentProjectionSample.sln +++ b/src/Samples/NetProjectionSample/CppWinRTComponentProjectionSample.sln @@ -1,56 +1,57 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.1.31911.260 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleMathProjection", "SimpleMathProjection\SimpleMathProjection.csproj", "{3D562B38-115F-481C-ADE2-5A60EFBA1ED0}" -EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimpleMathComponent", "SimpleMathComponent\SimpleMathComponent.vcxproj", "{D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{47FC5375-776F-4922-81DE-74E1A345F35F}" - ProjectSection(SolutionItems) = preProject - Directory.Build.props = Directory.Build.props - Directory.Build.targets = Directory.Build.targets - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|ARM64 = Debug|ARM64 - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|ARM64 = Release|ARM64 - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Debug|ARM64.Build.0 = Debug|ARM64 - {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Debug|x64.ActiveCfg = Debug|x64 - {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Debug|x64.Build.0 = Debug|x64 - {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Debug|x86.ActiveCfg = Debug|x64 - {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Release|ARM64.ActiveCfg = Release|Any CPU - {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Release|ARM64.Build.0 = Release|Any CPU - {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Release|x64.ActiveCfg = Release|Any CPU - {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Release|x64.Build.0 = Release|Any CPU - {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Release|x86.ActiveCfg = Release|Any CPU - {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Release|x86.Build.0 = Release|Any CPU - {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Debug|ARM64.Build.0 = Debug|ARM64 - {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Debug|x64.ActiveCfg = Debug|x64 - {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Debug|x64.Build.0 = Debug|x64 - {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Debug|x86.ActiveCfg = Debug|Win32 - {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Debug|x86.Build.0 = Debug|Win32 - {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Release|ARM64.ActiveCfg = Release|ARM64 - {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Release|ARM64.Build.0 = Release|ARM64 - {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Release|x64.ActiveCfg = Release|x64 - {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Release|x64.Build.0 = Release|x64 - {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Release|x86.ActiveCfg = Release|Win32 - {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Release|x86.Build.0 = Release|Win32 - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {2E0D131C-0B10-4500-939B-D58EC3871871} - EndGlobalSection -EndGlobal + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.31911.260 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleMathProjection", "SimpleMathProjection\SimpleMathProjection.csproj", "{3D562B38-115F-481C-ADE2-5A60EFBA1ED0}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SimpleMathComponent", "SimpleMathComponent\SimpleMathComponent.vcxproj", "{D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{47FC5375-776F-4922-81DE-74E1A345F35F}" + ProjectSection(SolutionItems) = preProject + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|ARM64 = Debug|ARM64 + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|ARM64 = Release|ARM64 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Debug|ARM64.Build.0 = Debug|Any CPU + {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Debug|x64.ActiveCfg = Debug|Any CPU + {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Debug|x64.Build.0 = Debug|Any CPU + {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Debug|x86.ActiveCfg = Debug|Any CPU + {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Debug|x86.Build.0 = Debug|Any CPU + {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Release|ARM64.ActiveCfg = Release|Any CPU + {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Release|ARM64.Build.0 = Release|Any CPU + {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Release|x64.ActiveCfg = Release|Any CPU + {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Release|x64.Build.0 = Release|Any CPU + {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Release|x86.ActiveCfg = Release|Any CPU + {3D562B38-115F-481C-ADE2-5A60EFBA1ED0}.Release|x86.Build.0 = Release|Any CPU + {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Debug|ARM64.Build.0 = Debug|ARM64 + {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Debug|x64.ActiveCfg = Debug|x64 + {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Debug|x64.Build.0 = Debug|x64 + {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Debug|x86.ActiveCfg = Debug|Win32 + {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Debug|x86.Build.0 = Debug|Win32 + {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Release|ARM64.ActiveCfg = Release|ARM64 + {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Release|ARM64.Build.0 = Release|ARM64 + {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Release|x64.ActiveCfg = Release|x64 + {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Release|x64.Build.0 = Release|x64 + {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Release|x86.ActiveCfg = Release|Win32 + {D6B0C16D-858A-4C1B-99CF-D6F4CF5BCD5F}.Release|x86.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {2E0D131C-0B10-4500-939B-D58EC3871871} + EndGlobalSection +EndGlobal diff --git a/src/Samples/NetProjectionSample/README.md b/src/Samples/NetProjectionSample/README.md index 9ecb94551..fa63be7c8 100644 --- a/src/Samples/NetProjectionSample/README.md +++ b/src/Samples/NetProjectionSample/README.md @@ -9,7 +9,7 @@ This sample demonstrates how to do the following: ## Requirements * [Visual Studio 2022](https://visualstudio.microsoft.com/downloads/) with the Universal Windows Platform development workload installed. In **Installation Details** > **Universal Windows Platform development**, check the **C++ (v14x) Universal Windows Platform tools** option. -* [.NET 6 SDK](https://dotnet.microsoft.com/download/dotnet/6.0) +* [.NET 8 SDK](https://dotnet.microsoft.com/download/dotnet/8.0) * nuget.exe 5.8.0-preview.2 or later (for command line MSBuild) **Note**: This sample uses .NET 6 and therefore requires Visual Studio 2022 to build and run. If you prefer, you can use Visual Studio 2019 and modify the sample to target [.NET 5](https://dotnet.microsoft.com/download/dotnet/5.0). To do this, you will need to modify the `TargetFramework` and the *nuspec* file in the `SimpleMathProjection` project to target `net5.0-windows10.0.19041.0`. diff --git a/src/Samples/NetProjectionSample/SimpleMathComponent/SimpleMathComponent.vcxproj b/src/Samples/NetProjectionSample/SimpleMathComponent/SimpleMathComponent.vcxproj index 3ba58c27c..22cf473e2 100644 --- a/src/Samples/NetProjectionSample/SimpleMathComponent/SimpleMathComponent.vcxproj +++ b/src/Samples/NetProjectionSample/SimpleMathComponent/SimpleMathComponent.vcxproj @@ -1,157 +1,157 @@ - - - - - true - true - true - true - {d6b0c16d-858a-4c1b-99cf-d6f4cf5bcd5f} - SimpleMathComponent - SimpleMathComponent - en-US - 14.0 - true - Windows Store - 10.0 - 10.0 - 10.0.17763.0 - - - - - Debug - ARM64 - - - Debug - Win32 - - - Debug - x64 - - - Release - ARM64 - - - Release - Win32 - - - Release - x64 - - - - DynamicLibrary - v143 - Unicode - false - - - true - true - - - false - true - false - - - true - - - true - - - true - - - true - - - true - - - true - - - - - - - - - - - - - - Use - pch.h - $(IntDir)pch.pch - Level4 - %(AdditionalOptions) /bigobj - - /DWINRT_NO_MAKE_DETECTION %(AdditionalOptions) - - - _WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) - $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) - - - Console - false - SimpleMathComponent.def - - - - - _DEBUG;%(PreprocessorDefinitions) - - - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - SimpleMath.idl - - - - - Create - - - SimpleMath.idl - - - - - - - - - - - - - - - - - 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}. - - - - + + + + + true + true + true + true + {d6b0c16d-858a-4c1b-99cf-d6f4cf5bcd5f} + SimpleMathComponent + SimpleMathComponent + en-US + 14.0 + true + Windows Store + 10.0 + 10.0 + 10.0.17763.0 + + + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + DynamicLibrary + v143 + Unicode + false + + + true + true + + + false + true + false + + + true + + + true + + + true + + + true + + + true + + + true + + + + + + + + + + + + + + Use + pch.h + $(IntDir)pch.pch + Level4 + %(AdditionalOptions) /bigobj + + /DWINRT_NO_MAKE_DETECTION %(AdditionalOptions) + + + _WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + + + Console + false + SimpleMathComponent.def + + + + + _DEBUG;%(PreprocessorDefinitions) + + + + + NDEBUG;%(PreprocessorDefinitions) + + + true + true + + + + + + SimpleMath.idl + + + + + Create + + + SimpleMath.idl + + + + + + + + + + + + + + + + + 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/Samples/NetProjectionSample/SimpleMathComponent/SimpleMathComponent.vcxproj.filters b/src/Samples/NetProjectionSample/SimpleMathComponent/SimpleMathComponent.vcxproj.filters index da3329bd5..1a5f62c74 100644 --- a/src/Samples/NetProjectionSample/SimpleMathComponent/SimpleMathComponent.vcxproj.filters +++ b/src/Samples/NetProjectionSample/SimpleMathComponent/SimpleMathComponent.vcxproj.filters @@ -1,26 +1,26 @@ - - - - - accd3aa8-1ba0-4223-9bbe-0c431709210b - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms - - - {926ab91d-31b4-48c3-b9a4-e681349f27f0} - - - - - - - - - - - - - - - - + + + + + accd3aa8-1ba0-4223-9bbe-0c431709210b + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms + + + {926ab91d-31b4-48c3-b9a4-e681349f27f0} + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Samples/NetProjectionSample/SimpleMathComponent/packages.config b/src/Samples/NetProjectionSample/SimpleMathComponent/packages.config index 16e10b47b..f229371b3 100644 --- a/src/Samples/NetProjectionSample/SimpleMathComponent/packages.config +++ b/src/Samples/NetProjectionSample/SimpleMathComponent/packages.config @@ -1,4 +1,4 @@ - - - + + + \ No newline at end of file diff --git a/src/Samples/NetProjectionSample/SimpleMathProjection/SimpleMathProjection.csproj b/src/Samples/NetProjectionSample/SimpleMathProjection/SimpleMathProjection.csproj index ab9be6053..97fb96c45 100644 --- a/src/Samples/NetProjectionSample/SimpleMathProjection/SimpleMathProjection.csproj +++ b/src/Samples/NetProjectionSample/SimpleMathProjection/SimpleMathProjection.csproj @@ -1,7 +1,7 @@  - net6.0-windows10.0.19041.0 + net8.0-windows10.0.19041.0 10.0.17763.0 AnyCPU @@ -11,7 +11,7 @@ - + @@ -32,4 +32,4 @@ true - + \ No newline at end of file diff --git a/src/Samples/NetProjectionSample/SimpleMathProjection/nuget/SimpleMathProjection.nuspec b/src/Samples/NetProjectionSample/SimpleMathProjection/nuget/SimpleMathProjection.nuspec index 58c1d02e6..33c2a9962 100644 --- a/src/Samples/NetProjectionSample/SimpleMathProjection/nuget/SimpleMathProjection.nuspec +++ b/src/Samples/NetProjectionSample/SimpleMathProjection/nuget/SimpleMathProjection.nuspec @@ -6,17 +6,16 @@ Contoso Math Inc. A simple component with basic math operations - - + - + - + - - + + \ No newline at end of file diff --git a/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.cpp b/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.cpp index 365fcd59f..0a788a9bd 100644 --- a/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.cpp +++ b/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.cpp @@ -4,4 +4,24 @@ namespace winrt::Alpha::implementation { + template > + Windows::Foundation::Collections::IVectorView single_threaded_vector_view(std::vector&& values = {}) + { + return make>>(std::move(values)); + } + + winrt::Windows::Foundation::Collections::IVector Class::GetStringList() + { + return winrt::single_threaded_vector({ L"alpha", L"beta" }); + } + + winrt::Windows::Foundation::Collections::IVector Class::GetIntList() + { + return winrt::single_threaded_vector({ 4, 3, 2, 1 }); + } + + winrt::Windows::Foundation::Collections::IVectorView Class::GetObjectList() + { + return single_threaded_vector_view({ *this, *this }); + } } diff --git a/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.h b/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.h index c1fe55067..9a43c81af 100644 --- a/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.h +++ b/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.h @@ -7,6 +7,10 @@ namespace winrt::Alpha::implementation struct Class : ClassT { Class() = default; + + winrt::Windows::Foundation::Collections::IVector GetStringList(); + winrt::Windows::Foundation::Collections::IVector GetIntList(); + winrt::Windows::Foundation::Collections::IVectorView GetObjectList(); }; } diff --git a/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.idl b/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.idl index 5f438a79d..dba92fe07 100644 --- a/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.idl +++ b/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.idl @@ -4,6 +4,10 @@ namespace Alpha runtimeclass Class { Class(); + + Windows.Foundation.Collections.IVector GetStringList(); + Windows.Foundation.Collections.IVector GetIntList(); + Windows.Foundation.Collections.IVectorView GetObjectList(); } interface IAlpha diff --git a/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.vcxproj b/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.vcxproj index 5c54ea6bf..49dc6676e 100644 --- a/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.vcxproj +++ b/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.vcxproj @@ -1,153 +1,151 @@ - - - - - true - true - true - true - {9d73f8c2-332b-4aa9-95e3-59c92e68efb4} - Alpha - Alpha - en-US - 14.0 - true - Windows Store - 10.0 - 10.0 - 10.0.17134.0 - - - - - Debug - ARM - - - Debug - ARM64 - - - Debug - Win32 - - - Debug - x64 - - - Release - ARM - - - Release - ARM64 - - - Release - Win32 - - - Release - x64 - - - - DynamicLibrary - v143 - v142 - v141 - v140 - Unicode - false - - - true - true - - - false - true - false - - - true - - - - - - - - - - - - - - Use - pch.h - $(IntDir)pch.pch - Level4 - %(AdditionalOptions) /bigobj - - /DWINRT_NO_MAKE_DETECTION %(AdditionalOptions) - _WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) - $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) - - - Console - false - Alpha.def - - - - - _DEBUG;%(PreprocessorDefinitions) - - - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - Alpha.idl - - - - - Create - - - Alpha.idl - - - - - - - - - - - - - - - - - - - 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}. - - - - + + + + + true + true + true + true + {9d73f8c2-332b-4aa9-95e3-59c92e68efb4} + Alpha + Alpha + en-US + 14.0 + true + Windows Store + 10.0 + 10.0 + 10.0.17134.0 + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + DynamicLibrary + v143 + v142 + v141 + v140 + Unicode + false + + + true + true + + + false + true + false + + + true + + + + + + + + + + + + + + Use + pch.h + $(IntDir)pch.pch + Level4 + %(AdditionalOptions) /bigobj + + /DWINRT_NO_MAKE_DETECTION %(AdditionalOptions) + _WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + + + Console + false + Alpha.def + + + + + _DEBUG;%(PreprocessorDefinitions) + + + + + NDEBUG;%(PreprocessorDefinitions) + + + true + true + + + + + + Alpha.idl + + + + + Create + + + Alpha.idl + + + + + + + + + + + + + + + + + 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/Samples/TestEmbedded/C++ Components/Alpha/Alpha.vcxproj.filters b/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.vcxproj.filters index 8fcbd9897..db7185dd3 100644 --- a/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.vcxproj.filters +++ b/src/Samples/TestEmbedded/C++ Components/Alpha/Alpha.vcxproj.filters @@ -1,25 +1,25 @@ - - - - - accd3aa8-1ba0-4223-9bbe-0c431709210b - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms - - - - - - - - - - - - - - - - - - + + + + + accd3aa8-1ba0-4223-9bbe-0c431709210b + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Samples/TestEmbedded/C++ Components/Alpha/packages.config b/src/Samples/TestEmbedded/C++ Components/Alpha/packages.config index fb20a4e61..f229371b3 100644 --- a/src/Samples/TestEmbedded/C++ Components/Alpha/packages.config +++ b/src/Samples/TestEmbedded/C++ Components/Alpha/packages.config @@ -1,4 +1,4 @@ - - - + + + \ No newline at end of file diff --git a/src/Samples/TestEmbedded/C++ Components/Beta/Beta.vcxproj b/src/Samples/TestEmbedded/C++ Components/Beta/Beta.vcxproj index b6eadcfc1..8f851fdbd 100644 --- a/src/Samples/TestEmbedded/C++ Components/Beta/Beta.vcxproj +++ b/src/Samples/TestEmbedded/C++ Components/Beta/Beta.vcxproj @@ -1,163 +1,161 @@ - - - - - true - true - true - true - {e1c44b28-965c-45ab-bcb4-475b627e32a6} - Beta - Beta - en-US - 14.0 - true - Windows Store - 10.0 - 10.0 - 10.0.17134.0 - - - - - Debug - ARM - - - Debug - ARM64 - - - Debug - Win32 - - - Debug - x64 - - - Release - ARM - - - Release - ARM64 - - - Release - Win32 - - - Release - x64 - - - - DynamicLibrary - v143 - v142 - v141 - v140 - Unicode - false - - - true - true - - - false - true - false - - - true - - - - - - - - - - - - - - Use - pch.h - $(IntDir)pch.pch - Level4 - %(AdditionalOptions) /bigobj - - /DWINRT_NO_MAKE_DETECTION %(AdditionalOptions) - _WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) - $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) - - - Console - false - Beta.def - - - - - _DEBUG;%(PreprocessorDefinitions) - - - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - Beta.idl - - - - - - Create - - - Beta.idl - - - - - - - - - - - - - - - - {9d73f8c2-332b-4aa9-95e3-59c92e68efb4} - - - {ddb1a216-1527-4f49-8729-17a6519659c8} - - - - - - - - - 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}. - - - - + + + + + true + true + true + true + {e1c44b28-965c-45ab-bcb4-475b627e32a6} + Beta + Beta + en-US + 14.0 + true + Windows Store + 10.0 + 10.0 + 10.0.17134.0 + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + DynamicLibrary + v143 + v142 + v141 + v140 + Unicode + false + + + true + true + + + false + true + false + + + true + + + + + + + + + + + + + + Use + pch.h + $(IntDir)pch.pch + Level4 + %(AdditionalOptions) /bigobj + + /DWINRT_NO_MAKE_DETECTION %(AdditionalOptions) + _WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + + + Console + false + Beta.def + + + + + _DEBUG;%(PreprocessorDefinitions) + + + + + NDEBUG;%(PreprocessorDefinitions) + + + true + true + + + + + + Beta.idl + + + + + + Create + + + Beta.idl + + + + + + + + + + + + + + {9d73f8c2-332b-4aa9-95e3-59c92e68efb4} + + + {ddb1a216-1527-4f49-8729-17a6519659c8} + + + + + + + + + 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/Samples/TestEmbedded/C++ Components/Beta/Beta.vcxproj.filters b/src/Samples/TestEmbedded/C++ Components/Beta/Beta.vcxproj.filters index 6bbd0a011..6c967b413 100644 --- a/src/Samples/TestEmbedded/C++ Components/Beta/Beta.vcxproj.filters +++ b/src/Samples/TestEmbedded/C++ Components/Beta/Beta.vcxproj.filters @@ -1,30 +1,30 @@ - - - - - accd3aa8-1ba0-4223-9bbe-0c431709210b - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms - - - {926ab91d-31b4-48c3-b9a4-e681349f27f0} - - - - - - - - - - - - - - - - - - - - + + + + + accd3aa8-1ba0-4223-9bbe-0c431709210b + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms + + + {926ab91d-31b4-48c3-b9a4-e681349f27f0} + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Samples/TestEmbedded/C++ Components/Beta/packages.config b/src/Samples/TestEmbedded/C++ Components/Beta/packages.config index fb20a4e61..f229371b3 100644 --- a/src/Samples/TestEmbedded/C++ Components/Beta/packages.config +++ b/src/Samples/TestEmbedded/C++ Components/Beta/packages.config @@ -1,4 +1,4 @@ - - - + + + \ No newline at end of file diff --git a/src/Samples/TestEmbedded/C++ Components/Directory.Build.props b/src/Samples/TestEmbedded/C++ Components/Directory.Build.props index 9851da811..0dc8de394 100644 --- a/src/Samples/TestEmbedded/C++ Components/Directory.Build.props +++ b/src/Samples/TestEmbedded/C++ Components/Directory.Build.props @@ -1,2 +1,21 @@ - \ No newline at end of file + + + + true + Guard + + + %(AdditionalOptions) /Qspectre /ZH:SHA_256 + + + + + + %(AdditionalOptions) /Brepro /PDBALTPATH:$(TargetName).pdb + %(AdditionalOptions) /Brepro /PDBALTPATH:$(TargetName).pdb /CETCOMPAT + + + + diff --git a/src/Samples/TestEmbedded/C++ Components/Gamma/Gamma.vcxproj b/src/Samples/TestEmbedded/C++ Components/Gamma/Gamma.vcxproj index 5629eab1b..a7543107a 100644 --- a/src/Samples/TestEmbedded/C++ Components/Gamma/Gamma.vcxproj +++ b/src/Samples/TestEmbedded/C++ Components/Gamma/Gamma.vcxproj @@ -1,153 +1,151 @@ - - - - - true - true - true - true - {ddb1a216-1527-4f49-8729-17a6519659c8} - Gamma - Gamma - en-US - 14.0 - true - Windows Store - 10.0 - 10.0 - 10.0.17134.0 - - - - - Debug - ARM - - - Debug - ARM64 - - - Debug - Win32 - - - Debug - x64 - - - Release - ARM - - - Release - ARM64 - - - Release - Win32 - - - Release - x64 - - - - DynamicLibrary - v143 - v142 - v141 - v140 - Unicode - false - - - true - true - - - false - true - false - - - true - - - - - - - - - - - - - - Use - pch.h - $(IntDir)pch.pch - Level4 - %(AdditionalOptions) /bigobj - - /DWINRT_NO_MAKE_DETECTION %(AdditionalOptions) - _WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) - $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) - - - Console - false - Gamma.def - - - - - _DEBUG;%(PreprocessorDefinitions) - - - - - NDEBUG;%(PreprocessorDefinitions) - - - true - true - - - - - - Gamma.idl - - - - - Create - - - Gamma.idl - - - - - - - - - - - - - - - - - - - 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}. - - - - + + + + + true + true + true + true + {ddb1a216-1527-4f49-8729-17a6519659c8} + Gamma + Gamma + en-US + 14.0 + true + Windows Store + 10.0 + 10.0 + 10.0.17134.0 + + + + + Debug + ARM + + + Debug + ARM64 + + + Debug + Win32 + + + Debug + x64 + + + Release + ARM + + + Release + ARM64 + + + Release + Win32 + + + Release + x64 + + + + DynamicLibrary + v143 + v142 + v141 + v140 + Unicode + false + + + true + true + + + false + true + false + + + true + + + + + + + + + + + + + + Use + pch.h + $(IntDir)pch.pch + Level4 + %(AdditionalOptions) /bigobj + + /DWINRT_NO_MAKE_DETECTION %(AdditionalOptions) + _WINRT_DLL;WIN32_LEAN_AND_MEAN;WINRT_LEAN_AND_MEAN;%(PreprocessorDefinitions) + $(WindowsSDK_WindowsMetadata);$(AdditionalUsingDirectories) + + + Console + false + Gamma.def + + + + + _DEBUG;%(PreprocessorDefinitions) + + + + + NDEBUG;%(PreprocessorDefinitions) + + + true + true + + + + + + Gamma.idl + + + + + Create + + + Gamma.idl + + + + + + + + + + + + + + + + + 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/Samples/TestEmbedded/C++ Components/Gamma/Gamma.vcxproj.filters b/src/Samples/TestEmbedded/C++ Components/Gamma/Gamma.vcxproj.filters index dbbb5df72..f7aa3ee73 100644 --- a/src/Samples/TestEmbedded/C++ Components/Gamma/Gamma.vcxproj.filters +++ b/src/Samples/TestEmbedded/C++ Components/Gamma/Gamma.vcxproj.filters @@ -1,28 +1,28 @@ - - - - - accd3aa8-1ba0-4223-9bbe-0c431709210b - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms - - - {926ab91d-31b4-48c3-b9a4-e681349f27f0} - - - - - - - - - - - - - - - - - - + + + + + accd3aa8-1ba0-4223-9bbe-0c431709210b + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tga;tiff;tif;png;wav;mfcribbon-ms + + + {926ab91d-31b4-48c3-b9a4-e681349f27f0} + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Samples/TestEmbedded/C++ Components/Gamma/packages.config b/src/Samples/TestEmbedded/C++ Components/Gamma/packages.config index fb20a4e61..f229371b3 100644 --- a/src/Samples/TestEmbedded/C++ Components/Gamma/packages.config +++ b/src/Samples/TestEmbedded/C++ Components/Gamma/packages.config @@ -1,4 +1,4 @@ - - - + + + \ No newline at end of file diff --git a/src/Samples/TestEmbedded/Net5App.Bootstrap/Net5App.Bootstrap.csproj b/src/Samples/TestEmbedded/Net8App.Bootstrap/Net8App.Bootstrap.csproj similarity index 85% rename from src/Samples/TestEmbedded/Net5App.Bootstrap/Net5App.Bootstrap.csproj rename to src/Samples/TestEmbedded/Net8App.Bootstrap/Net8App.Bootstrap.csproj index 8595d924b..938369a73 100644 --- a/src/Samples/TestEmbedded/Net5App.Bootstrap/Net5App.Bootstrap.csproj +++ b/src/Samples/TestEmbedded/Net8App.Bootstrap/Net8App.Bootstrap.csproj @@ -1,7 +1,7 @@  Exe - net5.0-windows + net8.0-windows x64;x86 @@ -20,8 +20,8 @@ - - + + @@ -41,4 +41,4 @@ Windows.Foundation.PropertyType; - + \ No newline at end of file diff --git a/src/Samples/TestEmbedded/Net5App.Bootstrap/Program.cs b/src/Samples/TestEmbedded/Net8App.Bootstrap/Program.cs similarity index 98% rename from src/Samples/TestEmbedded/Net5App.Bootstrap/Program.cs rename to src/Samples/TestEmbedded/Net8App.Bootstrap/Program.cs index bd9872f5b..3c0bd8abe 100644 --- a/src/Samples/TestEmbedded/Net5App.Bootstrap/Program.cs +++ b/src/Samples/TestEmbedded/Net8App.Bootstrap/Program.cs @@ -4,7 +4,7 @@ using Gamma; using Windows.Media; -namespace Net5App.Bootstrap +namespace Net8App.Bootstrap { class Program { diff --git a/src/Samples/TestEmbedded/Net5App/Net5App.csproj b/src/Samples/TestEmbedded/Net8App/Net8App.csproj similarity index 88% rename from src/Samples/TestEmbedded/Net5App/Net5App.csproj rename to src/Samples/TestEmbedded/Net8App/Net8App.csproj index 87718352b..bf1387bde 100644 --- a/src/Samples/TestEmbedded/Net5App/Net5App.csproj +++ b/src/Samples/TestEmbedded/Net8App/Net8App.csproj @@ -2,7 +2,7 @@ Exe x64;x86 - net5.0-windows + net8.0-windows true sdk diff --git a/src/Samples/TestEmbedded/Net5App/Program.cs b/src/Samples/TestEmbedded/Net8App/Program.cs similarity index 100% rename from src/Samples/TestEmbedded/Net5App/Program.cs rename to src/Samples/TestEmbedded/Net8App/Program.cs diff --git a/src/Samples/TestEmbedded/Net5App/Properties/launchSettings.json b/src/Samples/TestEmbedded/Net8App/Properties/launchSettings.json similarity index 84% rename from src/Samples/TestEmbedded/Net5App/Properties/launchSettings.json rename to src/Samples/TestEmbedded/Net8App/Properties/launchSettings.json index 09a2fcb65..ada322420 100644 --- a/src/Samples/TestEmbedded/Net5App/Properties/launchSettings.json +++ b/src/Samples/TestEmbedded/Net8App/Properties/launchSettings.json @@ -1,6 +1,6 @@ { "profiles": { - "Net5App": { + "Net8App": { "commandName": "Project", "nativeDebugging": true } diff --git a/src/Samples/TestEmbedded/NetFrameworkApp/NetFrameworkApp.csproj b/src/Samples/TestEmbedded/NetFrameworkApp/NetFrameworkApp.csproj index b46eae9d9..c5232d9c4 100644 --- a/src/Samples/TestEmbedded/NetFrameworkApp/NetFrameworkApp.csproj +++ b/src/Samples/TestEmbedded/NetFrameworkApp/NetFrameworkApp.csproj @@ -3,7 +3,7 @@ Exe net47 x64;x86 - win7-x64;win7-x86 + win-x64;win-x86 @@ -15,8 +15,8 @@ - - + + @@ -42,4 +42,4 @@ Windows.Foundation.PropertyType; - + \ No newline at end of file diff --git a/src/Samples/TestEmbedded/README.md b/src/Samples/TestEmbedded/README.md index 8d6b63f38..043dd52ea 100644 --- a/src/Samples/TestEmbedded/README.md +++ b/src/Samples/TestEmbedded/README.md @@ -9,8 +9,8 @@ This sample is composed of a few projects to demonstrate C#/WinRT embedded suppo - C++/WinRT component projects: *Alpha*, *Beta*, *Gamma* - *TestEmbeddedLibrary*: a C# library project using C#/WinRT embedded support to embed the WinRT.Runtime and Windows SDK sources into the projection/library output. - C#/.NET apps of various flavors that use the embedded projection (either by referencing *TestEmbeddedLibrary*, or by referencing the C++/WinRT components and generating the projection directly in the app project): - - *Net5App*: References *TestEmbeddedLibrary* to consume the embedded projection. - - *Net5App.Bootstrap*: References *Alpha*, *Beta*, and *Gamma* and creates an embeddded projection in the app itself. + - *Net8App*: References *TestEmbeddedLibrary* to consume the embedded projection. + - *Net8App.Bootstrap*: References *Alpha*, *Beta*, and *Gamma* and creates an embeddded projection in the app itself. - *NetCore3App*: References *TestEmbeddedLibrary* to consume the embedded projection. - *NetFrameworkApp*: References *Alpha*, *Beta*, and *Gamma* and creates an embeddded projection in the app itself. @@ -18,11 +18,11 @@ Looking at the C#/WinRT embedded projection, `TestEmbeddedLibrary`, we see it ta demonstrating support for the latest Windows SDK on all platforms. ```xml - net6.0-windows;net5.0-windows;netstandard2.0;net48 + net8.0-windows;netstandard2.0;net48 ``` -Of the apps, `Net5App` and `NetCore3App` reference the same C#/WinRT embedded projection (*TestEmbeddedLibrary*) to make use of WinRT APIs -like `Windows.Devices.Geolocation`. The other two apps, `Net5App.Bootstrap` and `NetFrameworkApp`, demonstrate how an app can generate and embed the projection itself using a package reference to C#/WinRT. +Of the apps, `Net8App` and `NetCore3App` reference the same C#/WinRT embedded projection (*TestEmbeddedLibrary*) to make use of WinRT APIs +like `Windows.Devices.Geolocation`. The other two apps, `Net8App.Bootstrap` and `NetFrameworkApp`, demonstrate how an app can generate and embed the projection itself using a package reference to C#/WinRT. ## Build and run the sample diff --git a/src/Samples/TestEmbedded/TestEmbedded.sln b/src/Samples/TestEmbedded/TestEmbedded.sln index da94ca9ab..c89438839 100644 --- a/src/Samples/TestEmbedded/TestEmbedded.sln +++ b/src/Samples/TestEmbedded/TestEmbedded.sln @@ -24,7 +24,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution README.md = README.md EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Net5App", "Net5App\Net5App.csproj", "{1FD2F1DA-ACF3-408F-BD13-9D1BA24833E0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Net8App", "Net8App\Net8App.csproj", "{1FD2F1DA-ACF3-408F-BD13-9D1BA24833E0}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetCore3App", "NetCore3App\NetCore3App.csproj", "{9B147973-329B-4331-8207-B6832696D01A}" EndProject @@ -32,7 +32,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTestEmbedded", "UnitTes EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetFrameworkApp", "NetFrameworkApp\NetFrameworkApp.csproj", "{6A457D15-4080-4CCA-9980-D418C2EE2BDF}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Net5App.Bootstrap", "Net5App.Bootstrap\Net5App.Bootstrap.csproj", "{D2D4B83C-1E8B-44F0-BCC3-217B6D9141FB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Net8App.Bootstrap", "Net8App.Bootstrap\Net8App.Bootstrap.csproj", "{D2D4B83C-1E8B-44F0-BCC3-217B6D9141FB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/src/Samples/TestEmbedded/TestEmbeddedLibrary/TestEmbeddedLibrary.csproj b/src/Samples/TestEmbedded/TestEmbeddedLibrary/TestEmbeddedLibrary.csproj index 864ae677b..ad8459ed4 100644 --- a/src/Samples/TestEmbedded/TestEmbeddedLibrary/TestEmbeddedLibrary.csproj +++ b/src/Samples/TestEmbedded/TestEmbeddedLibrary/TestEmbeddedLibrary.csproj @@ -1,7 +1,7 @@  - net6.0-windows;net5.0-windows;netstandard2.0;net48 + net8.0-windows;netstandard2.0;net48 x64;x86 @@ -23,8 +23,8 @@ - - + + @@ -44,4 +44,4 @@ Windows.Foundation.PropertyType; - + \ No newline at end of file diff --git a/src/Samples/TestEmbedded/TestEmbeddedLibrary/TestLib.cs b/src/Samples/TestEmbedded/TestEmbeddedLibrary/TestLib.cs index 652a080f8..908f900c9 100644 --- a/src/Samples/TestEmbedded/TestEmbeddedLibrary/TestLib.cs +++ b/src/Samples/TestEmbedded/TestEmbeddedLibrary/TestLib.cs @@ -69,6 +69,48 @@ public int Test5() { return (int)abuff.Capacity; } + } + + public int Test6() + { + Alpha.Class a = new(); + + int success = 0; + var stringList = a.GetStringList(); + if (stringList.Count == 2) + { + success++; + } + + if (stringList[0] == "alpha" && stringList[1] == "beta") + { + success++; + } + + var intList = a.GetIntList(); + if (intList.Count == 4) + { + success++; + } + + int sum = 0; + foreach (var i in intList) + { + sum += i; + } + + if (sum == 10) + { + success++; + } + + var objList = a.GetObjectList(); + if ((objList[0] == objList[1])) + { + success++; + } + + return success; } } diff --git a/src/Samples/TestEmbedded/UnitTestEmbedded/TestClass.cs b/src/Samples/TestEmbedded/UnitTestEmbedded/TestClass.cs index 2cd3e6696..bd27da89b 100644 --- a/src/Samples/TestEmbedded/UnitTestEmbedded/TestClass.cs +++ b/src/Samples/TestEmbedded/UnitTestEmbedded/TestClass.cs @@ -37,6 +37,12 @@ public void Test4() public void Test5() { Assert.Equal(20, TestLib.Test5()); + } + + [Fact] + public void Test6() + { + Assert.Equal(5, TestLib.Test6()); } } } diff --git a/src/Samples/TestEmbedded/UnitTestEmbedded/UnitTestEmbedded.csproj b/src/Samples/TestEmbedded/UnitTestEmbedded/UnitTestEmbedded.csproj index abee87300..b69437407 100644 --- a/src/Samples/TestEmbedded/UnitTestEmbedded/UnitTestEmbedded.csproj +++ b/src/Samples/TestEmbedded/UnitTestEmbedded/UnitTestEmbedded.csproj @@ -1,7 +1,7 @@  - netcoreapp3.1;net5.0;net6.0 + netcoreapp3.1;net8.0 x64;x86 false true @@ -11,27 +11,27 @@ - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + - + \ No newline at end of file diff --git a/src/Samples/TestEmbedded/build.cmd b/src/Samples/TestEmbedded/build.cmd index 87520aed5..6f5ea088f 100644 --- a/src/Samples/TestEmbedded/build.cmd +++ b/src/Samples/TestEmbedded/build.cmd @@ -3,6 +3,10 @@ if /i "%echo_build%" == "on" @echo on set this_dir=%~dp0 +set DOTNET_ROOT=%LocalAppData%\Microsoft\dotnet +set DOTNET_ROOT(x86)=%LocalAppData%\Microsoft\dotnet\x86 +set path=%DOTNET_ROOT%;%DOTNET_ROOT(x86)%;%path% + :params set build_platform=%1 set build_configuration=%2 @@ -39,7 +43,7 @@ if "%only_build%"=="true" goto :eof :test rem Build/Run xUnit tests, generating xml output report for Azure Devops reporting, via XunitXml.TestLogger NuGet if %build_platform%==x86 ( - set dotnet_exe="%DOTNET_ROOT(86)%\dotnet.exe" + set dotnet_exe="%DOTNET_ROOT(x86)%\dotnet.exe" ) else ( set dotnet_exe="%DOTNET_ROOT%\dotnet.exe" ) diff --git a/src/Samples/TestEmbedded/clean.cmd b/src/Samples/TestEmbedded/clean.cmd index ee68aeb16..94b5d64c4 100644 --- a/src/Samples/TestEmbedded/clean.cmd +++ b/src/Samples/TestEmbedded/clean.cmd @@ -14,8 +14,8 @@ rd /q/s "%this_dir%\C++ Components\Gamma\Generated Files" rd /q/s "%this_dir%\C++ Components\Gamma\x64" rd /q/s "%this_dir%\C++ Components\Gamma\x86" -rd /q/s "%this_dir%\Net5App\bin" -rd /q/s "%this_dir%\Net5App\obj" +rd /q/s "%this_dir%\Net8App\bin" +rd /q/s "%this_dir%\Net8App\obj" rd /q/s "%this_dir%\NetCore3App\bin" rd /q/s "%this_dir%\NetCore3App\obj" diff --git a/src/Samples/WinUIDesktopSample/WinUIDesktopSample.csproj b/src/Samples/WinUIDesktopSample/WinUIDesktopSample.csproj index 7c54070c2..7d8dbb55b 100644 --- a/src/Samples/WinUIDesktopSample/WinUIDesktopSample.csproj +++ b/src/Samples/WinUIDesktopSample/WinUIDesktopSample.csproj @@ -2,7 +2,7 @@ WinExe - net5.0-windows10.0.19041.0 + net8.0-windows10.0.19041.0 DISABLE_XAML_GENERATED_MAIN WinUIDesktopSample.exe.manifest DoNotGenerateOtherProviders x86;x64 + win-x86;win-x64;win-arm64 true false + true @@ -22,16 +24,19 @@ - + - + - + compile; runtime + + all + + true true true true - + + {ffa9a78b-f53f-43ee-af87-24a80f4c330a} - TargetFramework=net6.0 + TargetFramework=net8.0 - - {0a991d5f-bfee-4d2f-9aad-6ad06470a5df} - TargetFramework=net6.0 + + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA} + TargetFramework=net8.0 {0bb8f82d-874e-45aa-bca3-20ce0562164a} - TargetFramework=net6.0 + TargetFramework=net8.0 {7e33bcb7-19c5-4061-981d-ba695322708a} {25244ced-966e-45f2-9711-1f51e951ff89} - TargetFramework=net6.0 + TargetFramework=net8.0 {41e2a272-150f-42f5-ad40-047aad9088a0} - TargetFramework=net6.0 + TargetFramework=net8.0 + + + AuthoringTest + $([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)..\$(NativeLibraryProjectName)'))\ + $(NativeLibraryProjectDirectory)$(NativeLibraryProjectName).csproj + $(NativeLibraryProjectDirectory)bin\$(Platform)\$(Configuration)\net8.0\ + $(NativeLibraryOutputFolder)$(NativeLibraryProjectName).dll + $(NativeLibraryProjectDirectory)bin\$(Platform)\$(Configuration)\net8.0\win-$(Platform)\publish\ + $(NativeLibraryPublishFolder)$(NativeLibraryProjectName).dll + + + + + + + + + + - ..\AuthoringTest\bin\$(_WinMDPlatform)\$(Configuration)\net6.0\AuthoringTest.winmd + ..\AuthoringTest\bin\$(_WinMDPlatform)\$(Configuration)\net8.0\AuthoringTest.winmd true - + + - - - + + + + + + + + + @@ -172,7 +218,7 @@ Use pch.h - X64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + X64;NDEBUG;AOT;_CONSOLE;%(PreprocessorDefinitions) MultiThreadedDLL Level3 ProgramDatabase @@ -204,11 +250,22 @@ 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/Tests/AuthoringConsumptionTest/Directory.Build.targets b/src/Tests/AuthoringConsumptionTest/Directory.Build.targets index 623267b30..f297544fd 100644 --- a/src/Tests/AuthoringConsumptionTest/Directory.Build.targets +++ b/src/Tests/AuthoringConsumptionTest/Directory.Build.targets @@ -2,7 +2,7 @@ $(MSBuildProjectDirectory)/DoNotImport_MsAppxPackageTargets.targets - CopyTestAssets;$(PrepareForRunDependsOn) + CopyTestAssets;$(PrepareForRunDependsOn) diff --git a/src/Tests/AuthoringConsumptionTest/WinRT.Host.runtimeconfig.json b/src/Tests/AuthoringConsumptionTest/WinRT.Host.runtimeconfig.json index 14f127d48..f0eda9aa4 100644 --- a/src/Tests/AuthoringConsumptionTest/WinRT.Host.runtimeconfig.json +++ b/src/Tests/AuthoringConsumptionTest/WinRT.Host.runtimeconfig.json @@ -1,10 +1,10 @@ { "runtimeOptions": { - "tfm": "net6.0", + "tfm": "net8.0", "rollForward": "LatestMinor", "framework": { "name": "Microsoft.NETCore.App", - "version": "6.0.0-preview" + "version": "8.0.0" } } -} \ No newline at end of file +} diff --git a/src/Tests/AuthoringConsumptionTest/packages.config b/src/Tests/AuthoringConsumptionTest/packages.config index cd8f371e2..383dcaa3c 100644 --- a/src/Tests/AuthoringConsumptionTest/packages.config +++ b/src/Tests/AuthoringConsumptionTest/packages.config @@ -1,7 +1,13 @@  - - - + + + + + + + + + \ No newline at end of file diff --git a/src/Tests/AuthoringConsumptionTest/pch.h b/src/Tests/AuthoringConsumptionTest/pch.h index 1eb7dade4..00ed7367d 100644 --- a/src/Tests/AuthoringConsumptionTest/pch.h +++ b/src/Tests/AuthoringConsumptionTest/pch.h @@ -4,6 +4,7 @@ // conflict with Storyboard::GetCurrentTime #undef GetCurrentTime +#include #include #include @@ -16,8 +17,10 @@ #pragma pop_macro("X86") #include +#include #include #include +#include #include #include diff --git a/src/Tests/AuthoringConsumptionTest/test.cpp b/src/Tests/AuthoringConsumptionTest/test.cpp index 45f8c87cd..60539ed14 100644 --- a/src/Tests/AuthoringConsumptionTest/test.cpp +++ b/src/Tests/AuthoringConsumptionTest/test.cpp @@ -259,6 +259,8 @@ TEST(AuthoringTest, Arrays) EXPECT_EQ(arr2[idx], idx + 1); } + // Array marshaling on AOT needs dynamic code. +#ifndef AOT std::array basicStructArr; basicStructArr[0] = basicClass.GetBasicStruct(); basicStructArr[1].X = 4; @@ -272,6 +274,7 @@ TEST(AuthoringTest, Arrays) EXPECT_EQ(result[1].X, basicStructArr[1].X); EXPECT_EQ(result[1].Y, basicStructArr[1].Y); EXPECT_EQ(result[1].Value, basicStructArr[1].Value); +#endif } TEST(AuthoringTest, CustomTypes) @@ -344,14 +347,32 @@ TEST(AuthoringTest, CustomTypes) EXPECT_EQ(pv, nullptr); } + // Array marshaling on AOT needs dynamic code. +#ifndef AOT auto erasedProjectedArrays = testClass.GetTypeErasedProjectedArrays(); - EXPECT_EQ(erasedProjectedArrays.Size(), 8); + EXPECT_EQ(erasedProjectedArrays.Size(), 7); for (auto obj : erasedProjectedArrays) { auto ra = obj.try_as(); EXPECT_NE(ra, nullptr); auto type = ra.Type(); } +#endif +} + +TEST(AuthoringTest, Async) +{ + TestClass testClass; + auto asyncOperation = testClass.GetDoubleAsyncOperation(); + EXPECT_EQ(asyncOperation.wait_for(std::chrono::seconds(2)), AsyncStatus::Completed); + EXPECT_EQ(asyncOperation.GetResults(), 4.0); + + auto asyncOperation2 = testClass.GetStructAsyncOperation(); + EXPECT_EQ(asyncOperation2.wait_for(std::chrono::seconds(2)), AsyncStatus::Completed); + auto result = asyncOperation2.GetResults(); + EXPECT_EQ(result.X, 2); + EXPECT_EQ(result.Y, 4); + EXPECT_EQ(result.Value, L"Test"); } TEST(AuthoringTest, CustomDictionaryImplementations) @@ -501,9 +522,6 @@ TEST(AuthoringTest, XamlMappings) first.Current().as().Close(); EXPECT_TRUE(first.Current().as().IsDisposed()); EXPECT_FALSE(vector.GetAt(1).as().IsDisposed()); - for (auto obj : vector.GetView()) - { - } vector.RemoveAt(0); EXPECT_EQ(vector.Size(), 2); @@ -639,4 +657,169 @@ TEST(AuthoringTest, PartialClass) EXPECT_EQ(partialStruct.X, 3); EXPECT_EQ(partialStruct.Y, 4); EXPECT_EQ(partialStruct.Z, 5); +} + +TEST(AuthoringTest, MixedWinRTClassicCOM) +{ + TestMixedWinRTCOMWrapper wrapper; + + // Normal WinRT methods work as you'd expect + EXPECT_EQ(wrapper.HelloWorld(), L"Hello from mixed WinRT/COM"); + + // Verify we can grab the internal interface + IID internalInterface1Iid; + check_hresult(IIDFromString(L"{C7850559-8FF2-4E54-A237-6ED813F20CDC}", &internalInterface1Iid)); + winrt::com_ptr<::IUnknown> unknown1 = wrapper.as<::IUnknown>(); + winrt::com_ptr<::IUnknown> internalInterface1; + EXPECT_EQ(unknown1->QueryInterface(internalInterface1Iid, internalInterface1.put_void()), S_OK); + + // Verify we can grab the nested public interface (in an internal type) + IID internalInterface2Iid; + check_hresult(IIDFromString(L"{8A08E18A-8D20-4E7C-9242-857BFE1E3159}", &internalInterface2Iid)); + winrt::com_ptr<::IUnknown> unknown2 = wrapper.as<::IUnknown>(); + winrt::com_ptr<::IUnknown> internalInterface2; + EXPECT_EQ(unknown2->QueryInterface(internalInterface2Iid, internalInterface2.put_void()), S_OK); + + typedef int (__stdcall* GetNumber)(void*, int*); + + int number; + + // Validate the first call on IInternalInterface1 + EXPECT_EQ(reinterpret_cast((*reinterpret_cast(internalInterface1.get()))[3])(internalInterface1.get(), &number), S_OK); + EXPECT_EQ(number, 42); + + // Validate the second call on IInternalInterface2 + EXPECT_EQ(reinterpret_cast((*reinterpret_cast(internalInterface2.get()))[3])(internalInterface2.get(), &number), S_OK); + EXPECT_EQ(number, 123); +} + +TEST(AuthoringTest, GetRuntimeClassName) +{ + CustomDictionary2 dictionary; + EXPECT_EQ(winrt::get_class_name(dictionary), L"AuthoringTest.CustomDictionary2"); + + DisposableClass disposed; + EXPECT_EQ(winrt::get_class_name(disposed), L"AuthoringTest.DisposableClass"); + + TestMixedWinRTCOMWrapper wrapper; + EXPECT_EQ(winrt::get_class_name(wrapper), L"AuthoringTest.TestMixedWinRTCOMWrapper"); + + TestClass testClass; + testClass.SetNonProjectedDisposableObject(); + EXPECT_EQ(winrt::get_class_name(testClass.DisposableObject()), L"Windows.Foundation.IClosable"); + + testClass.SetProjectedDisposableObject(); + EXPECT_EQ(winrt::get_class_name(testClass.DisposableObject()), L"AuthoringTest.DisposableClass"); +} + +TEST(AuthoringTest, XamlMetadataProvider) +{ + CustomXamlMetadataProvider provider; + EXPECT_NE(provider.GetXamlType(winrt::xaml_typename>()), nullptr); + EXPECT_NE(provider.GetXamlType(winrt::xaml_typename>()), nullptr); + EXPECT_NE(provider.GetXamlType(winrt::xaml_typename>()), nullptr); + EXPECT_NE(provider.GetXamlType(winrt::xaml_typename>()), nullptr); +} + +TEST(AuthoringTest, CustomInterfaceGuid) +{ + CustomInterfaceGuidClass customInterfaceGuidClass; + winrt::com_ptr<::IUnknown> customInterfaceClassUnknown = customInterfaceGuidClass.as<::IUnknown>(); + ICustomInterfaceGuid customInterface; + + IID customInterfaceIid; + check_hresult(IIDFromString(L"{26D8EE57-8B1B-46F4-A4F9-8C6DEEEAF53A}", &customInterfaceIid)); + check_hresult(customInterfaceClassUnknown->QueryInterface(customInterfaceIid, reinterpret_cast(winrt::put_abi(customInterface)))); + + EXPECT_EQ(customInterface.HelloWorld(), L"Hello World!"); +} + +TEST(AuthoringTest, NonActivatableFactory) +{ + EXPECT_EQ(NonActivatableFactory::Create().GetText(), L"Test123"); +} + +TEST(AuthoringTest, TypeOnlyActivatableViaItsOwnFactory) +{ + EXPECT_EQ(TypeOnlyActivatableViaItsOwnFactory::Create().GetText(), L"Hello!"); +} + +TEST(AuthoringTest, ExplicitlyImplementedICustomPropertyProvider) +{ + CustomPropertyProviderWithExplicitImplementation userObject; + + // We should be able to cast to 'ICustomPropertyProvider' + auto propertyProvider = userObject.as(); + + auto providerType = propertyProvider.Type(); + EXPECT_EQ(providerType.Kind, Windows::UI::Xaml::Interop::TypeKind::Metadata); + EXPECT_EQ(providerType.Name, L"AuthoringTest.CustomPropertyProviderWithExplicitImplementation"); + + auto customProperty = propertyProvider.GetCustomProperty(L"TestCustomProperty"); + + EXPECT_NE(customProperty, nullptr); + EXPECT_TRUE(customProperty.CanRead()); + EXPECT_FALSE(customProperty.CanWrite()); + EXPECT_EQ(customProperty.Name(), L"TestCustomProperty"); + + auto propertyType = customProperty.Type(); + EXPECT_EQ(propertyType.Kind, Windows::UI::Xaml::Interop::TypeKind::Metadata); + EXPECT_EQ(propertyType.Name, L"AuthoringTest.CustomPropertyWithExplicitImplementation"); + + auto propertyValue = customProperty.GetValue(nullptr); + EXPECT_EQ(winrt::unbox_value(propertyValue), L"TestPropertyValue"); +} + +TEST(AuthoringTest, GeneratedCustomPropertyStructType) +{ + auto userObject = CustomPropertyRecordTypeFactory::CreateStruct(); + + // We should be able to cast to 'ICustomPropertyProvider' + auto propertyProvider = userObject.as(); + + auto customProperty = propertyProvider.GetCustomProperty(L"Value"); + + EXPECT_NE(customProperty, nullptr); + EXPECT_TRUE(customProperty.CanRead()); + EXPECT_FALSE(customProperty.CanWrite()); + EXPECT_EQ(customProperty.Name(), L"Value"); + + auto propertyValue = customProperty.GetValue(userObject); + EXPECT_EQ(winrt::unbox_value(propertyValue), L"CsWinRTFromStructType"); +} + +TEST(AuthoringTest, GeneratedCustomPropertyRecordType) +{ + auto userObject = CustomPropertyRecordTypeFactory::CreateRecord(); + + // We should be able to cast to 'ICustomPropertyProvider' + auto propertyProvider = userObject.as(); + + auto customProperty = propertyProvider.GetCustomProperty(L"Value"); + + EXPECT_NE(customProperty, nullptr); + EXPECT_TRUE(customProperty.CanRead()); + EXPECT_FALSE(customProperty.CanWrite()); + EXPECT_EQ(customProperty.Name(), L"Value"); + + auto propertyValue = customProperty.GetValue(userObject); + EXPECT_EQ(winrt::unbox_value(propertyValue), L"CsWinRTFromRecordType"); +} + +TEST(AuthoringTest, CustomPropertyRecordStructTypeFactoryAndICPP) +{ + auto userObject = CustomPropertyRecordTypeFactory::CreateRecordStruct(); + + // We should be able to cast to 'ICustomPropertyProvider' + auto propertyProvider = userObject.as(); + + auto customProperty = propertyProvider.GetCustomProperty(L"Value"); + + EXPECT_NE(customProperty, nullptr); + EXPECT_TRUE(customProperty.CanRead()); + EXPECT_FALSE(customProperty.CanWrite()); + EXPECT_EQ(customProperty.Name(), L"Value"); + + auto propertyValue = customProperty.GetValue(userObject); + EXPECT_EQ(winrt::unbox_value(propertyValue), L"CsWinRTFromRecordStructType"); } \ No newline at end of file diff --git a/src/Tests/AuthoringTest/AuthoringTest.csproj b/src/Tests/AuthoringTest/AuthoringTest.csproj index 68442a518..7f2f3c623 100644 --- a/src/Tests/AuthoringTest/AuthoringTest.csproj +++ b/src/Tests/AuthoringTest/AuthoringTest.csproj @@ -1,31 +1,43 @@ - net6.0 + net8.0 x64;x86 true + true + 10.0.18362.0 - + + win-x86;win-x64;win-arm64 + + + true + + true + + + $(WarningsNotAsErrors);CS0067;CS0282 - + + all - - - - - - - - - + + + diff --git a/src/Tests/AuthoringTest/Directory.Build.props b/src/Tests/AuthoringTest/Directory.Build.props index 4cf186e3d..317ce5fe1 100644 --- a/src/Tests/AuthoringTest/Directory.Build.props +++ b/src/Tests/AuthoringTest/Directory.Build.props @@ -2,8 +2,20 @@ true + true + + + + true + Shared + diff --git a/src/Tests/AuthoringTest/Directory.Build.targets b/src/Tests/AuthoringTest/Directory.Build.targets index 53a2d80be..8dde12d1b 100644 --- a/src/Tests/AuthoringTest/Directory.Build.targets +++ b/src/Tests/AuthoringTest/Directory.Build.targets @@ -1,9 +1,5 @@ - - - - diff --git a/src/Tests/AuthoringTest/Program.cs b/src/Tests/AuthoringTest/Program.cs index aa277382c..f4abf226f 100644 --- a/src/Tests/AuthoringTest/Program.cs +++ b/src/Tests/AuthoringTest/Program.cs @@ -1,5 +1,6 @@ using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; +using Microsoft.UI.Xaml.Data; using Microsoft.UI.Xaml.Markup; using System; using System.Collections; @@ -9,12 +10,18 @@ using System.ComponentModel.DataAnnotations; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.WindowsRuntime; using System.Threading; using System.Threading.Tasks; using System.Windows.Input; using Windows.Foundation; using Windows.Foundation.Collections; using Windows.Foundation.Metadata; +using Windows.Graphics.Effects; +using WinRT; +using WinRT.Interop; #pragma warning disable CA1416 @@ -193,6 +200,112 @@ public sealed class CustomWWW : IWwwFormUrlDecoderEntry public string Value => "CsWinRT"; } + [GeneratedBindableCustomProperty] + public sealed partial class CustomProperty + { + public int Number { get; } = 4; + public string Value => "CsWinRT"; + public CustomWWW Url => null; + public CustomPropertyStructType CustomPropertyStructType => new CustomPropertyStructType(); + } + + [GeneratedBindableCustomProperty] + public partial struct CustomPropertyStructType + { + // Public WinRT struct types must have at least one field + public int Dummy; + + public int Number => 4; + public string Value => "CsWinRTFromStructType"; + } + + [GeneratedBindableCustomProperty] + internal sealed partial record CustomPropertyRecordType + { + public int Number { get; } = 4; + public string Value => "CsWinRTFromRecordType"; + } + + [GeneratedBindableCustomProperty] + internal partial record struct CustomPropertyRecordStructType + { + public int Number => 4; + public string Value => "CsWinRTFromRecordStructType"; + } + + public static class CustomPropertyRecordTypeFactory + { + public static object CreateStruct() => new CustomPropertyStructType(); + + public static object CreateRecord() => new CustomPropertyRecordType(); + + public static object CreateRecordStruct() => default(CustomPropertyRecordStructType); + } + + public sealed partial class CustomPropertyProviderWithExplicitImplementation : ICustomPropertyProvider + { + public Type Type => typeof(CustomPropertyProviderWithExplicitImplementation); + + public ICustomProperty GetCustomProperty(string name) + { + if (name == "TestCustomProperty") + { + return new CustomPropertyWithExplicitImplementation(); + } + + return null; + } + + public ICustomProperty GetIndexedProperty(string name, Type type) + { + return null; + } + + public string GetStringRepresentation() + { + return string.Empty; + } + } + + public sealed partial class CustomPropertyWithExplicitImplementation : ICustomProperty + { + internal CustomPropertyWithExplicitImplementation() + { + } + + public bool CanRead => true; + + public bool CanWrite => false; + + public string Name => "TestCustomProperty"; + + public Type Type => typeof(CustomPropertyWithExplicitImplementation); + + /// + public object GetIndexedValue(object target, object index) + { + throw new NotSupportedException(); + } + + /// + public object GetValue(object target) + { + return "TestPropertyValue"; + } + + /// + public void SetIndexedValue(object target, object value, object index) + { + throw new NotSupportedException(); + } + + /// + public void SetValue(object target, object value) + { + throw new NotSupportedException(); + } + } + [Version(3u)] public interface IDouble { @@ -323,6 +436,36 @@ public static IReadOnlyList GetUris() }; } + public static IList GetBools() + { + return new List() + { + true, + false, + true + }; + } + + public static IList GetBasicStructs() + { + return new List() + { + new BasicStruct() { X = 1, Y = 2, Value = "Basic" }, + new BasicStruct() { X = 2, Y = 4, Value = "Struct" }, + }; + } + + public static IList GetComplexStructs() + { + return new List() + { + new ComplexStruct() { + X = 12, + Val = true, + BasicStruct = new BasicStruct() { X = 1, Y = 2, Value = "Basic" } }, + }; + } + public IAsyncOperation GetIntAsyncOperation() { int val = IntAsyncOperation.GetResults(); @@ -335,6 +478,25 @@ public IAsyncOperation GetIntAsyncOperation() return task.AsAsyncOperation(); } + public IAsyncOperationWithProgress GetDoubleAsyncOperation() + { + return AsyncInfo.Run(async (cancellationToken, progress) => + { + await Task.Delay(100); + return 4.0; + }); + } + + public IAsyncOperation GetStructAsyncOperation() + { + return System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.FromResult(new BasicStruct() { X = 2, Y = 4, Value = "Test" }); + } + + public IAsyncOperation GetBoolAsyncOperation() + { + return Task.FromResult(false).AsAsyncOperation(); + } + public int SetIntAsyncOperation(IAsyncOperation op) { return op.GetResults(); @@ -459,11 +621,11 @@ public IList GetTypeErasedProjectedArrays() new BasicDelegate[] { new BasicDelegate((uint value) => {}) }, new [] { new DisposableClass().GetType() , new NonProjectedDisposableClass().GetType() }, new Type[] { typeof(TestClass), typeof(DisposableClass) }, - new PrivateEnum[] { PrivateEnum.PrivateFirst, PrivateEnum.PrivateSecond} }; } } + [WinRTRuntimeClassName("AuthoringTest.DisposableClassImpl")] public sealed class DisposableClass : IDisposable { public bool IsDisposed { get; set; } @@ -479,7 +641,7 @@ public void Dispose() } } - internal sealed class NonProjectedDisposableClass : IDisposable + internal sealed partial class NonProjectedDisposableClass : IDisposable { public bool IsDisposed { get; set; } @@ -579,6 +741,7 @@ IEnumerator IEnumerable.GetEnumerator() } } + [WinRTRuntimeClassName("AuthoringTest.CustomReadOnlyDictionaryImpl")] public sealed class CustomReadOnlyDictionary : IReadOnlyDictionary { private readonly CustomDictionary _dictionary; @@ -827,6 +990,7 @@ public void RemoveAt(int index) } } + [WinRTRuntimeClassName("AuthoringTest.StaticClassImpl")] public static class StaticClass { public static int GetNumber() @@ -1050,6 +1214,14 @@ public sealed class CustomXamlMetadataProvider : IXamlMetadataProvider // Tests DefaultOverload attribute specified in projected interface. public IXamlType GetXamlType(Type type) { + if (type == typeof(Nullable) || + type == typeof(TimeSpan?) || + type == typeof(BasicEnum?) || + type == typeof(FlagsEnum?)) + { + return new XamlType(type); + } + return null; } @@ -1064,6 +1236,72 @@ public XmlnsDefinition[] GetXmlnsDefinitions() } } + internal sealed partial class XamlType : IXamlType + { + private readonly Type _type; + + public XamlType(Type type) + { + _type = type; + } + + public IXamlType BaseType => new XamlType(_type.BaseType); + + public IXamlType BoxedType => throw new NotImplementedException(); + + public IXamlMember ContentProperty => throw new NotImplementedException(); + + public string FullName => _type.FullName; + + public bool IsArray => _type.IsArray; + + public bool IsBindable => throw new NotImplementedException(); + + public bool IsCollection => throw new NotImplementedException(); + + public bool IsConstructible => throw new NotImplementedException(); + + public bool IsDictionary => throw new NotImplementedException(); + + public bool IsMarkupExtension => throw new NotImplementedException(); + + public IXamlType ItemType => throw new NotImplementedException(); + + public IXamlType KeyType => throw new NotImplementedException(); + + public Type UnderlyingType => throw new NotImplementedException(); + + public object ActivateInstance() + { + throw new NotImplementedException(); + } + + public void AddToMap(object instance, object key, object value) + { + throw new NotImplementedException(); + } + + public void AddToVector(object instance, object value) + { + throw new NotImplementedException(); + } + + public object CreateFromString(string value) + { + throw new NotImplementedException(); + } + + public IXamlMember GetMember(string name) + { + throw new NotImplementedException(); + } + + public void RunInitializer() + { + throw new NotImplementedException(); + } + } + public sealed class SingleInterfaceClass : IDouble { private double _number; @@ -1470,6 +1708,10 @@ bool IDictionary.TryGetValue(string key, out int value) } } + public sealed class TestCollection : CollectionBase + { + } + public partial interface IPartialInterface { public string GetNumberAsString(); @@ -1570,4 +1812,249 @@ public partial struct PartialStruct { public double Z; } + + // Nested type to validate (https://github.com/microsoft/CsWinRT/issues/1477) + // Doesn't need to be consumed, we just want to verify the generator does work. + internal partial class Nested1 + { + internal partial record struct Nested2 + { + internal partial struct Nested3 + { + internal partial interface INested4 + { + internal partial record Nested5 + { + internal partial class InnerMostType : IGraphicsEffectSource, IPublicInterface, IDisposable + { + public string HelloWorld() + { + return "Hello from mixed WinRT/COM"; + } + + public void Dispose() + { + } + } + } + } + } + } + } + + public sealed class TestMixedWinRTCOMWrapper : IGraphicsEffectSource, IPublicInterface, IInternalInterface1, SomeInternalType.IInternalInterface2 + { + public string HelloWorld() + { + return "Hello from mixed WinRT/COM"; + } + + unsafe int IInternalInterface1.GetNumber(int* value) + { + *value = 42; + + return 0; + } + + unsafe int SomeInternalType.IInternalInterface2.GetNumber(int* value) + { + *value = 123; + + return 0; + } + } + + public interface IPublicInterface + { + string HelloWorld(); + } + + // Internal, classic COM interface + [global::System.Runtime.InteropServices.Guid("C7850559-8FF2-4E54-A237-6ED813F20CDC")] + [WindowsRuntimeType] + [WindowsRuntimeHelperType(typeof(IInternalInterface1))] + internal unsafe interface IInternalInterface1 + { + int GetNumber(int* value); + + [global::System.Runtime.InteropServices.Guid("C7850559-8FF2-4E54-A237-6ED813F20CDC")] + public struct Vftbl + { + public static readonly IntPtr AbiToProjectionVftablePtr = InitVtbl(); + + private static IntPtr InitVtbl() + { + Vftbl* lpVtbl = (Vftbl*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(Vftbl)); + + lpVtbl->IUnknownVftbl = IUnknownVftbl.AbiToProjectionVftbl; + lpVtbl->GetNumber = &GetNumberFromAbi; + + return (IntPtr)lpVtbl; + } + + private IUnknownVftbl IUnknownVftbl; + private delegate* unmanaged[Stdcall] GetNumber; + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static int GetNumberFromAbi(void* thisPtr, int* value) + { + try + { + return ComWrappersSupport.FindObject((IntPtr)thisPtr).GetNumber(value); + } + catch (Exception e) + { + ExceptionHelpers.SetErrorInfo(e); + + return Marshal.GetHRForException(e); + } + } + } + } + + internal struct SomeInternalType + { + // Nested, classic COM interface + [global::System.Runtime.InteropServices.Guid("8A08E18A-8D20-4E7C-9242-857BFE1E3159")] + [WindowsRuntimeType] + [WindowsRuntimeHelperType(typeof(IInternalInterface2))] + public unsafe interface IInternalInterface2 + { + int GetNumber(int* value); + + [global::System.Runtime.InteropServices.Guid("8A08E18A-8D20-4E7C-9242-857BFE1E3159")] + public struct Vftbl + { + public static readonly IntPtr AbiToProjectionVftablePtr = InitVtbl(); + + private static IntPtr InitVtbl() + { + Vftbl* lpVtbl = (Vftbl*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(Vftbl)); + + lpVtbl->IUnknownVftbl = IUnknownVftbl.AbiToProjectionVftbl; + lpVtbl->GetNumber = &GetNumberFromAbi; + + return (IntPtr)lpVtbl; + } + + private IUnknownVftbl IUnknownVftbl; + private delegate* unmanaged[Stdcall] GetNumber; + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static int GetNumberFromAbi(void* thisPtr, int* value) + { + try + { + return ComWrappersSupport.FindObject((IntPtr)thisPtr).GetNumber(value); + } + catch (Exception e) + { + ExceptionHelpers.SetErrorInfo(e); + + return Marshal.GetHRForException(e); + } + } + } + } + } + + [System.Runtime.InteropServices.Guid("26D8EE57-8B1B-46F4-A4F9-8C6DEEEAF53A")] + public interface ICustomInterfaceGuid + { + string HelloWorld(); + } + + public sealed class CustomInterfaceGuidClass : ICustomInterfaceGuid + { + public string HelloWorld() => "Hello World!"; + } + + public sealed class NonActivatableType + { + private readonly string _text; + + // This should not be referenced by the generated activation factory + internal NonActivatableType(string text) + { + _text = text; + } + + public string GetText() + { + return _text; + } + } + + [WinRTRuntimeClassName("AuthoringTest.NonActivatableFactoryImpl")] + public static class NonActivatableFactory + { + public static NonActivatableType Create() + { + return new("Test123"); + } + } + + public sealed class TypeOnlyActivatableViaItsOwnFactory + { + private readonly string _text; + + private TypeOnlyActivatableViaItsOwnFactory(string text) + { + _text = text; + } + + public static TypeOnlyActivatableViaItsOwnFactory Create() + { + return new("Hello!"); + } + + public string GetText() + { + return _text; + } + } +} + +namespace ABI.AuthoringTest +{ + internal static class IInternalInterface1Methods + { + public static Guid IID => typeof(global::AuthoringTest.IInternalInterface1).GUID; + + public static IntPtr AbiToProjectionVftablePtr => global::AuthoringTest.IInternalInterface1.Vftbl.AbiToProjectionVftablePtr; + } + + internal struct SomeInternalType + { + internal static class IInternalInterface2Methods + { + public static Guid IID => typeof(global::AuthoringTest.SomeInternalType.IInternalInterface2).GUID; + + public static IntPtr AbiToProjectionVftablePtr => global::AuthoringTest.SomeInternalType.IInternalInterface2.Vftbl.AbiToProjectionVftablePtr; + } + } +} + +namespace AnotherNamespace +{ + internal partial class PartialClass3 + { + public void InternalFunction() + { + } + } + + partial class PartialClass3 + { + public void InternalFunction2() + { + } + } + + internal class InternalClass + { + public void InternalFunction() + { + } + } } \ No newline at end of file diff --git a/src/Tests/AuthoringWinUITest/AuthoringWinUITest (Package)/AuthoringWinUITest (Package).wapproj b/src/Tests/AuthoringWinUITest/AuthoringWinUITest (Package)/AuthoringWinUITest (Package).wapproj index f450d6288..1bb293f98 100644 --- a/src/Tests/AuthoringWinUITest/AuthoringWinUITest (Package)/AuthoringWinUITest (Package).wapproj +++ b/src/Tests/AuthoringWinUITest/AuthoringWinUITest (Package)/AuthoringWinUITest (Package).wapproj @@ -41,7 +41,6 @@ 10.0.17763.0 en-US false - $(MSBuildThisFileDirectory)build\ ..\AuthoringWinUITest\AuthoringWinUITest.vcxproj <_WinMDPlatform>$(Platform) <_WinMDPlatform Condition="'$(Platform)' == 'Win32'">x86 @@ -67,7 +66,7 @@ WinRT.Host.dll.mui Always - + AuthoringTest.dll Always @@ -82,6 +81,13 @@ + + + build + + + build + + - \ No newline at end of file diff --git a/src/Tests/AuthoringWinUITest/AuthoringWinUITest (Package)/build/Microsoft.WinUI.AppX.targets b/src/Tests/AuthoringWinUITest/AuthoringWinUITest (Package)/build/Microsoft.WinUI.AppX.targets deleted file mode 100644 index 307641788..000000000 --- a/src/Tests/AuthoringWinUITest/AuthoringWinUITest (Package)/build/Microsoft.WinUI.AppX.targets +++ /dev/null @@ -1,236 +0,0 @@ - - - - - - - - - - - - $(MSBuildThisFileDirectory) - - - $(WinUIClassRegistrationsDir.Substring(0,$(WinUIClassRegistrationsDir.IndexOf(';')))) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $(MSBuildWarningsAsMessages); - - - APPX1707; - - - $(MSBuildWarningsAsMessages); - - - MSB3268; - - - - - - - - - Windows - 10.0 - en-US - - - - - - <_MuxRuntimeIdentifier Condition="'$(Platform)' == 'Win32'">win10-x86 - <_MuxRuntimeIdentifier Condition="'$(Platform)' != 'Win32'">win10-$(Platform) - - - - $([MSBuild]::MakeRelative($(MSBuildThisFileDirectory)..\runtimes\$(_MuxRuntimeIdentifier)\native\, %(RootDir)%(Directory))) - - - - - - - - - %(WapProjPackageFile.DestinationSubDirectory)%(TargetPath) - - - %(UploadWapProjPackageFile.DestinationSubDirectory)%(TargetPath) - - - - - - - - - - - - - - - - - - - - - UAP - - - - diff --git a/src/Tests/AuthoringWinUITest/AuthoringWinUITest/App.xaml.cpp b/src/Tests/AuthoringWinUITest/AuthoringWinUITest/App.xaml.cpp index 50d0f5ca7..f73eb6018 100644 --- a/src/Tests/AuthoringWinUITest/AuthoringWinUITest/App.xaml.cpp +++ b/src/Tests/AuthoringWinUITest/AuthoringWinUITest/App.xaml.cpp @@ -21,7 +21,6 @@ using namespace AuthoringWinUITest::implementation; App::App() { InitializeComponent(); - Suspending({ this, &App::OnSuspending }); #if defined _DEBUG && !defined DISABLE_XAML_GENERATED_BREAK_ON_UNHANDLED_EXCEPTION UnhandledException([this](IInspectable const&, UnhandledExceptionEventArgs const& e) @@ -45,15 +44,3 @@ void App::OnLaunched(LaunchActivatedEventArgs const&) window = make(); window.Activate(); } - -/// -/// Invoked when application execution is being suspended. Application state is saved -/// without knowing whether the application will be terminated or resumed with the contents -/// of memory still intact. -/// -/// The source of the suspend request. -/// Details about the suspend request. -void App::OnSuspending([[maybe_unused]] IInspectable const& sender, [[maybe_unused]] Windows::ApplicationModel::SuspendingEventArgs const& e) -{ - // Save application state and stop any background activity -} \ No newline at end of file diff --git a/src/Tests/AuthoringWinUITest/AuthoringWinUITest/AuthoringWinUITest.vcxproj b/src/Tests/AuthoringWinUITest/AuthoringWinUITest/AuthoringWinUITest.vcxproj index 8c676fb0e..483e8012d 100644 --- a/src/Tests/AuthoringWinUITest/AuthoringWinUITest/AuthoringWinUITest.vcxproj +++ b/src/Tests/AuthoringWinUITest/AuthoringWinUITest/AuthoringWinUITest.vcxproj @@ -1,7 +1,12 @@  - - + + + + + + + true true @@ -22,6 +27,7 @@ 10.0 10.0 10.0.17134.0 + true @@ -134,12 +140,14 @@ + + true + - ..\..\AuthoringTest\bin\$(_WinMDPlatform)\$(Configuration)\net6.0\AuthoringTest.winmd + ..\..\AuthoringTest\bin\$(_WinMDPlatform)\$(Configuration)\net8.0\AuthoringTest.winmd true - WinRT.Host.dll @@ -148,19 +156,19 @@ {ffa9a78b-f53f-43ee-af87-24a80f4c330a} - TargetFramework=net6.0 + TargetFramework=net8.0 - - {0a991d5f-bfee-4d2f-9aad-6ad06470a5df} - TargetFramework=net6.0 + + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA} + TargetFramework=net8.0 {25244ced-966e-45f2-9711-1f51e951ff89} - TargetFramework=net6.0 + TargetFramework=net8.0 {0bb8f82d-874e-45aa-bca3-20ce0562164a} - TargetFramework=net6.0 + TargetFramework=net8.0 {41e2a272-150f-42f5-ad40-047aad9088a0} @@ -168,18 +176,35 @@ - - - + + + + + + + + + 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/Tests/AuthoringWinUITest/AuthoringWinUITest/AuthoringWinUITest.vcxproj.filters b/src/Tests/AuthoringWinUITest/AuthoringWinUITest/AuthoringWinUITest.vcxproj.filters index 234faee9a..2676bc0b7 100644 --- a/src/Tests/AuthoringWinUITest/AuthoringWinUITest/AuthoringWinUITest.vcxproj.filters +++ b/src/Tests/AuthoringWinUITest/AuthoringWinUITest/AuthoringWinUITest.vcxproj.filters @@ -24,6 +24,7 @@ + diff --git a/src/Tests/AuthoringWinUITest/AuthoringWinUITest/WinRT.Host.runtimeconfig.json b/src/Tests/AuthoringWinUITest/AuthoringWinUITest/WinRT.Host.runtimeconfig.json new file mode 100644 index 000000000..f0eda9aa4 --- /dev/null +++ b/src/Tests/AuthoringWinUITest/AuthoringWinUITest/WinRT.Host.runtimeconfig.json @@ -0,0 +1,10 @@ +{ + "runtimeOptions": { + "tfm": "net8.0", + "rollForward": "LatestMinor", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "8.0.0" + } + } +} diff --git a/src/Tests/AuthoringWinUITest/AuthoringWinUITest/app.manifest b/src/Tests/AuthoringWinUITest/AuthoringWinUITest/app.manifest index 174719454..94d911e2e 100644 --- a/src/Tests/AuthoringWinUITest/AuthoringWinUITest/app.manifest +++ b/src/Tests/AuthoringWinUITest/AuthoringWinUITest/app.manifest @@ -34,6 +34,10 @@ name="AuthoringTest.CustomReadOnlyDictionary" threadingModel="both" xmlns="urn:schemas-microsoft-com:winrt.v1" /> + - - - + + + + + + + + + \ No newline at end of file diff --git a/src/Tests/AuthoringWuxConsumptionTest/AuthoringWuxConsumptionTest.exe.manifest b/src/Tests/AuthoringWuxConsumptionTest/AuthoringWuxConsumptionTest.exe.manifest new file mode 100644 index 000000000..e1edf0406 --- /dev/null +++ b/src/Tests/AuthoringWuxConsumptionTest/AuthoringWuxConsumptionTest.exe.manifest @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + diff --git a/src/Tests/AuthoringWuxConsumptionTest/AuthoringWuxConsumptionTest.vcxproj b/src/Tests/AuthoringWuxConsumptionTest/AuthoringWuxConsumptionTest.vcxproj new file mode 100644 index 000000000..2fbcf4b6f --- /dev/null +++ b/src/Tests/AuthoringWuxConsumptionTest/AuthoringWuxConsumptionTest.vcxproj @@ -0,0 +1,210 @@ + + + + + + + Debug + ARM64 + + + Debug + Win32 + + + Release + ARM64 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + {A04A0416-5E35-4DD0-8226-63D941B28467} + Win32Proj + AuthoringConsumptionTest + Application + v143 + v142 + Unicode + <_WinMDPlatform>$(Platform) + <_WinMDPlatform Condition="'$(Platform)' == 'Win32'">x86 + false + + + + + + + + + + + + + + Create + + + + + + + + true + true + true + true + + + + + {ffa9a78b-f53f-43ee-af87-24a80f4c330a} + TargetFramework=net8.0 + + + {0bb8f82d-874e-45aa-bca3-20ce0562164a} + TargetFramework=net8.0 + + + {7e33bcb7-19c5-4061-981d-ba695322708a} + + + {25244ced-966e-45f2-9711-1f51e951ff89} + TargetFramework=net8.0 + + + {d60cfcad-4a13-4047-91c8-9d7df4753493} + TargetFramework=net8.0 + + + + + ..\AuthoringWuxTest\bin\$(_WinMDPlatform)\$(Configuration)\net8.0\AuthoringWuxTest.winmd + true + + + + + + + + + + + + + + + Use + pch.h + Disabled + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + Level3 + + + true + Console + + + + + Use + pch.h + Disabled + X64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + Level3 + + + DebugFull + Console + + + + + Use + pch.h + Disabled + X64;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + Level3 + + + DebugFull + Console + + + + + Use + pch.h + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + ProgramDatabase + + + true + Console + true + true + + + + + Use + pch.h + X64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + ProgramDatabase + + + true + Console + true + true + + + + + Use + pch.h + X64;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + Level3 + ProgramDatabase + + + true + Console + true + true + + + + + 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/Tests/AuthoringWuxConsumptionTest/AuthoringWuxConsumptionTest.vcxproj.filters b/src/Tests/AuthoringWuxConsumptionTest/AuthoringWuxConsumptionTest.vcxproj.filters new file mode 100644 index 000000000..1332cff35 --- /dev/null +++ b/src/Tests/AuthoringWuxConsumptionTest/AuthoringWuxConsumptionTest.vcxproj.filters @@ -0,0 +1,39 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + + + Source Files + + + Source Files + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Tests/AuthoringWuxConsumptionTest/Directory.Build.targets b/src/Tests/AuthoringWuxConsumptionTest/Directory.Build.targets new file mode 100644 index 000000000..623267b30 --- /dev/null +++ b/src/Tests/AuthoringWuxConsumptionTest/Directory.Build.targets @@ -0,0 +1,16 @@ + + + + $(MSBuildProjectDirectory)/DoNotImport_MsAppxPackageTargets.targets + CopyTestAssets;$(PrepareForRunDependsOn) + + + + + + + + + diff --git a/src/Tests/AuthoringWuxConsumptionTest/DoNotImport_MsAppxPackageTargets.targets b/src/Tests/AuthoringWuxConsumptionTest/DoNotImport_MsAppxPackageTargets.targets new file mode 100644 index 000000000..bcb335b80 --- /dev/null +++ b/src/Tests/AuthoringWuxConsumptionTest/DoNotImport_MsAppxPackageTargets.targets @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Tests/AuthoringWuxConsumptionTest/WinRT.Host.runtimeconfig.json b/src/Tests/AuthoringWuxConsumptionTest/WinRT.Host.runtimeconfig.json new file mode 100644 index 000000000..64a9c9082 --- /dev/null +++ b/src/Tests/AuthoringWuxConsumptionTest/WinRT.Host.runtimeconfig.json @@ -0,0 +1,13 @@ +{ + "runtimeOptions": { + "tfm": "net8.0", + "rollForward": "LatestMinor", + "framework": { + "name": "Microsoft.NETCore.App", + "version": "8.0.0" + }, + "configProperties": { + "CSWINRT_USE_WINDOWS_UI_XAML_PROJECTIONS": "true" + } + } +} \ No newline at end of file diff --git a/src/Tests/AuthoringWuxConsumptionTest/packages.config b/src/Tests/AuthoringWuxConsumptionTest/packages.config new file mode 100644 index 000000000..f8f57ca30 --- /dev/null +++ b/src/Tests/AuthoringWuxConsumptionTest/packages.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/Tests/AuthoringWuxConsumptionTest/pch.cpp b/src/Tests/AuthoringWuxConsumptionTest/pch.cpp new file mode 100644 index 000000000..bcb5590be --- /dev/null +++ b/src/Tests/AuthoringWuxConsumptionTest/pch.cpp @@ -0,0 +1 @@ +#include "pch.h" diff --git a/src/Tests/AuthoringWuxConsumptionTest/pch.h b/src/Tests/AuthoringWuxConsumptionTest/pch.h new file mode 100644 index 000000000..db3c5d270 --- /dev/null +++ b/src/Tests/AuthoringWuxConsumptionTest/pch.h @@ -0,0 +1,26 @@ +#pragma once + +// Undefine GetCurrentTime macro to prevent +// conflict with Storyboard::GetCurrentTime +#undef GetCurrentTime + +#include +#include +#include + +#pragma push_macro("X86") +#pragma push_macro("X64") +#undef X86 +#undef X64 +#include "winrt/Windows.System.h" +#pragma pop_macro("X64") +#pragma pop_macro("X86") + +#include +#include +#include +#include + +#include + +#include "gtest/gtest.h" diff --git a/src/Tests/AuthoringWuxConsumptionTest/test.cpp b/src/Tests/AuthoringWuxConsumptionTest/test.cpp new file mode 100644 index 000000000..9c54886fa --- /dev/null +++ b/src/Tests/AuthoringWuxConsumptionTest/test.cpp @@ -0,0 +1,80 @@ +#include "pch.h" + +using namespace winrt; +using namespace Windows::Foundation; +using namespace AuthoringWuxTest; + +TEST(AuthoringWuxTest, Collections) +{ + DisposableClass disposed; + disposed.Close(); + MultipleInterfaceMappingClass multipleInterfaces; + Windows::UI::Xaml::Interop::IBindableIterable bindable = multipleInterfaces; + Windows::Foundation::Collections::IVector vector = multipleInterfaces; + Windows::UI::Xaml::Interop::IBindableVector bindableVector = multipleInterfaces; + EXPECT_EQ(vector.Size(), 0); + EXPECT_EQ(bindableVector.Size(), 0); + vector.Append(DisposableClass()); + vector.Append(DisposableClass()); + vector.Append(disposed); + bindableVector.Append(DisposableClass()); + EXPECT_EQ(vector.Size(), 4); + EXPECT_EQ(bindableVector.Size(), 4); + + auto first = vector.First(); + EXPECT_TRUE(first.HasCurrent()); + EXPECT_FALSE(first.Current().IsDisposed()); + auto bindableFirst = bindable.First(); + EXPECT_TRUE(bindableFirst.HasCurrent()); + EXPECT_FALSE(bindableFirst.Current().as().IsDisposed()); + bindableFirst.Current().as().Close(); + EXPECT_TRUE(first.Current().IsDisposed()); + EXPECT_FALSE(vector.GetAt(1).IsDisposed()); + EXPECT_TRUE(vector.GetAt(2).IsDisposed()); + EXPECT_TRUE(bindableVector.First().Current().as().IsDisposed()); + EXPECT_FALSE(bindableVector.GetAt(3).as().IsDisposed()); + EXPECT_TRUE(bindableVector.GetAt(2).as().IsDisposed()); + for (auto obj : vector.GetView()) + { + obj.Close(); + } + + std::array view{}; + EXPECT_EQ(vector.GetMany(1, view), 2); + EXPECT_EQ(view.size(), 2); + for (auto& obj : view) + { + EXPECT_TRUE(obj.IsDisposed()); + } +} + +TEST(AuthoringWuxTest, PropertyChanged) +{ + CustomNotifyPropertyChanged customNotifyPropertyChanged; + Windows::UI::Xaml::Data::INotifyPropertyChanged propChanged = customNotifyPropertyChanged; + winrt::hstring propName = L"Number"; + bool eventTriggered = false; + auto token = propChanged.PropertyChanged(auto_revoke, [&eventTriggered, &propName](IInspectable sender, Windows::UI::Xaml::Data::PropertyChangedEventArgs args) + { + eventTriggered = (args.PropertyName() == propName); + }); + + customNotifyPropertyChanged.RaisePropertyChanged(propName); + + EXPECT_TRUE(eventTriggered); +} + +TEST(AuthoringWuxTest, XamlExceptionTypes) +{ + EXPECT_TRUE(XamlExceptionTypes::VerifyExceptionTypes()); +} + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + winrt::init_apartment(winrt::apartment_type::single_threaded); + auto manager = winrt::Windows::UI::Xaml::Hosting::WindowsXamlManager::InitializeForCurrentThread(); + int result = RUN_ALL_TESTS(); + manager.Close(); + return result; +} \ No newline at end of file diff --git a/src/Tests/AuthoringWuxTest/AuthoringWuxTest.csproj b/src/Tests/AuthoringWuxTest/AuthoringWuxTest.csproj new file mode 100644 index 000000000..d7eb510ef --- /dev/null +++ b/src/Tests/AuthoringWuxTest/AuthoringWuxTest.csproj @@ -0,0 +1,32 @@ + + + + net8.0 + x64;x86 + true + true + true + false + true + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Tests/AuthoringWuxTest/Directory.Build.props b/src/Tests/AuthoringWuxTest/Directory.Build.props new file mode 100644 index 000000000..4cf186e3d --- /dev/null +++ b/src/Tests/AuthoringWuxTest/Directory.Build.props @@ -0,0 +1,9 @@ + + + + true + + + + + diff --git a/src/Tests/AuthoringWuxTest/Directory.Build.targets b/src/Tests/AuthoringWuxTest/Directory.Build.targets new file mode 100644 index 000000000..8dde12d1b --- /dev/null +++ b/src/Tests/AuthoringWuxTest/Directory.Build.targets @@ -0,0 +1,5 @@ + + + + + diff --git a/src/Tests/AuthoringWuxTest/Module.cs b/src/Tests/AuthoringWuxTest/Module.cs new file mode 100644 index 000000000..636144eed --- /dev/null +++ b/src/Tests/AuthoringWuxTest/Module.cs @@ -0,0 +1,3 @@ +#if NET +[assembly: global::System.Runtime.Versioning.SupportedOSPlatform("Windows")] +#endif diff --git a/src/Tests/AuthoringWuxTest/Program.cs b/src/Tests/AuthoringWuxTest/Program.cs new file mode 100644 index 000000000..a43657bde --- /dev/null +++ b/src/Tests/AuthoringWuxTest/Program.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using WinRT; + +#pragma warning disable CA1416 + +namespace AuthoringWuxTest +{ + public sealed class DisposableClass : IDisposable + { + public bool IsDisposed { get; set; } + + public DisposableClass() + { + IsDisposed = false; + } + + public void Dispose() + { + IsDisposed = true; + } + } + public sealed class CustomNotifyPropertyChanged : INotifyPropertyChanged + { + public event PropertyChangedEventHandler PropertyChanged; + + public void RaisePropertyChanged(string propertyName) + { + PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); + } + } + + public sealed class CustomNotifyCollectionChanged : INotifyCollectionChanged + { + public event NotifyCollectionChangedEventHandler CollectionChanged; + + public void RaiseCollectionChanged(NotifyCollectionChangedEventArgs args) + { + CollectionChanged?.Invoke(this, args); + } + } + + public sealed class CustomEnumerable : IEnumerable + { + private IEnumerable _enumerable; + + public CustomEnumerable(IEnumerable enumerable) + { + _enumerable = enumerable; + } + + public IEnumerator GetEnumerator() + { + return _enumerable.GetEnumerator(); + } + } + + public sealed class MultipleInterfaceMappingClass : IList, IList + { + private List _list = new List(); + + DisposableClass IList.this[int index] { get => _list[index]; set => _list[index] = value; } + object IList.this[int index] { get => _list[index]; set => ((IList)_list) [index] = value; } + + int ICollection.Count => _list.Count; + + int ICollection.Count => _list.Count; + + bool ICollection.IsReadOnly => true; + + bool IList.IsReadOnly => true; + + bool IList.IsFixedSize => false; + + bool ICollection.IsSynchronized => true; + + object ICollection.SyncRoot => ((ICollection) _list).SyncRoot; + + void ICollection.Add(DisposableClass item) + { + _list.Add(item); + } + + int IList.Add(object value) + { + return ((IList) _list).Add(value); + } + + void ICollection.Clear() + { + _list.Clear(); + } + + void IList.Clear() + { + _list.Clear(); + } + + bool ICollection.Contains(DisposableClass item) + { + return _list.Contains(item); + } + + bool IList.Contains(object value) + { + return ((IList) _list).Contains(value); + } + + void ICollection.CopyTo(DisposableClass[] array, int arrayIndex) + { + _list.CopyTo(array, arrayIndex); + } + + void ICollection.CopyTo(Array array, int index) + { + ((ICollection) _list).CopyTo(array, index); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _list.GetEnumerator(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return _list.GetEnumerator(); + } + + int IList.IndexOf(DisposableClass item) + { + return _list.IndexOf(item); + } + + int IList.IndexOf(object value) + { + return ((IList) _list).IndexOf(value); + } + + void IList.Insert(int index, DisposableClass item) + { + _list.Insert(index, item); + } + + void IList.Insert(int index, object value) + { + ((IList) _list).Insert(index, value); + } + + bool ICollection.Remove(DisposableClass item) + { + return _list.Remove(item); + } + + void IList.Remove(object value) + { + ((IList) _list).Remove(value); + } + + void IList.RemoveAt(int index) + { + _list.RemoveAt(index); + } + + void IList.RemoveAt(int index) + { + _list.RemoveAt(index); + } + } + + public static class XamlExceptionTypes + { + public static bool VerifyExceptionTypes() + { + const int E_XAMLPARSEFAILED = unchecked((int)0x802B000A); + const int E_LAYOUTCYCLE = unchecked((int)0x802B0014); + const int E_ELEMENTNOTENABLED = unchecked((int)0x802B001E); + const int E_ELEMENTNOTAVAILABLE = unchecked((int)0x802B001F); + + return + ExceptionHelpers.GetExceptionForHR(E_XAMLPARSEFAILED)?.GetType() == typeof(Windows.UI.Xaml.Markup.XamlParseException) && + ExceptionHelpers.GetExceptionForHR(E_LAYOUTCYCLE)?.GetType() == typeof(Windows.UI.Xaml.LayoutCycleException) && + ExceptionHelpers.GetExceptionForHR(E_ELEMENTNOTENABLED)?.GetType() == typeof(Windows.UI.Xaml.Automation.ElementNotEnabledException) && + ExceptionHelpers.GetExceptionForHR(E_ELEMENTNOTAVAILABLE)?.GetType() == typeof(Windows.UI.Xaml.Automation.ElementNotAvailableException); + } + } +} diff --git a/src/Tests/AuthoringWuxTest/Properties/launchSettings.json b/src/Tests/AuthoringWuxTest/Properties/launchSettings.json new file mode 100644 index 000000000..c05aeebf5 --- /dev/null +++ b/src/Tests/AuthoringWuxTest/Properties/launchSettings.json @@ -0,0 +1,8 @@ +{ + "profiles": { + "AuthoringSample": { + "commandName": "Project", + "nativeDebugging": true + } + } +} \ No newline at end of file diff --git a/src/Tests/CSWinMDComponent/CSWinMDComponent.vcxproj b/src/Tests/CSWinMDComponent/CSWinMDComponent.vcxproj index 2526d7391..534239306 100644 --- a/src/Tests/CSWinMDComponent/CSWinMDComponent.vcxproj +++ b/src/Tests/CSWinMDComponent/CSWinMDComponent.vcxproj @@ -1,6 +1,6 @@ - + true true @@ -143,19 +143,18 @@ - ..\..\Authoring\cswinmd\bin\$(Configuration)\net6.0\CsWinMD.exe + ..\..\Authoring\cswinmd\bin\$(Configuration)\net8.0\CsWinMD.exe - + 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/Tests/CSWinMDComponent/packages.config b/src/Tests/CSWinMDComponent/packages.config index 43182aa87..f229371b3 100644 --- a/src/Tests/CSWinMDComponent/packages.config +++ b/src/Tests/CSWinMDComponent/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/src/Tests/DiagnosticTests/DiagnosticTests.csproj b/src/Tests/DiagnosticTests/DiagnosticTests.csproj index f7667f79b..7b7eb3bc3 100644 --- a/src/Tests/DiagnosticTests/DiagnosticTests.csproj +++ b/src/Tests/DiagnosticTests/DiagnosticTests.csproj @@ -3,18 +3,19 @@ $(TestsBuildTFMs) false + true - - - - - - + + + + + + - + - + \ No newline at end of file diff --git a/src/Tests/DiagnosticTests/NegativeData.cs b/src/Tests/DiagnosticTests/NegativeData.cs index 0177e1c59..cf8f450a0 100644 --- a/src/Tests/DiagnosticTests/NegativeData.cs +++ b/src/Tests/DiagnosticTests/NegativeData.cs @@ -43,6 +43,31 @@ namespace A public sealed class Blank { public Blank() {} } }"; + private const string UnrelatedNamespaceWithPublicPartialTypes = @" +namespace DiagnosticTests +{ + namespace Foo + { + public sealed class Dog + { + public int Woof { get; set; } + } + } +} +namespace Utilities +{ + public sealed partial class Sandwich + { + private int BreadCount { get; set; } + } + + partial class Sandwich + { + private int BreadCount2 { get; set; } + } +} +"; + // note the below test should only fail if the AssemblyName is "DiagnosticTests.A", this is valid under the default "DiagnosticTests" private const string NamespaceDifferByDot = @" namespace DiagnosticTests.A diff --git a/src/Tests/DiagnosticTests/PositiveData.cs b/src/Tests/DiagnosticTests/PositiveData.cs index 9bd060a98..90b673ac9 100644 --- a/src/Tests/DiagnosticTests/PositiveData.cs +++ b/src/Tests/DiagnosticTests/PositiveData.cs @@ -24,6 +24,30 @@ private class Sandwich } }"; + private const string Valid_UnrelatedNamespaceWithNoPublicTypes2 = @" +namespace DiagnosticTests +{ + namespace Foo + { + public sealed class Dog + { + public int Woof { get; set; } + } + } +} +namespace Utilities +{ + internal partial class Sandwich + { + private int BreadCount { get; set; } + } + + partial class Sandwich + { + private int BreadCount2 { get; set; } + } +}"; + private const string Valid_SubNamespacesWithOverlappingNames = @" namespace DiagnosticTests { diff --git a/src/Tests/DiagnosticTests/UnitTesting.cs b/src/Tests/DiagnosticTests/UnitTesting.cs index 0a793e6dd..d87cc3764 100644 --- a/src/Tests/DiagnosticTests/UnitTesting.cs +++ b/src/Tests/DiagnosticTests/UnitTesting.cs @@ -120,6 +120,7 @@ private static IEnumerable InvalidCases yield return new TestCaseData(PropertyNoGetter, WinRTRules.PrivateGetterRule).SetName("Property. No Get, public Setter"); // namespace tests yield return new TestCaseData(SameNameNamespacesDisjoint, WinRTRules.DisjointNamespaceRule).SetName("Namespace. isn't accessible without Test prefix, doesn't use type"); + yield return new TestCaseData(UnrelatedNamespaceWithPublicPartialTypes, WinRTRules.DisjointNamespaceRule).SetName("Namespace. Component has public types in different namespaces"); yield return new TestCaseData(NamespacesDifferByCase, WinRTRules.NamespacesDifferByCase).SetName("Namespace. names only differ by case"); yield return new TestCaseData(DisjointNamespaces, WinRTRules.DisjointNamespaceRule).SetName("Namespace. isn't accessible without Test prefix, doesn't use type"); yield return new TestCaseData(DisjointNamespaces2, WinRTRules.DisjointNamespaceRule).SetName("Namespace. using type from inaccessible namespace"); @@ -414,7 +415,8 @@ private static IEnumerable ValidCases { get { - yield return new TestCaseData(Valid_UnrelatedNamespaceWithNoPublicTypes).SetName("Valid. Namespace. Helper namespace with no public types"); + yield return new TestCaseData(Valid_UnrelatedNamespaceWithNoPublicTypes).SetName("Valid. Namespace. Helper namespace with no public types"); + yield return new TestCaseData(Valid_UnrelatedNamespaceWithNoPublicTypes2).SetName("Valid. Namespace. Helper namespace with partial types but aren't public"); yield return new TestCaseData(Valid_SubNamespacesWithOverlappingNames).SetName("Valid. Namespace. Overlapping namesepaces names is OK if in different namespaces"); yield return new TestCaseData(Valid_PrivateSetter).SetName("Valid. Property. Private Setter"); yield return new TestCaseData(Valid_RollYourOwnAsyncAction).SetName("Valid. AsyncInterfaces. Implementing your own IAsyncAction"); diff --git a/src/Tests/FunctionalTests/Async/Async.csproj b/src/Tests/FunctionalTests/Async/Async.csproj index 43370b531..812581cc3 100644 --- a/src/Tests/FunctionalTests/Async/Async.csproj +++ b/src/Tests/FunctionalTests/Async/Async.csproj @@ -2,15 +2,14 @@ Exe - net6.0 + $(FunctionalTestsBuildTFMs) x86;x64 - win10-x86;win10-x64 - $(MSBuildProjectDirectory)\..\PublishProfiles\win10-$(Platform).pubxml - true - false + win-x86;win-x64 + $(MSBuildProjectDirectory)\..\PublishProfiles\win-$(Platform).pubxml + diff --git a/src/Tests/FunctionalTests/Async/Program.cs b/src/Tests/FunctionalTests/Async/Program.cs index b43cc2c89..8b8784c4b 100644 --- a/src/Tests/FunctionalTests/Async/Program.cs +++ b/src/Tests/FunctionalTests/Async/Program.cs @@ -1,6 +1,12 @@ using System; +using System.IO; +using System.Runtime.InteropServices.WindowsRuntime; using System.Threading.Tasks; using TestComponentCSharp; +using Windows.Foundation; +using Windows.Storage.Streams; +using Windows.Web.Http; +using WinRT; var instance = new Class(); @@ -57,6 +63,77 @@ } } +var folderPath = Path.GetDirectoryName(AppContext.BaseDirectory); +var file = await Windows.Storage.StorageFile.GetFileFromPathAsync(folderPath + "\\Async.exe"); +var handle = WindowsRuntimeStorageExtensions.CreateSafeFileHandle(file, FileAccess.Read); +if (handle is null) +{ + return 107; +} +handle.Close(); + +handle = null; +var getFolderFromPathAsync = Windows.Storage.StorageFolder.GetFolderFromPathAsync(folderPath); +getFolderFromPathAsync.Completed += (s, e) => +{ + handle = WindowsRuntimeStorageExtensions.CreateSafeFileHandle(s.GetResults(), "TestComponent.dll", FileMode.Open, FileAccess.Read); +}; +await Task.Delay(1000); +if (handle is null) +{ + return 108; +} +handle.Close(); + +using var stream = await file.OpenAsync(Windows.Storage.FileAccessMode.Read).AsTask().ConfigureAwait(continueOnCapturedContext: false); +using var sw = new StreamReader(stream.AsStreamForRead()); +if (string.IsNullOrEmpty(sw.ReadToEnd())) +{ + return 109; +} + +using var fileStream = File.OpenRead(folderPath + "\\Async.exe"); +var randomAccessStream = fileStream.AsRandomAccessStream(); +var ptr = MarshalInterface.FromManaged(randomAccessStream); +if (ptr == IntPtr.Zero) +{ + return 110; +} +var arr = new byte[100]; +var buffer = arr.AsBuffer(); +ptr = MarshalInterface.FromManaged(buffer); +if (ptr == IntPtr.Zero) +{ + return 111; +} + +var asyncOperation = randomAccessStream.ReadAsync(buffer, 50, InputStreamOptions.Partial); +ptr = MarshalInterface>.FromManaged(asyncOperation); +if (ptr == IntPtr.Zero) +{ + return 112; +} + +ptr = MarshalInterface.FromManaged(asyncOperation); +if (ptr == IntPtr.Zero) +{ + return 113; +} + +bool progressCalledWithExpectedResults = false; +var asyncProgressHandler = new AsyncActionProgressHandler((info, progress) => +{ + if (progress.BytesReceived == 3 && progress.TotalBytesToReceive == 4) + { + progressCalledWithExpectedResults = true; + } +}); +Class.UnboxAndCallProgressHandler(asyncProgressHandler); +if (!progressCalledWithExpectedResults) +{ + return 114; +} + return 100; static async Task InvokeAddAsync(Class instance, int lhs, int rhs) diff --git a/src/Tests/FunctionalTests/CCW/CCW.csproj b/src/Tests/FunctionalTests/CCW/CCW.csproj index 481703f07..4a4b75519 100644 --- a/src/Tests/FunctionalTests/CCW/CCW.csproj +++ b/src/Tests/FunctionalTests/CCW/CCW.csproj @@ -1,18 +1,18 @@ - + Exe - net6.0 + $(FunctionalTestsBuildTFMs) x86;x64 - win10-x86;win10-x64 - $(MSBuildProjectDirectory)\..\PublishProfiles\win10-$(Platform).pubxml - true - false + win-x86;win-x64 + $(MSBuildProjectDirectory)\..\PublishProfiles\win-$(Platform).pubxml + true + - \ No newline at end of file + \ No newline at end of file diff --git a/src/Tests/FunctionalTests/CCW/Program.cs b/src/Tests/FunctionalTests/CCW/Program.cs index f436a2da1..6159b431e 100644 --- a/src/Tests/FunctionalTests/CCW/Program.cs +++ b/src/Tests/FunctionalTests/CCW/Program.cs @@ -2,6 +2,14 @@ using TestComponentCSharp; using WinRT.Interop; using WinRT; +using System.Collections.Generic; +using System.Collections; +using System.Runtime.CompilerServices; +using System.Windows.Input; +using System.Runtime.InteropServices; +using System.Collections.Specialized; +using Windows.Foundation; +using Windows.Web.Http; var managedProperties = new ManagedProperties(42); var instance = new Class(); @@ -14,7 +22,7 @@ } // Check for the default interfaces provided by WinRT.Runtime -Guid IID_IMarshal = new Guid("00000003-0000-0000-c000-000000000046"); +Guid IID_IMarshal = new("00000003-0000-0000-c000-000000000046"); IObjectReference ccw = MarshalInterface.CreateMarshaler(managedProperties); ccw.TryAs(IID_IMarshal, out var marshalCCW); if (marshalCCW == null) @@ -23,16 +31,22 @@ } // Check for managed implemented interface to ensure not trimmed. -Guid IID_IUriHandler = new Guid("FF4B4334-2104-537D-812E-67E3856AC7A2"); +Guid IID_IUriHandler = new("FF4B4334-2104-537D-812E-67E3856AC7A2"); ccw.TryAs(IID_IUriHandler, out var uriHandlerCCW); if (uriHandlerCCW == null) { return 103; } +if (!CheckRuntimeClassName(ccw, "TestComponentCSharp.IProperties1")) +{ + return 119; +} + // Ensure that interfaces on the vtable / object don't get trimmed even if unused. -Guid IID_IWarning1 = new Guid("4DB3FA26-4BB1-50EA-8362-98F49651E516"); -Guid IID_IWarningClassOverrides = new Guid("E5635CE4-D483-55AA-86D5-080DC07F0A09"); +Guid IID_IWarning1 = new("4DB3FA26-4BB1-50EA-8362-98F49651E516"); +Guid IID_IWarningClassOverrides = new("E5635CE4-D483-55AA-86D5-080DC07F0A09"); +Guid IID_IArtist = new("B7233F79-63CF-5AFA-A026-E4F1924F17A1"); var managedWarningClass = new ManagedWarningClass(); ccw = MarshalInterface.CreateMarshaler(managedWarningClass); @@ -48,9 +62,335 @@ return 105; } +ccw.TryAs(IID_IArtist, out var artistCCW); +if (artistCCW == null) +{ + return 106; +} + +// Testing for overrided name using attribute specified by author on type. +if (!CheckRuntimeClassName(ccw, "ManagedWarningClass")) +{ + return 120; +} + +var managedWarningClass2 = new ManagedWarningClass2(); +ccw = MarshalInspectable.CreateMarshaler(managedWarningClass2); +ccw.TryAs(IID_IWarning1, out var warningCCW2); +if (warningCCW2 == null) +{ + return 107; +} + +ccw.TryAs(IID_IWarningClassOverrides, out var warningOverrideCCW2); +if (warningOverrideCCW2 == null) +{ + return 108; +} + +Guid IID_IProperties1 = new("4BB22177-718B-57C4-8977-CDF2621C781A"); +Guid IID_IProperties2 = new("6090AE4B-83A1-5474-A8D9-AF9B8C8DBD09"); +var managedInterfaceInheritance = new ManagedInterfaceInheritance(); +ccw = MarshalInspectable.CreateMarshaler(managedInterfaceInheritance); +ccw.TryAs(IID_IProperties1, out var propertiesCCW); +if (propertiesCCW == null) +{ + return 109; +} + +ccw.TryAs(IID_IProperties2, out var properties2CCW); +if (properties2CCW == null) +{ + return 110; +} + +if (!CheckRuntimeClassName(ccw, "TestComponentCSharp.IProperties2")) +{ + return 121; +} + +Guid IID_IlistInt = new("B939AF5B-B45D-5489-9149-61442C1905FE"); +Guid IID_IEnumerable = new("036D2C08-DF29-41AF-8AA2-D774BE62BA6F"); +var intList = new ManagedIntList(); +ccw = MarshalInspectable.CreateMarshaler(intList); +ccw.TryAs(IID_IlistInt, out var listIntCCW); +if (listIntCCW == null) +{ + return 111; +} + +ccw.TryAs(IID_IEnumerable, out var enumerableCCW); +if (enumerableCCW == null) +{ + return 112; +} + +if (!CheckRuntimeClassName(ccw, "Windows.Foundation.Collections.IVector`1")) +{ + return 122; +} + +Guid IID_IEnumerableDerived = new ("A70EC662-9975-51BB-9A28-82A876E01177"); +Guid IID_IEnumerableComposed = new ("BDCEC2FC-5BBE-5A69-989D-222563A811A6"); +Guid IID_IEnumerableIRequiredTwo = new ("10879613-0953-58AC-A6C0-817E28DD5A25"); +var derivedList = new ManagedDerivedList(); +ccw = MarshalInspectable.CreateMarshaler(derivedList); +ccw.TryAs(IID_IEnumerableDerived, out var enumerableDerived); +if (enumerableDerived == null) +{ + return 113; +} + +ccw.TryAs(IID_IEnumerableComposed, out var enumerableComposed); +if (enumerableComposed == null) +{ + return 114; +} + +ccw.TryAs(IID_IEnumerableIRequiredTwo, out var enumerableRequiredTwo); +if (enumerableRequiredTwo == null) +{ + return 115; +} + +if (!CheckRuntimeClassName(ccw, "Windows.Foundation.Collections.IVector`1")) +{ + return 123; +} + +var nestedClass = TestClass2.GetInstance(); +ccw = MarshalInspectable.CreateMarshaler(nestedClass); +ccw.TryAs(IID_IProperties2, out properties2CCW); +if (properties2CCW == null) +{ + return 116; +} + +var genericNestedClass = TestClass2.GetGenericInstance(); +ccw = MarshalInspectable.CreateMarshaler(genericNestedClass); +ccw.TryAs(IID_IProperties2, out properties2CCW); +if (properties2CCW == null) +{ + return 117; +} + +var managedWarningClassList = new List(); +instance.BindableIterableProperty = managedWarningClassList; + +var notifyCollectionChangedActionList = new List(); +instance.BindableIterableProperty = notifyCollectionChangedActionList; + +var nullableDoubleList = new List(); +instance.BindableIterableProperty = nullableDoubleList; + +var nullableDoubleList2 = new List>(); +instance.BindableIterableProperty = nullableDoubleList2; + +var nullableHandleList = new List(); +instance.BindableIterableProperty = nullableHandleList; + +var customCommand = new CustomCommand() as ICommand; +ccw = MarshalInspectable.CreateMarshaler(customCommand); +ccw.TryAs(ABI.System.Windows.Input.ICommandMethods.IID, out var commandCCW); +if (commandCCW == null) +{ + return 118; +} + +if (!CheckRuntimeClassName(ccw, "Microsoft.UI.Xaml.Input.ICommand")) +{ + return 124; +} + +TestClass.TestNestedClass(); + +// These scenarios aren't supported today on AOT, but testing to ensure they +// compile without issues. They should still work fine outside of AOT. +try +{ + TestClass.TestGenericList(); +} +catch(Exception) +{ + if (RuntimeFeature.IsDynamicCodeCompiled) + { + throw; + } +} + + +// Test ICustomProperty +Language language = new Language(); +language.Value = 42; +language[1] = "Bindable"; + +// Used for non-indexer types to avoid passing null. +// Note this isn't checked in non-indexer scenarios. +var ignoredType = typeof(Language); +if (!instance.ValidateBindableProperty(language, "Name", ignoredType, false, true, false, false, typeof(string), null, null, out var retrievedValue) || + !instance.ValidateBindableProperty(language, "Value", ignoredType, false, true, true, false, typeof(int), null, 22, out var retrievedValue2) || + !instance.ValidateBindableProperty(language, "Item", typeof(int), false, true, true, true, typeof(string), 1, "Language", out var retrievedValue3)) +{ + return 125; +} + +// Validate previous values +if ((string)retrievedValue != "Language" || (int)retrievedValue2 != 42 || (string)retrievedValue3 != "Bindable") +{ + return 126; +} + +// Validate if values got set during ValidateBindableProperty via ICustomProperty +if (language.Value != 22 || language[1] != "Language") +{ + return 127; +} + +Language2 language2 = new Language2(); +// Test private property not found +if (instance.ValidateBindableProperty(language2, "Number", ignoredType, true, true, true, false, typeof(int), null, null, out _)) +{ + return 128; +} + +// Test private accessors not found +if (!instance.ValidateBindableProperty(language2, "SetOnly", ignoredType, false, false, true, false, typeof(string), null, "One", out _) || + !instance.ValidateBindableProperty(language2, "PrivateSet", ignoredType, false, true, false, false, typeof(string), null, "Two", out var retrievedValue4) || + !instance.ValidateBindableProperty(language2, "StaticDouble", ignoredType, false, true, true, false, typeof(double), null, 5.0, out var retrievedValue11)) +{ + return 129; +} + +// Set during SetOnly call. +if ((string)retrievedValue4 != "One" || + (double)retrievedValue11 != 4.0 || Language2.StaticDouble != 5.0) +{ + return 130; +} + +Language4 language4 = new Language4(); +// Test internal property not found +if (instance.ValidateBindableProperty(language4, "Name", ignoredType, true, true, false, false, typeof(string), null, null, out _)) +{ + return 131; +} + +// Validate generic scenarios +Language5 language5 = new Language5(); +language5.Value = 5; +if (!instance.ValidateBindableProperty(language5, "Value", ignoredType, false, true, true, false, typeof(int), null, 2, out var retrievedValue5)) +{ + return 132; +} + +if ((int)retrievedValue5 != 5 || language5.Value != 2) +{ + return 133; +} + +Language5 language6 = new Language5(); +language6.Value = language2; +language6.Number = 4; +if (!instance.ValidateBindableProperty(language6, "Value", ignoredType, false, true, true, false, typeof(object), null, language, out var retrievedValue6) || + !instance.ValidateBindableProperty(language6, "Number", ignoredType, false, true, true, false, typeof(int), null, 2, out var retrievedValue7)) +{ + return 133; +} + +if (retrievedValue6 != language2 || language6.Value != language || + (int)retrievedValue7 != 4 || language6.Number != 2) +{ + return 134; +} + +// Validate dervied scenarios +LanguageDervied languageDervied = new LanguageDervied(); +languageDervied.Value = 22; +LanguageDervied2 languageDervied2 = new LanguageDervied2(); +languageDervied2.Value = 11; +languageDervied2.Derived = 22; + +if (!instance.ValidateBindableProperty(languageDervied, "Derived", ignoredType, false, true, false, false, typeof(int), null, null, out var retrievedValue8) || + // Not projected as custom property + instance.ValidateBindableProperty(languageDervied, "Value", ignoredType, true, true, true, false, typeof(int), null, 33, out var _) || + !instance.ValidateBindableProperty(languageDervied2, "Derived", ignoredType, false, true, true, false, typeof(int), null, 2, out var retrievedValue9) || + !instance.ValidateBindableProperty(languageDervied2, "Name", ignoredType, false, true, false, false, typeof(string), null, null, out var retrievedValue10)) +{ + return 135; +} + +if ((int)retrievedValue8 != 4 || + (int)retrievedValue9 != 22 || languageDervied2.Derived != 2 || + (string)retrievedValue10 != "Language") +{ + return 136; +} + +#if NET8_0_OR_GREATER +if (RunAndGetException(() => ComWrappersSupport.CreateCCWForObject(new ManagedOnlyClass())) is not NotSupportedException) +{ + return 137; +} + +if (RunAndGetException(() => ComWrappersSupport.CreateCCWForObject(new ManagedOnlyStruct())) is not NotSupportedException) +{ + return 138; +} +#endif + return 100; -sealed class ManagedProperties : IProperties1, IUriHandler + +[DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] +static extern unsafe char* WindowsGetStringRawBuffer(IntPtr hstring, uint* length); + +[DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] +static extern int WindowsDeleteString(IntPtr hstring); + +#if NET8_0_OR_GREATER +static Exception RunAndGetException(Action action) +{ + try + { + action(); + + return null; + } + catch (Exception e) + { + return e; + } +} +#endif + +unsafe bool CheckRuntimeClassName(IObjectReference objRef, string expected) +{ + objRef.TryAs(IID.IID_IInspectable, out var inspectable); + if (inspectable == null) + { + return false; + } + + IntPtr __retval = default; + try + { + var hr = inspectable.Vftbl.GetRuntimeClassName(inspectable.ThisPtr, &__retval); + if (hr != 0) + { + return false; + } + + uint length; + char* buffer = WindowsGetStringRawBuffer(__retval, &length); + return expected == new string(buffer, 0, (int)length); + } + finally + { + WindowsDeleteString(__retval); + } +} + +sealed partial class ManagedProperties : IProperties1, IUriHandler { private readonly int _value; @@ -69,12 +409,408 @@ public void AddUriHandler(ProvideUri provideUri) void IUriHandler.AddUriHandler(ProvideUri provideUri) => AddUriHandler(provideUri); } -sealed class ManagedWarningClass : WarningClass, IUriHandler +[WinRTRuntimeClassName("ManagedWarningClass")] +sealed partial class ManagedWarningClass : WarningClass, IUriHandler, IArtist { + public int Test => 4; + public void AddUriHandler(ProvideUri provideUri) { _ = provideUri(); } + public void Draw() + { + } + + public void Draw(int _) + { + } + + public int DrawTo() + { + return 0; + } + void IUriHandler.AddUriHandler(ProvideUri provideUri) => AddUriHandler(provideUri); -} \ No newline at end of file +} + +// Used to test interfaces on base class where +// the child class has no WinRT interfaces. +sealed partial class ManagedWarningClass2 : WarningClass +{ +} + +sealed partial class ManagedInterfaceInheritance : IProperties2 +{ + private int _value; + + public int ReadWriteProperty { get => _value; set => _value = value; } +} + +sealed partial class ManagedIntList : IList +{ + public int this[int index] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public int Count => throw new NotImplementedException(); + + public bool IsReadOnly => throw new NotImplementedException(); + + public void Add(int item) + { + throw new NotImplementedException(); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(int item) + { + throw new NotImplementedException(); + } + + public void CopyTo(int[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + public int IndexOf(int item) + { + throw new NotImplementedException(); + } + + public void Insert(int index, int item) + { + throw new NotImplementedException(); + } + + public bool Remove(int item) + { + throw new NotImplementedException(); + } + + public void RemoveAt(int index) + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } +} + +sealed partial class ManagedDerivedList : IList +{ + public TestComponent.Derived this[int index] { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + + public int Count => throw new NotImplementedException(); + + public bool IsReadOnly => throw new NotImplementedException(); + + public void Add(TestComponent.Derived item) + { + throw new NotImplementedException(); + } + + public void Clear() + { + throw new NotImplementedException(); + } + + public bool Contains(TestComponent.Derived item) + { + throw new NotImplementedException(); + } + + public void CopyTo(TestComponent.Derived[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + public int IndexOf(TestComponent.Derived item) + { + throw new NotImplementedException(); + } + + public void Insert(int index, TestComponent.Derived item) + { + throw new NotImplementedException(); + } + + public bool Remove(TestComponent.Derived item) + { + throw new NotImplementedException(); + } + + public void RemoveAt(int index) + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } +} + +sealed class TestClass +{ + // Testing various nested and generic classes on vtable lookup table. + public static void TestNestedClass() + { + var instance = new Class(); + var nestedClassList = new List(); + instance.BindableIterableProperty = nestedClassList; + + var nestedClassList2 = new List(); + instance.BindableIterableProperty = nestedClassList2; + + var nestedClassList3 = new List>(); + instance.BindableIterableProperty = nestedClassList3; + + var nestedClassList4 = new List.NestedClass2>(); + instance.BindableIterableProperty = nestedClassList4; + + var nestedClassList5 = new List.NestedClass2>.NestedClass2>(); + instance.BindableIterableProperty = nestedClassList5; + + var nestedClassList6 = new List.NestedClass3>(); + instance.BindableIterableProperty = nestedClassList6; + } + + public static void TestGenericList() + { + var instance = new Class(); + var nestedClassList = new List(); + instance.BindableIterableProperty = nestedClassList; + } + +#pragma warning disable CsWinRT1028 // Class is not marked partial + sealed class NestedClass : IProperties2 + { + private int _value; + public int ReadWriteProperty { get => _value; set => _value = value; } + + internal sealed class NestedClass2 : IProperties2 + { + private int _value; + public int ReadWriteProperty { get => _value; set => _value = value; } + } + } + + sealed class NestedGenericClass : IProperties2 + { + private int _value; + public int ReadWriteProperty { get => _value; set => _value = value; } + + internal sealed class NestedClass2 : IProperties2 + { + private int _value; + public int ReadWriteProperty { get => _value; set => _value = value; } + } + + internal sealed class NestedClass3 : IProperties2 + { + private int _value; + public int ReadWriteProperty { get => _value; set => _value = value; } + } + } +#pragma warning restore CsWinRT1028 // Class is not marked partial +} + +partial class TestClass2 +{ + private partial class NestedTestClass : IProperties2 + { + private int _value; + public int ReadWriteProperty { get => _value; set => _value = value; } + } + + // Implements non WinRT generic interface to test WinRTExposedType attribute + // generated during these scenarios. + private partial class GenericNestedTestClass : IProperties2, IComparer + { + private int _value; + public int ReadWriteProperty { get => _value; set => _value = value; } + +#nullable enable + public int Compare(T? x, T? y) + { + return 1; + } +#nullable restore + } + + internal static IProperties2 GetInstance() + { + return new NestedTestClass(); + } + + internal static IProperties2 GetGenericInstance() + { + return new GenericNestedTestClass(); + } +} + +class TestClass3 +{ + // Making sure it compiles if the parent class isn't partial, but the actual class is. +#pragma warning disable CsWinRT1028 // Class is not marked partial + partial class NestedTestClass2 : IProperties2 +#pragma warning restore CsWinRT1028 // Class is not marked partial + { + private int _value; + public int ReadWriteProperty { get => _value; set => _value = value; } + } +} +sealed partial class CustomCommand : ICommand +{ + public event EventHandler CanExecuteChanged; + + public bool CanExecute(object parameter) + { + throw new NotImplementedException(); + } + + public void Execute(object parameter) + { + throw new NotImplementedException(); + } +} + +[GeneratedBindableCustomProperty([nameof(Name), nameof(Value)], [typeof(int)])] +partial class Language +{ + private readonly string[] _values = new string[4]; + + public string Name { get; init; } = "Language"; + public int Value { get; set; } + public string this[int i] + { + get => _values[i]; + set => _values[i] = value; + } +} + +[global::WinRT.GeneratedBindableCustomProperty([nameof(Name), nameof(Derived)], [typeof(int)])] +partial class LanguageDervied : Language +{ + public int Derived { get; } = 4; +} + +[WinRT.GeneratedBindableCustomProperty] +partial class LanguageDervied2 : Language +{ + public int Derived { get; set; } +} + +// Testing code compiles when not marked partial +[GeneratedBindableCustomProperty] +#pragma warning disable CsWinRT1028 // Class is not marked partial +class LanguageDervied3 : Language +#pragma warning restore CsWinRT1028 // Class is not marked partial +{ + public int Derived { get; set; } +} + +class ParentClass +{ + // Testing code compiles when not marked partial + [GeneratedBindableCustomProperty] +#pragma warning disable CsWinRT1028 // Class is not marked partial + partial class LanguageDervied3 : Language +#pragma warning restore CsWinRT1028 // Class is not marked partial + { + public int Derived { get; set; } + } +} + + +[GeneratedBindableCustomPropertyAttribute] +sealed partial class Language2 +{ + public string Name { get; } = "Language2"; + public string[] Value { get; set; } + private int Number { get; set; } + public string SetOnly + { + set + { + PrivateSet = value; + } + } + public string PrivateSet { get; private set; } = "PrivateSet"; + public static double StaticDouble { get; set; } = 4.0; + public ManagedProperties ManagedProperties { get; set; } = new(4); +} + +[GeneratedBindableCustomProperty] +sealed partial class Language4 +{ + internal string Name { get; } + private int Number { get; set; } +} + +[GeneratedBindableCustomProperty] +sealed partial class Language5 +{ + private readonly Dictionary _values = new(); + + public T Value { get; set; } + public int Number { get; set; } + public T this[T i] + { + get => _values[i]; + set => _values[i] = value; + } +} + +namespace Test +{ + namespace Test2 + { + sealed partial class Nested + { + [GeneratedBindableCustomProperty([nameof(Value)], [])] + sealed partial class Language3 : IProperties2 + { + private readonly string[] _values = new string[4]; + + public string Name { get; } + public int Value { get; set; } + public string this[int i] + { + get => _values[i]; + set => _values[i] = value; + } + private int Number { get; set; } + public int ReadWriteProperty { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + } + } + } +} + +#if NET8_0_OR_GREATER +[WinRTExposedType(typeof(WinRTManagedOnlyTypeDetails))] +public sealed partial class ManagedOnlyClass +{ +} + +[WinRTExposedType(typeof(WinRTManagedOnlyTypeDetails))] +public partial struct ManagedOnlyStruct +{ +} +#endif \ No newline at end of file diff --git a/src/Tests/FunctionalTests/ClassActivation/ClassActivation.csproj b/src/Tests/FunctionalTests/ClassActivation/ClassActivation.csproj index 43370b531..3dfd13d96 100644 --- a/src/Tests/FunctionalTests/ClassActivation/ClassActivation.csproj +++ b/src/Tests/FunctionalTests/ClassActivation/ClassActivation.csproj @@ -2,15 +2,14 @@ Exe - net6.0 + $(FunctionalTestsBuildTFMs) x86;x64 - win10-x86;win10-x64 - $(MSBuildProjectDirectory)\..\PublishProfiles\win10-$(Platform).pubxml - true - false + win-x86;win-x64 + $(MSBuildProjectDirectory)\..\PublishProfiles\win-$(Platform).pubxml + diff --git a/src/Tests/FunctionalTests/Collections/Collections.csproj b/src/Tests/FunctionalTests/Collections/Collections.csproj index 43370b531..017604d2b 100644 --- a/src/Tests/FunctionalTests/Collections/Collections.csproj +++ b/src/Tests/FunctionalTests/Collections/Collections.csproj @@ -1,18 +1,18 @@ - + Exe - net6.0 + $(FunctionalTestsBuildTFMs) x86;x64 - win10-x86;win10-x64 - $(MSBuildProjectDirectory)\..\PublishProfiles\win10-$(Platform).pubxml - true - false + win-x86;win-x64 + $(MSBuildProjectDirectory)\..\PublishProfiles\win-$(Platform).pubxml + + diff --git a/src/Tests/FunctionalTests/Collections/Program.cs b/src/Tests/FunctionalTests/Collections/Program.cs index cba570661..9b991eb4c 100644 --- a/src/Tests/FunctionalTests/Collections/Program.cs +++ b/src/Tests/FunctionalTests/Collections/Program.cs @@ -1,7 +1,12 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; using System.Linq; +using System.Threading; using test_component_derived.Nested; using TestComponentCSharp; +using Windows.Foundation; var instance = new Class(); @@ -36,6 +41,84 @@ return 101; } +string[] stringArr = new string[] { "apples", "oranges", "pears" }; +string[] stringArr2 = new string[stringArr.Length]; +string[] outStringArr; +string[] retStringArr = instance2.Array12(stringArr, stringArr2, out outStringArr); +if (!AllEqual(stringArr, stringArr2, outStringArr, retStringArr)) +{ + return 101; +} + +TestComponent.Blittable[] blittableArr = new TestComponent.Blittable[] { + new TestComponent.Blittable(1, 2, 3, 4, -5, -6, -7, 8.0f, 9.0, typeof(TestComponent.ITests).GUID), + new TestComponent.Blittable(10, 20, 30, 40, -50, -60, -70, 80.0f, 90.0, typeof(IStringable).GUID) + }; +TestComponent.Blittable[] blittableArr2 = new TestComponent.Blittable[blittableArr.Length]; +TestComponent.Blittable[] outBlittableArr; +TestComponent.Blittable[] retBlittableArr = instance2.Array13(blittableArr, blittableArr2, out outBlittableArr); +if (!AllEqual(blittableArr, blittableArr2, outBlittableArr, retBlittableArr)) +{ + return 101; +} + +#if NET9_0_OR_GREATER + +TestComponent.NonBlittable[] nonBlittableArr = new TestComponent.NonBlittable[] { + new TestComponent.NonBlittable(false, 'X', "First", (long?)PropertyValue.CreateInt64(123)), + new TestComponent.NonBlittable(true, 'Y', "Second", (long?)PropertyValue.CreateInt64(456)), + new TestComponent.NonBlittable(false, 'Z', "Third", (long?)PropertyValue.CreateInt64(789)) + }; +TestComponent.NonBlittable[] nonBlittableArr2 = new TestComponent.NonBlittable[nonBlittableArr.Length]; +TestComponent.NonBlittable[] outNonBlittableArr; +TestComponent.NonBlittable[] retNonBlittableArr = instance2.Array14(nonBlittableArr, nonBlittableArr2, out outNonBlittableArr); +if (!AllEqual(nonBlittableArr, nonBlittableArr2, outNonBlittableArr, retNonBlittableArr)) +{ + return 101; +} + +TestComponent.Nested[] nestedArr = new TestComponent.Nested[]{ + new TestComponent.Nested( + new TestComponent.Blittable(1, 2, 3, 4, -5, -6, -7, 8.0f, 9.0, typeof(TestComponent.ITests).GUID), + new TestComponent.NonBlittable(false, 'X', "First", (long?)PropertyValue.CreateInt64(123))), + new TestComponent.Nested( + new TestComponent.Blittable(10, 20, 30, 40, -50, -60, -70, 80.0f, 90.0, typeof(IStringable).GUID), + new TestComponent.NonBlittable(true, 'Y', "Second", (long?)PropertyValue.CreateInt64(456))), + new TestComponent.Nested( + new TestComponent.Blittable(1, 2, 3, 4, -5, -6, -7, 8.0f, 9.0, typeof(WinRT.IInspectable).GUID), + new TestComponent.NonBlittable(false, 'Z', "Third", (long?)PropertyValue.CreateInt64(789))) + }; +TestComponent.Nested[] nestedArr2 = new TestComponent.Nested[nestedArr.Length]; +TestComponent.Nested[] outNestedArr; +TestComponent.Nested[] retNestedArr = instance2.Array15(nestedArr, nestedArr2, out outNestedArr); +if (!AllEqual(nestedArr, nestedArr2, outNestedArr, retNestedArr)) +{ + return 101; +} + +EnumValue[] enumArr = new EnumValue[] { EnumValue.One, EnumValue.Two }; +instance.EnumsProperty = enumArr; +EnumValue[] retEnumArr = instance.EnumsProperty; +if (!AllEqual(enumArr, retEnumArr)) +{ + return 101; +} + +#endif + +IStringable[] stringableArr = new IStringable[] { + Windows.Data.Json.JsonValue.CreateNumberValue(3), + Windows.Data.Json.JsonValue.CreateNumberValue(4), + Windows.Data.Json.JsonValue.CreateNumberValue(5.0) + }; +IStringable[] stringableArr2 = new IStringable[stringableArr.Length]; +IStringable[] outStringableArr; +IStringable[] retStringableArr = instance2.Array16(stringableArr, stringableArr2, out outStringableArr); +if (!AllEqual(stringableArr, stringableArr2, outStringableArr, retStringableArr)) +{ + return 101; +} + var hierarchyDAsObjectList = HierarchyC.CreateDerivedHierarchyDAsObjectList(); foreach (var hierarchyDAsObject in hierarchyDAsObjectList) { @@ -53,12 +136,351 @@ if (hierarchyDAsHierarchyC.HierarchyB_Method() != "HierarchyC.HierarchyB_Method" || hierarchyDAsHierarchyC.HierarchyA_Method() != "HierarchyB.HierarchyA_Method") { + // This is set in a failure state as we are testing the TestLibrary module having + // an entry on the vtable lookup table without it actually being loaded or used yet. + // So our intention is to not actually set it. This is just to ensure when in this + // scenario we don't run into other crashes from other lookup tables being registered. + instance.BindableIterableProperty = new List(); return 101; } } +var propertySet = Class.PropertySet; +if (propertySet["beta"] is not string str || str != "second") +{ + return 101; +} + +propertySet.Add("test", new short[] { 1, 2, 3, 4 }); +if (propertySet["test"] is not short[] shortArray || shortArray.Length != 4) +{ + return 101; +} + +if (propertySet["delta"] is not byte[] byteArray || byteArray.Length != 4 || byteArray[1] != 2) +{ + return 101; +} + +if (propertySet["echo"] is not Point[] pointArray || pointArray.Length != 3 || pointArray[1].X != 2 || pointArray[1].Y != 2) +{ + return 101; +} + +var types = Class.ListOfTypes; +if (types.Count != 2 || types[0] != typeof(Class)) +{ + return 101; +} + +var cancellationDictionary = new Dictionary(); +instance.BindableIterableProperty = cancellationDictionary; +if (cancellationDictionary != instance.BindableIterableProperty) +{ + return 101; +} + +var observableCollection = new System.Collections.ObjectModel.ObservableCollection(); +instance.BindableIterableProperty = observableCollection; +if (observableCollection != instance.BindableIterableProperty) +{ + return 101; +} + +var typeList = new List(); +instance.BindableIterableProperty = typeList; +if (typeList != instance.BindableIterableProperty) +{ + return 101; +} + +var stringList = new List(); +instance.BindableIterableProperty = stringList; +if (stringList != instance.BindableIterableProperty) +{ + return 101; +} + +var profile = Windows.Networking.Connectivity.NetworkInformation.GetInternetConnectionProfile(); +var names = profile?.GetNetworkNames(); + +List networkNames = new(); +if (names?.Count > 0) +{ + networkNames.AddRange(names); +} + +if (names is IList networkNamesList) +{ + return 101; +} + +var exceptionList = new List(); +instance.BindableIterableProperty = exceptionList; +if (exceptionList != instance.BindableIterableProperty) +{ + return 101; +} + +var exceptionList2 = new List(); +instance.BindableIterableProperty = exceptionList2; +if (exceptionList2 != instance.BindableIterableProperty) +{ + return 101; +} + +instance.BindableIterableProperty = CustomClass.Instances; +if (CustomClass.Instances != instance.BindableIterableProperty) +{ + return 101; +} + +instance.BindableIterableProperty = CustomClass.DictionaryInstance; +instance.BindableIterableProperty = CustomClass.DictionaryInstance2; + +var customObservableCollection = new CustomObservableCollection(); +instance.BindableIterableProperty = customObservableCollection; +if (customObservableCollection != instance.BindableIterableProperty) +{ + return 101; +} + +var customIntObservableCollection = new CustomGenericObservableCollection(); +instance.BindableIterableProperty = customIntObservableCollection; +if (customIntObservableCollection != instance.BindableIterableProperty) +{ + return 101; +} + +var uriList = new List(); +instance.BindableIterableProperty = uriList; +if (uriList != instance.BindableIterableProperty) +{ + return 101; +} + +var dateTimeOffsetList = new List(); +instance.BindableIterableProperty = dateTimeOffsetList; +if (dateTimeOffsetList != instance.BindableIterableProperty) +{ + return 101; +} + +// Test sccenarios where the actual implementation type of the result or its RCW factory +// hasn't been initialized and the statically declared type is a derived generic interface. +var enums = instance.GetEnumIterable(); +int count = 0; +foreach (var curEnum in enums) +{ + count++; +} + +if (count != 2) +{ + return 101; +} + +count = 0; +var disposableClasses = instance.GetClassIterable(); +foreach (var curr in disposableClasses) +{ + count++; +} + +if (count != 2) +{ + return 101; +} + +int sum = 0; +var enumerator = instance.GetIteratorForCollection(expected); +while (enumerator.MoveNext()) +{ + sum += enumerator.Current; +} + +if (sum != 3) +{ + return 101; +} + +sum = 0; + +CustomIterableTest customIterableTest = new CustomIterableTest(); +foreach (var i in customIterableTest) +{ + sum += i; +} + +if (sum != 7) +{ + return 101; +} + +sum = 0; + +var arr = new int[] { 2, 4, 6 }; +CustomIterableTest customIterableTest2 = new CustomIterableTest(arr); +foreach (var i in customIterableTest2) +{ + sum += i; +} + +if (sum != 12) +{ + return 101; +} + +CustomIteratorTest iterator = new CustomIteratorTest(); +iterator.MoveNext(); +if (iterator.Current != 2) +{ + return 101; +} + +sum = 0; + +var customIterableTest3 = CustomIterableTest.CreateWithCustomIterator(); +foreach (var i in customIterableTest3) +{ + sum += i; +} + +if (sum != 7) +{ + return 101; +} + +var nullableDoubleList = new List() { 1, 2, null, 3, 4, null}; +var result = instance.Calculate(nullableDoubleList); +if (result != 10) +{ + return 101; +} + +sum = 0; + +var nullableIntList = instance.GetNullableIntList(); +foreach (var num in nullableIntList) +{ + if (num.HasValue) + { + sum += num.Value; + } +} + +if (sum != 3) +{ + return 101; +} + +// Testing to ensure no exceptions from any of the analyzers while building. +Action s = (a, b) => { _ = a + b; }; +ActionToFunction(s)(2, 3); + +var intToListDict = instance.GetIntToListDictionary(); +intToListDict.Add(2, new List { EnumValue.One, EnumValue.Two }); +intToListDict[4] = new Collection { EnumValue.Two }; +if (intToListDict.Count != 3 || + intToListDict[1].First() != EnumValue.One) +{ + return 101; +} + +if (intToListDict[2].Count != 2 || intToListDict[2].First() != EnumValue.One) +{ + return 101; +} + +if (intToListDict[4].Count != 1 || intToListDict[4].First() != EnumValue.Two) +{ + return 101; +} + +// Make sure for collections of value types that they don't project IEnumerable +// as it isn't a covariant interface. +if (instance.CheckForBindableObjectInterface(new List()) || + instance.CheckForBindableObjectInterface(new List()) || + instance.CheckForBindableObjectInterface(new List()) || + instance.CheckForBindableObjectInterface(new Dictionary())) +{ + return 102; +} + +// Make sure for collections of object types that they do project IEnumerable +// as it is an covariant interface. +if (!instance.CheckForBindableObjectInterface(new List()) || + !instance.CheckForBindableObjectInterface(new List()) || + !instance.CheckForBindableObjectInterface(new List()) || + !instance.CheckForBindableObjectInterface(new List())) +{ + return 103; +} + +var stringArr3 = (string[])Class.BoxedStringArray; +if (stringArr3.Length != 3 || stringArr3[0] != "one" || stringArr3[1] != "two" || stringArr3[2] != "three") +{ + return 104; +} + +var intArr = (int[])Class.BoxedInt32Array; +if (intArr.Length != 3 || intArr[0] != 1 || intArr[1] != 2 || intArr[2] != 3) +{ + return 104; +} + +#if NET9_0_OR_GREATER + +var timeSpanArr = (TimeSpan[])Class.BoxedTimeSpanArray; +if (timeSpanArr.Length != 2 || timeSpanArr[0] != TimeSpan.FromSeconds(10) || timeSpanArr[1] != TimeSpan.FromSeconds(20)) +{ + return 105; +} + +#endif + +var objectArr = (object[])instance.BoxedObjectArray; +if (objectArr.Length != 2 || objectArr[0] is not Class c || c != instance || objectArr[1] is not Class c2 || c2 != instance) +{ + return 105; +} + return 100; static bool SequencesEqual(IEnumerable x, params IEnumerable[] list) => list.All((y) => x.SequenceEqual(y)); static bool AllEqual(T[] x, params T[][] list) => list.All((y) => x.SequenceEqual(y)); + +static Func ActionToFunction(Action action) => + (a1, a2) => + { + action(a1, a2); + return a1; + }; + +sealed partial class CustomClass : INotifyPropertyChanged +{ + public event PropertyChangedEventHandler PropertyChanged; + + public static IReadOnlyList Instances { get; } = new CustomClass[] { }; + + public static IReadOnlyDictionary DictionaryInstance => new Dictionary(); + + public static IReadOnlyDictionary DictionaryInstance2 + { + get + { + return new Dictionary(); + } + } +} + +sealed partial class CustomObservableCollection : System.Collections.ObjectModel.ObservableCollection +{ + public int CustomCount => Items.Count; +} + +sealed partial class CustomGenericObservableCollection : System.Collections.ObjectModel.ObservableCollection +{ + public int CustomCount => Items.Count; +} diff --git a/src/Tests/FunctionalTests/DerivedClassActivation/DerivedClassActivation.csproj b/src/Tests/FunctionalTests/DerivedClassActivation/DerivedClassActivation.csproj index 43370b531..f65cc8b91 100644 --- a/src/Tests/FunctionalTests/DerivedClassActivation/DerivedClassActivation.csproj +++ b/src/Tests/FunctionalTests/DerivedClassActivation/DerivedClassActivation.csproj @@ -1,16 +1,15 @@ - + Exe - net6.0 + $(FunctionalTestsBuildTFMs) x86;x64 - win10-x86;win10-x64 - $(MSBuildProjectDirectory)\..\PublishProfiles\win10-$(Platform).pubxml - true - false + win-x86;win-x64 + $(MSBuildProjectDirectory)\..\PublishProfiles\win-$(Platform).pubxml + diff --git a/src/Tests/FunctionalTests/DerivedClassAsBaseClass/DerivedClassAsBaseClass.csproj b/src/Tests/FunctionalTests/DerivedClassAsBaseClass/DerivedClassAsBaseClass.csproj index 43370b531..812581cc3 100644 --- a/src/Tests/FunctionalTests/DerivedClassAsBaseClass/DerivedClassAsBaseClass.csproj +++ b/src/Tests/FunctionalTests/DerivedClassAsBaseClass/DerivedClassAsBaseClass.csproj @@ -2,15 +2,14 @@ Exe - net6.0 + $(FunctionalTestsBuildTFMs) x86;x64 - win10-x86;win10-x64 - $(MSBuildProjectDirectory)\..\PublishProfiles\win10-$(Platform).pubxml - true - false + win-x86;win-x64 + $(MSBuildProjectDirectory)\..\PublishProfiles\win-$(Platform).pubxml + diff --git a/src/Tests/FunctionalTests/DerivedClassAsBaseClass/Program.cs b/src/Tests/FunctionalTests/DerivedClassAsBaseClass/Program.cs index af9dfdd3c..3165b6e09 100644 --- a/src/Tests/FunctionalTests/DerivedClassAsBaseClass/Program.cs +++ b/src/Tests/FunctionalTests/DerivedClassAsBaseClass/Program.cs @@ -1,5 +1,7 @@ -using test_component_base; +using System; +using test_component_base; using test_component_derived.Nested; +using WinRT; var hierarchyDAsHierarchyC = HierarchyC.CreateDerivedHierarchyD(); if (hierarchyDAsHierarchyC.HierarchyB_Method() != "HierarchyC.HierarchyB_Method" || @@ -15,4 +17,85 @@ return 101; } +// Test RCW being constructed from weak reference via rehydration +var hierarchyEAsHierarchyB = HierarchyC.CreateNonProjectedDerivedHierarchyEAsHierarchyB(); +WeakReference weakRefHierarchyB = new WeakReference(hierarchyEAsHierarchyB); + +// Hold native object alive while letting go of RCW +var ptr = MarshalInspectable.FromManaged(hierarchyEAsHierarchyB); +hierarchyEAsHierarchyB = null; + +for (int i = 0; i < 4; i++) +{ + GC.Collect(); + GC.WaitForPendingFinalizers(); +} + +if (weakRefHierarchyB.TryGetTarget(out var hierarchyE)) +{ + if (hierarchyE.HierarchyB_Method() != "HierarchyC.HierarchyB_Method" || + hierarchyE.HierarchyA_Method() != "HierarchyB.HierarchyA_Method") + { + return 101; + } +} +else +{ + return 102; +} + +// Test RCW being constructed from weak reference again but with a more derived static type +var hierarchyEAsHierarchyC = HierarchyC.CreateNonProjectedDerivedHierarchyEAsHierarchyC(); +WeakReference weakRefHierarchyC = new WeakReference(hierarchyEAsHierarchyC); + +// Hold native object alive while letting go of RCW +ptr = MarshalInspectable.FromManaged(hierarchyEAsHierarchyC); +hierarchyEAsHierarchyC = null; + +for (int i = 0; i < 4; i++) +{ + GC.Collect(); + GC.WaitForPendingFinalizers(); +} + +if (weakRefHierarchyC.TryGetTarget(out var hierarchyE2)) +{ + if (hierarchyE2.HierarchyB_Method() != "HierarchyC.HierarchyB_Method" || + hierarchyE2.HierarchyA_Method() != "HierarchyB.HierarchyA_Method") + { + return 101; + } +} +else +{ + return 102; +} + +// Test inital scenario again +var hierarchyEAsHierarchyB2 = HierarchyC.CreateNonProjectedDerivedHierarchyEAsHierarchyB(); +WeakReference weakRefHierarchyB2 = new WeakReference(hierarchyEAsHierarchyB2); + +// Hold native object alive while letting go of RCW +ptr = MarshalInspectable.FromManaged(hierarchyEAsHierarchyB2); +hierarchyEAsHierarchyB2 = null; + +for (int i = 0; i < 4; i++) +{ + GC.Collect(); + GC.WaitForPendingFinalizers(); +} + +if (weakRefHierarchyB2.TryGetTarget(out var hierarchyE3)) +{ + if (hierarchyE3.HierarchyB_Method() != "HierarchyC.HierarchyB_Method" || + hierarchyE3.HierarchyA_Method() != "HierarchyB.HierarchyA_Method") + { + return 101; + } +} +else +{ + return 102; +} + return 100; \ No newline at end of file diff --git a/src/Tests/FunctionalTests/Directory.Build.props b/src/Tests/FunctionalTests/Directory.Build.props new file mode 100644 index 000000000..3b5a42cd8 --- /dev/null +++ b/src/Tests/FunctionalTests/Directory.Build.props @@ -0,0 +1,36 @@ + + + + + + true + true + false + true + true + true + + + $(WarningsNotAsErrors);CS0067;CA1416;IL2087 + + + $(WarningsNotAsErrors);CsWinRT1034 + + + + true + true + + + diff --git a/src/Tests/FunctionalTests/Directory.Build.targets b/src/Tests/FunctionalTests/Directory.Build.targets new file mode 100644 index 000000000..0ef68f2c3 --- /dev/null +++ b/src/Tests/FunctionalTests/Directory.Build.targets @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/Tests/FunctionalTests/DynamicInterfaceCasting/DynamicInterfaceCasting.csproj b/src/Tests/FunctionalTests/DynamicInterfaceCasting/DynamicInterfaceCasting.csproj index 1929889f0..3c23f59b4 100644 --- a/src/Tests/FunctionalTests/DynamicInterfaceCasting/DynamicInterfaceCasting.csproj +++ b/src/Tests/FunctionalTests/DynamicInterfaceCasting/DynamicInterfaceCasting.csproj @@ -1,18 +1,17 @@ - + Exe - net6.0 + $(FunctionalTestsBuildTFMs) x86;x64 - win10-x86;win10-x64 - $(MSBuildProjectDirectory)\..\PublishProfiles\win10-$(Platform).pubxml - true - false + win-x86;win-x64 + $(MSBuildProjectDirectory)\..\PublishProfiles\win-$(Platform).pubxml true + diff --git a/src/Tests/FunctionalTests/DynamicInterfaceCasting/Program.cs b/src/Tests/FunctionalTests/DynamicInterfaceCasting/Program.cs index dcbb8323f..f026e2d67 100644 --- a/src/Tests/FunctionalTests/DynamicInterfaceCasting/Program.cs +++ b/src/Tests/FunctionalTests/DynamicInterfaceCasting/Program.cs @@ -1,7 +1,63 @@ -using TestComponent; +using System; +using System.Collections.Generic; +using TestComponent; +using Windows.Foundation; using WinRT; using WinRT.Interop; var instance = new Class(); var agileObject = (IAgileObject)(IWinRTObject)instance; -return agileObject != null ? 100 : 101; \ No newline at end of file +if (agileObject == null) +{ + return 101; +} + +TestComponentCSharp.Class instance2 = new TestComponentCSharp.Class(); +var list = new List { 0, 1, 2 }; +var retVal = SetAndGetBoxedValue(instance2, list); +if (list != retVal) +{ + return 102; +} + +var IID_IListInt = new Guid("b939af5b-b45d-5489-9149-61442c1905fe"); +var IID_IEnumeratorInt = new Guid("81a643fb-f51c-5565-83c4-f96425777b66"); +var ccw = MarshalInspectable.CreateMarshaler(retVal); +ccw.TryAs(IID_IListInt, out var iListCCW); +if (iListCCW == null) +{ + return 103; +} + +ccw.TryAs(IID_IEnumeratorInt, out var iEnumerableCCW); +if (iEnumerableCCW == null) +{ + return 104; +} + +IList> list2 = new List>(); +instance2.IterableOfPointIterablesProperty = list2; + +// Ensure that these don't crash but return null +if ((IWinRTObject)instance as IList != null) +{ + return 105; +} + +if ((IWinRTObject)instance as IList != null) +{ + return 106; +} + +return 100; + +object SetAndGetBoxedValue(TestComponentCSharp.Class instance, object val) +{ + instance.ObjectProperty = val; + return instance.ObjectProperty; +} + +class ManagedClass +{ + public int Number { get; set; } +} \ No newline at end of file diff --git a/src/Tests/FunctionalTests/Events/Events.csproj b/src/Tests/FunctionalTests/Events/Events.csproj index 43370b531..812581cc3 100644 --- a/src/Tests/FunctionalTests/Events/Events.csproj +++ b/src/Tests/FunctionalTests/Events/Events.csproj @@ -2,15 +2,14 @@ Exe - net6.0 + $(FunctionalTestsBuildTFMs) x86;x64 - win10-x86;win10-x64 - $(MSBuildProjectDirectory)\..\PublishProfiles\win10-$(Platform).pubxml - true - false + win-x86;win-x64 + $(MSBuildProjectDirectory)\..\PublishProfiles\win-$(Platform).pubxml + diff --git a/src/Tests/FunctionalTests/Events/Program.cs b/src/Tests/FunctionalTests/Events/Program.cs index f798035a8..5c6384e66 100644 --- a/src/Tests/FunctionalTests/Events/Program.cs +++ b/src/Tests/FunctionalTests/Events/Program.cs @@ -1,7 +1,11 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; using System.Linq; +using System.Threading; using TestComponentCSharp; +using Windows.Foundation.Collections; int events_expected = 0; int events_received = 0; @@ -46,9 +50,70 @@ instance.AddUriHandler(managedUriHandler); bool uriMatches = managedUriHandler.Uri == new Uri("http://github.com"); -return events_received == events_expected && uriMatches ? 100 : 101; +instance.StringPropertyChanged += Instance_StringPropertyChanged; +instance.RaiseStringChanged(); +instance.RaiseStringChanged(); +instance.StringPropertyChanged -= Instance_StringPropertyChanged; +instance.RaiseStringChanged(); +events_expected += 2; -class ManagedUriHandler : IUriHandler +instance.EnumStructPropertyChanged += Instance_EnumStructPropertyChanged; +instance.RaiseEnumStructChanged(); +events_expected += 1; + +instance.Event0 += Instance_Event0; +instance.InvokeEvent0(); +instance.Event0 -= Instance_Event0; +instance.InvokeEvent0(); +// This event here from before the unsubscribe and +// the lambda still registered at the top which is +// fired twice. +events_expected += 3; + +instance.PropertyChangedEventHandler += (object sender, PropertyChangedEventArgs args) => +{ +}; + +ProvideInt s = () => 4; +instance.ObjectProperty = s; +bool boxedDelegateMatches = ((ProvideInt)instance.ObjectProperty) == s; + +TestDelegate t = () => true; +instance.ObjectProperty = t; +boxedDelegateMatches &= ((TestDelegate)instance.ObjectProperty) == t; + +System.EventHandler u = (object sender, int args) => { }; +instance.ObjectProperty = u; +boxedDelegateMatches &= ((System.EventHandler)instance.ObjectProperty) == u; + +System.EventHandler v = (object sender, CancellationToken args) => { }; +instance.ObjectProperty = v; +boxedDelegateMatches &= ((System.EventHandler)instance.ObjectProperty) == v; + +TestDelegate[] arr = new TestDelegate[] { t, t }; +instance.ObjectProperty = arr; + +NotifyCollectionChangedEventHandler n = (object sender, NotifyCollectionChangedEventArgs args) => { }; +instance.ObjectProperty = n; + +return events_received == events_expected && uriMatches && boxedDelegateMatches ? 100 : 101; + +void Instance_Event0() +{ + events_received++; +} + +void Instance_EnumStructPropertyChanged(object sender, EnumStruct e) +{ + events_received++; +} + +void Instance_StringPropertyChanged(Class sender, string args) +{ + events_received++; +} + +partial class ManagedUriHandler : IUriHandler { public Uri Uri { get; private set; } @@ -56,4 +121,18 @@ public void AddUriHandler(ProvideUri provideUri) { Uri = provideUri(); } -} \ No newline at end of file +} + +partial class ObservableDictionaryChangedEventArgs : IMapChangedEventArgs +{ + public ObservableDictionaryChangedEventArgs(CollectionChange change, string key) + { + CollectionChange = change; + Key = key; + } + + public CollectionChange CollectionChange { get; private set; } + public string Key { get; private set; } +} + +delegate bool TestDelegate(); diff --git a/src/Tests/FunctionalTests/JsonValueFunctionCalls/JsonValueFunctionCalls.csproj b/src/Tests/FunctionalTests/JsonValueFunctionCalls/JsonValueFunctionCalls.csproj index ddd828fa9..df41ced1b 100644 --- a/src/Tests/FunctionalTests/JsonValueFunctionCalls/JsonValueFunctionCalls.csproj +++ b/src/Tests/FunctionalTests/JsonValueFunctionCalls/JsonValueFunctionCalls.csproj @@ -2,15 +2,14 @@ Exe - net6.0 + $(FunctionalTestsBuildTFMs) x86;x64 - win10-x86;win10-x64 - $(MSBuildProjectDirectory)\..\PublishProfiles\win10-$(Platform).pubxml - true - false + win-x86;win-x64 + $(MSBuildProjectDirectory)\..\PublishProfiles\win-$(Platform).pubxml + diff --git a/src/Tests/FunctionalTests/JsonValueFunctionCalls/Program.cs b/src/Tests/FunctionalTests/JsonValueFunctionCalls/Program.cs index 1800a57ea..2d47eb4aa 100644 --- a/src/Tests/FunctionalTests/JsonValueFunctionCalls/Program.cs +++ b/src/Tests/FunctionalTests/JsonValueFunctionCalls/Program.cs @@ -1,4 +1,5 @@ -using Windows.Foundation; +using System; +using Windows.Foundation; // Static function calls and create RCW for existing object. IStringable[] a = new IStringable[] { @@ -18,4 +19,16 @@ // Class function call result += (int)(a[1] as Windows.Data.Json.JsonValue).GetNumber(); -return result == 16 ? 100 : 101; \ No newline at end of file +CheckBoxedEnum(); + +return result == 17 ? 100 : 101; + +[WinRT.DynamicWindowsRuntimeCast(typeof(TestComponentCSharp.EnumValue))] +void CheckBoxedEnum() +{ + var enumVal = TestComponentCSharp.Class.BoxedEnum; + if (enumVal is TestComponentCSharp.EnumValue val && val == TestComponentCSharp.EnumValue.Two) + { + result += 1; + } +} \ No newline at end of file diff --git a/src/Tests/FunctionalTests/NonWinRT/NonWinRT.csproj b/src/Tests/FunctionalTests/NonWinRT/NonWinRT.csproj new file mode 100644 index 000000000..48e6d2d54 --- /dev/null +++ b/src/Tests/FunctionalTests/NonWinRT/NonWinRT.csproj @@ -0,0 +1,27 @@ + + + + Exe + $(FunctionalTestsBuildTFMs) + x86;x64 + win-x86;win-x64 + $(MSBuildProjectDirectory)\..\PublishProfiles\win-$(Platform).pubxml + true + TEST_UNSAFE_DISABLED + false + + false + + + + + + + + + + + + + + diff --git a/src/Tests/FunctionalTests/NonWinRT/Program.cs b/src/Tests/FunctionalTests/NonWinRT/Program.cs new file mode 100644 index 000000000..988597c6f --- /dev/null +++ b/src/Tests/FunctionalTests/NonWinRT/Program.cs @@ -0,0 +1,85 @@ +using System; +using System.Collections.Generic; +using System.Collections.Specialized; + +// This is a compile test to validate that +// non WinRT scenarios continue to build without issues. + +// Test calling .NET functions which are WinRT custom mappings +// to ensure we can build them without a reference to the Windows SDK projection. +// This is to ensure we do our best to only generate for WinRT scenarios. + +char[] charValues = new[] {'a', 'b', 'c'}; +_= string.Join(',', charValues); + +_ = bool.Parse("true"); + +string[] strValues = new[] { "a", "b", "c" }; +_= string.Concat(strValues); + +List strList = new List(){ "a", "b", "c" }; +_= string.Concat(strList); + +// Test scenarios where we can't tell for sure whether +// it is not a WinRT scenario so we will still generate. But if +// unsafe is disabled, we will not actually generate allowing the +// project to still build. +// This is scoped to debug which is when TEST_UNSAFE_DISABLED is set +// so we can test both scenarios (above with unsafe enabled and +// below with it disabled). +#if TEST_UNSAFE_DISABLED + +CustomConcat(strList); +CustomConcat(strValues); + +TestComponentCSharp.Class c = new TestComponentCSharp.Class(); +c.BindableIterableProperty = strValues; +c.BindableIterableProperty = strList; +c.ObjectProperty = new System.EventHandler((sender, e) => { }); + +IEnumerable ienumString = new List(); +CustomConcat(ienumString); + +static void CustomConcat(IEnumerable strList) +{ + _ = string.Concat(strList); +} + +partial class CustomEnumerable : IEnumerable +{ + public IEnumerator GetEnumerator() => throw new NotImplementedException(); + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() => throw new NotImplementedException(); +} + +partial class CustomNotifyCollectionChanged : INotifyCollectionChanged +{ + public event NotifyCollectionChangedEventHandler CollectionChanged; + + public IEnumerable CustomCollection { get; } = new List(); + + public static IEnumerable GetStringCollection() + { + return new string[] { "a", "b", "c" }; + } + + public Windows.Foundation.IAsyncOperation GetIntAsyncOperation() + { + var task = System.Threading.Tasks.Task.Run(() => + { + System.Threading.Thread.Sleep(100); + return 4; + }); + return task.AsAsyncOperation(); + } + + public Windows.Foundation.IAsyncOperationWithProgress GetDoubleAsyncOperation() + { + return System.Runtime.InteropServices.WindowsRuntime.AsyncInfo.Run(async (cancellationToken, progress) => + { + await System.Threading.Tasks.Task.Delay(100); + return 4.0; + }); + } +} + +#endif \ No newline at end of file diff --git a/src/Tests/FunctionalTests/OptInMode/OptInMode.csproj b/src/Tests/FunctionalTests/OptInMode/OptInMode.csproj new file mode 100644 index 000000000..a0b84d659 --- /dev/null +++ b/src/Tests/FunctionalTests/OptInMode/OptInMode.csproj @@ -0,0 +1,18 @@ + + + + Exe + net8.0 + x86;x64 + win-x86;win-x64 + $(MSBuildProjectDirectory)\..\PublishProfiles\win-$(Platform).pubxml + OptIn + + + + + + + + + diff --git a/src/Tests/FunctionalTests/OptInMode/Program.cs b/src/Tests/FunctionalTests/OptInMode/Program.cs new file mode 100644 index 000000000..d8aa3c8c5 --- /dev/null +++ b/src/Tests/FunctionalTests/OptInMode/Program.cs @@ -0,0 +1,141 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Windows.Input; +using TestComponentCSharp; +using Windows.Foundation; +using Windows.Web.Http; +using WinRT; +using WinRT.Interop; + +[assembly: WinRT.GeneratedWinRTExposedExternalType(typeof(int[]))] +[assembly: GeneratedWinRTExposedExternalType(typeof(Dictionary))] +[assembly: GeneratedWinRTExposedExternalTypeAttribute(typeof(AsyncActionProgressHandler))] + +Class instance = new Class(); +var expected = new int[] { 0, 1, 2 }; +instance.BindableIterableProperty = expected; +if (expected != instance.BindableIterableProperty) +{ + return 101; +} + +var expected2 = new double[] { 0, 1, 2 }; +try +{ + instance.BindableIterableProperty = expected2; + // Shouldn't reach here as we didn't opt-in double[]. + return 101; +} +catch (Exception) +{ +} + +var cancellationDictionary = new Dictionary(); +instance.BindableIterableProperty = cancellationDictionary; +if (cancellationDictionary != instance.BindableIterableProperty) +{ + return 101; +} + +var customCommand = new CustomCommand() as ICommand; +var ccw = MarshalInspectable.CreateMarshaler(customCommand); +ccw.TryAs(ABI.System.Windows.Input.ICommandMethods.IID, out var commandCCW); +if (commandCCW == null) +{ + return 101; +} + +// Didn't opt-in +var customCommand2 = new CustomCommand2() as ICommand; +ccw = MarshalInspectable.CreateMarshaler(customCommand2); +ccw.TryAs(ABI.System.Windows.Input.ICommandMethods.IID, out commandCCW); +if (commandCCW != null) +{ + return 101; +} + +instance.IntProperty = 12; +var async_get_int = instance.GetIntAsync(); +int async_int = 0; +async_get_int.Completed = (info, status) => async_int = info.GetResults(); +async_get_int.GetResults(); +if (async_int != 12) +{ + return 101; +} + +bool progressCalledWithExpectedResults = false; +var asyncProgressHandler = new AsyncActionProgressHandler((info, progress) => +{ + if (progress.BytesReceived == 3 && progress.TotalBytesToReceive == 4) + { + progressCalledWithExpectedResults = true; + } +}); +Class.UnboxAndCallProgressHandler(asyncProgressHandler); +if (!progressCalledWithExpectedResults) +{ + return 101; +} + +// Ensure we can use the IProperties interface from the native side. +var managedProperties = new ManagedProperties(42); +instance.CopyProperties(managedProperties); +if (managedProperties.ReadWriteProperty != instance.ReadWriteProperty) +{ + return 101; +} + +return 100; + +[GeneratedWinRTExposedType] +sealed partial class CustomCommand : ICommand +{ + public event EventHandler CanExecuteChanged; + + public bool CanExecute(object parameter) + { + throw new NotImplementedException(); + } + + public void Execute(object parameter) + { + throw new NotImplementedException(); + } +} + +sealed partial class CustomCommand2 : ICommand +{ + public event EventHandler CanExecuteChanged; + + public bool CanExecute(object parameter) + { + throw new NotImplementedException(); + } + + public void Execute(object parameter) + { + throw new NotImplementedException(); + } +} + +[global::WinRT.GeneratedWinRTExposedTypeAttribute] +sealed partial class ManagedProperties : IProperties1, IUriHandler +{ + private readonly int _value; + + public ManagedProperties(int value) + { + _value = value; + } + + public int ReadWriteProperty => _value; + + public void AddUriHandler(ProvideUri provideUri) + { + _ = provideUri(); + } + + void IUriHandler.AddUriHandler(ProvideUri provideUri) => AddUriHandler(provideUri); +} diff --git a/src/Tests/FunctionalTests/PublishProfiles/win10-arm64.pubxml b/src/Tests/FunctionalTests/PublishProfiles/win-arm64.pubxml similarity index 81% rename from src/Tests/FunctionalTests/PublishProfiles/win10-arm64.pubxml rename to src/Tests/FunctionalTests/PublishProfiles/win-arm64.pubxml index 2dab81aad..a590c9248 100644 --- a/src/Tests/FunctionalTests/PublishProfiles/win10-arm64.pubxml +++ b/src/Tests/FunctionalTests/PublishProfiles/win-arm64.pubxml @@ -1,14 +1,14 @@ - - - - FileSystem - arm64 - win10-arm64 - bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ - True - True - False - True - true - - + + + + FileSystem + arm64 + win-arm64 + bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ + True + True + False + True + true + + diff --git a/src/Tests/FunctionalTests/PublishProfiles/win10-x64.pubxml b/src/Tests/FunctionalTests/PublishProfiles/win-x64.pubxml similarity index 81% rename from src/Tests/FunctionalTests/PublishProfiles/win10-x64.pubxml rename to src/Tests/FunctionalTests/PublishProfiles/win-x64.pubxml index f914afef1..3e4d923a6 100644 --- a/src/Tests/FunctionalTests/PublishProfiles/win10-x64.pubxml +++ b/src/Tests/FunctionalTests/PublishProfiles/win-x64.pubxml @@ -1,14 +1,14 @@ - - - - FileSystem - x64 - win10-x64 - bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ - True - True - False - True - true - - + + + + FileSystem + x64 + win-x64 + bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ + True + True + False + True + true + + diff --git a/src/Tests/FunctionalTests/PublishProfiles/win10-x86.pubxml b/src/Tests/FunctionalTests/PublishProfiles/win-x86.pubxml similarity index 81% rename from src/Tests/FunctionalTests/PublishProfiles/win10-x86.pubxml rename to src/Tests/FunctionalTests/PublishProfiles/win-x86.pubxml index 6a9ac8b05..7b68d9aa0 100644 --- a/src/Tests/FunctionalTests/PublishProfiles/win10-x86.pubxml +++ b/src/Tests/FunctionalTests/PublishProfiles/win-x86.pubxml @@ -1,14 +1,14 @@ - - - - FileSystem - x86 - win10-x86 - bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ - True - True - False - True - true - - + + + + FileSystem + x86 + win-x86 + bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ + True + True + False + True + true + + diff --git a/src/Tests/FunctionalTests/Structs/Structs.csproj b/src/Tests/FunctionalTests/Structs/Structs.csproj index 43370b531..f65cc8b91 100644 --- a/src/Tests/FunctionalTests/Structs/Structs.csproj +++ b/src/Tests/FunctionalTests/Structs/Structs.csproj @@ -1,16 +1,15 @@ - + Exe - net6.0 + $(FunctionalTestsBuildTFMs) x86;x64 - win10-x86;win10-x64 - $(MSBuildProjectDirectory)\..\PublishProfiles\win10-$(Platform).pubxml - true - false + win-x86;win-x64 + $(MSBuildProjectDirectory)\..\PublishProfiles\win-$(Platform).pubxml + diff --git a/src/Tests/FunctionalTests/TestImplementExclusiveTo/TestImplementExclusiveTo.cs b/src/Tests/FunctionalTests/TestImplementExclusiveTo/TestImplementExclusiveTo.cs new file mode 100644 index 000000000..8f8060527 --- /dev/null +++ b/src/Tests/FunctionalTests/TestImplementExclusiveTo/TestImplementExclusiveTo.cs @@ -0,0 +1,12 @@ +using System; +using TestComponentCSharp.TestPublicExclusiveTo; + +namespace TestImplementExclusiveTo +{ + public sealed partial class TestClass : INonUniqueClass, IRegularInterface, INonUniqueClassFactory + { + public int Type => throw new NotImplementedException(); + public string Path => throw new NotImplementedException(); + public int StaticProperty => throw new NotImplementedException(); + } +} diff --git a/src/Tests/FunctionalTests/TestImplementExclusiveTo/TestImplementExclusiveTo.csproj b/src/Tests/FunctionalTests/TestImplementExclusiveTo/TestImplementExclusiveTo.csproj new file mode 100644 index 000000000..3e4ee3ef3 --- /dev/null +++ b/src/Tests/FunctionalTests/TestImplementExclusiveTo/TestImplementExclusiveTo.csproj @@ -0,0 +1,15 @@ + + + + net8.0 + x86;x64 + win-x86;win-x64 + + + + + + + + + diff --git a/src/Tests/FunctionalTests/TestLibrary/Test-Library.csproj b/src/Tests/FunctionalTests/TestLibrary/Test-Library.csproj new file mode 100644 index 000000000..eae541591 --- /dev/null +++ b/src/Tests/FunctionalTests/TestLibrary/Test-Library.csproj @@ -0,0 +1,15 @@ + + + + $(FunctionalTestsBuildTFMs) + x86;x64 + win-x86;win-x64 + + + + + + + + + diff --git a/src/Tests/FunctionalTests/TestLibrary/TestClass.cs b/src/Tests/FunctionalTests/TestLibrary/TestClass.cs new file mode 100644 index 000000000..2113a202e --- /dev/null +++ b/src/Tests/FunctionalTests/TestLibrary/TestClass.cs @@ -0,0 +1,15 @@ +using TestComponentCSharp; + +namespace TestLibrary +{ + public class TestClass + { + // Used to test multiple modules with generated lookup vtables. + public void InitList() + { + Class instance = new Class(); + var expected = new int[] { 0, 1, 2 }; + instance.BindableIterableProperty = expected; + } + } +} diff --git a/src/Tests/HostTest/BadMappedTarget.Host.runtimeconfig.json b/src/Tests/HostTest/BadMappedTarget.Host.runtimeconfig.json index 04a2d0f23..904206592 100644 --- a/src/Tests/HostTest/BadMappedTarget.Host.runtimeconfig.json +++ b/src/Tests/HostTest/BadMappedTarget.Host.runtimeconfig.json @@ -1,10 +1,10 @@ { "runtimeOptions": { - "tfm": "net6.0", + "tfm": "net8.0", "rollForward": "LatestMinor", "framework": { "name": "Microsoft.NETCore.App", - "version": "6.0.0-preview" + "version": "8.0.0" } }, "activatableClasses": { diff --git a/src/Tests/HostTest/ClassNotFound.Host.runtimeconfig.json b/src/Tests/HostTest/ClassNotFound.Host.runtimeconfig.json index 73a4f7d48..12b04bbfe 100644 --- a/src/Tests/HostTest/ClassNotFound.Host.runtimeconfig.json +++ b/src/Tests/HostTest/ClassNotFound.Host.runtimeconfig.json @@ -1,10 +1,10 @@ { "runtimeOptions": { - "tfm": "net6.0", + "tfm": "net8.0", "rollForward": "LatestMinor", "framework": { "name": "Microsoft.NETCore.App", - "version": "6.0.0-preview" + "version": "8.0.0" } }, "activatableClasses": { diff --git a/src/Tests/HostTest/HostTest.vcxproj b/src/Tests/HostTest/HostTest.vcxproj index 3d7ba5e21..cbf8b3761 100644 --- a/src/Tests/HostTest/HostTest.vcxproj +++ b/src/Tests/HostTest/HostTest.vcxproj @@ -1,7 +1,12 @@  - - + + + + + + + Debug @@ -35,6 +40,7 @@ v143 v142 Unicode + true @@ -59,26 +65,26 @@ {ef3326b5-716f-41d2-ab30-4efab30955e2} - TargetFramework=net6.0 + TargetFramework=net8.0 {c6d580c5-7037-4733-b933-916ff400afe2} - TargetFramework=net6.0 + TargetFramework=net8.0 {ffa9a78b-f53f-43ee-af87-24a80f4c330a} - TargetFramework=net6.0 + TargetFramework=net8.0 {0bb8f82d-874e-45aa-bca3-20ce0562164a} - TargetFramework=net6.0 + TargetFramework=net8.0 {7e33bcb7-19c5-4061-981d-ba695322708a} {25244ced-966e-45f2-9711-1f51e951ff89} - TargetFramework=net6.0 + TargetFramework=net8.0 @@ -225,9 +231,15 @@ - - + + + + + + + + @@ -326,10 +338,21 @@ 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/Tests/HostTest/MappedTarget.Host.runtimeconfig.json b/src/Tests/HostTest/MappedTarget.Host.runtimeconfig.json index 79347bff9..e2a488a15 100644 --- a/src/Tests/HostTest/MappedTarget.Host.runtimeconfig.json +++ b/src/Tests/HostTest/MappedTarget.Host.runtimeconfig.json @@ -1,10 +1,10 @@ { "runtimeOptions": { - "tfm": "net6.0", + "tfm": "net8.0", "rollForward": "LatestMinor", "framework": { "name": "Microsoft.NETCore.App", - "version": "6.0.0-preview" + "version": "8.0.0" } }, "activatableClasses": { diff --git a/src/Tests/HostTest/RuntimeFrameworkVersion.csproj b/src/Tests/HostTest/RuntimeFrameworkVersion.csproj index 75f69eb7d..5a4100cc5 100644 --- a/src/Tests/HostTest/RuntimeFrameworkVersion.csproj +++ b/src/Tests/HostTest/RuntimeFrameworkVersion.csproj @@ -1,14 +1,14 @@  - net6.0 + net8.0 diff --git a/src/Tests/HostTest/Test.Host.runtimeconfig.json b/src/Tests/HostTest/Test.Host.runtimeconfig.json index 14f127d48..be5d9aa3e 100644 --- a/src/Tests/HostTest/Test.Host.runtimeconfig.json +++ b/src/Tests/HostTest/Test.Host.runtimeconfig.json @@ -1,10 +1,10 @@ { "runtimeOptions": { - "tfm": "net6.0", + "tfm": "net8.0", "rollForward": "LatestMinor", "framework": { "name": "Microsoft.NETCore.App", - "version": "6.0.0-preview" + "version": "8.0.0" } } } \ No newline at end of file diff --git a/src/Tests/HostTest/WinRT.Host.runtimeconfig.json b/src/Tests/HostTest/WinRT.Host.runtimeconfig.json index 14f127d48..be5d9aa3e 100644 --- a/src/Tests/HostTest/WinRT.Host.runtimeconfig.json +++ b/src/Tests/HostTest/WinRT.Host.runtimeconfig.json @@ -1,10 +1,10 @@ { "runtimeOptions": { - "tfm": "net6.0", + "tfm": "net8.0", "rollForward": "LatestMinor", "framework": { "name": "Microsoft.NETCore.App", - "version": "6.0.0-preview" + "version": "8.0.0" } } } \ No newline at end of file diff --git a/src/Tests/HostTest/packages.config b/src/Tests/HostTest/packages.config index 7c1c2eaf4..4d8aa39f5 100644 --- a/src/Tests/HostTest/packages.config +++ b/src/Tests/HostTest/packages.config @@ -1,6 +1,12 @@  - - + + + + + + + + \ No newline at end of file diff --git a/src/Tests/HostTest/test.cpp b/src/Tests/HostTest/test.cpp index 40516ea9e..f0f01a51b 100644 --- a/src/Tests/HostTest/test.cpp +++ b/src/Tests/HostTest/test.cpp @@ -119,7 +119,7 @@ TEST(HostTest, TargetNotFound) // Mapped target assembly that does not implement the given runtime class TEST(HostTest, ClassNotFound) { - Activate(L"ClassNotFound.manifest", E_NOINTERFACE); + Activate(L"ClassNotFound.manifest", CLASS_E_CLASSNOTAVAILABLE); } // .runtimeconfig.json activatableClass entry with invalid target assembly diff --git a/src/Tests/OOPExe/OOPExe.csproj b/src/Tests/OOPExe/OOPExe.csproj index 16ff5adc0..68a789104 100644 --- a/src/Tests/OOPExe/OOPExe.csproj +++ b/src/Tests/OOPExe/OOPExe.csproj @@ -9,7 +9,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Tests/OOPExe/Program.cs b/src/Tests/OOPExe/Program.cs index 66d6d962d..dbc35c724 100644 --- a/src/Tests/OOPExe/Program.cs +++ b/src/Tests/OOPExe/Program.cs @@ -24,7 +24,11 @@ static void Main(string[] args) var asyncAction = MarshalInterface.FromAbi(Marshal.GetIUnknownForObject(obj)); asyncAction.Completed = Completed; - done.WaitOne(5000); + if (done.WaitOne(20000)) + { + // Allow Completed handler to finish. + Thread.Sleep(5000); + } } public static void Completed(IAsyncAction asyncInfo, AsyncStatus asyncStatus) diff --git a/src/Tests/ObjectLifetimeTests/App.xaml.cs b/src/Tests/ObjectLifetimeTests/App.xaml.cs index f5bda2290..57f94056e 100644 --- a/src/Tests/ObjectLifetimeTests/App.xaml.cs +++ b/src/Tests/ObjectLifetimeTests/App.xaml.cs @@ -14,7 +14,6 @@ using Windows.ApplicationModel; using Windows.ApplicationModel.Activation; using Windows.Foundation; -using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; using Windows.Foundation.Collections; // To learn more about WinUI, the WinUI project structure, @@ -49,8 +48,6 @@ protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs ar m_window = new MainWindow(); m_window.Activate(); - UITestMethodAttribute.DispatcherQueue = m_window.DispatcherQueue; - Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.Run(Environment.CommandLine); } diff --git a/src/Tests/ObjectLifetimeTests/AsyncQueue.cs b/src/Tests/ObjectLifetimeTests/AsyncQueue.cs index 37a131312..a93de85f0 100644 --- a/src/Tests/ObjectLifetimeTests/AsyncQueue.cs +++ b/src/Tests/ObjectLifetimeTests/AsyncQueue.cs @@ -5,13 +5,13 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.System; +using Microsoft.UI.Dispatching; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Data; using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Shapes; - +using Microsoft.Windows.System; namespace ObjectLifetimeTests { diff --git a/src/Tests/ObjectLifetimeTests/CSWinRTInApp.props b/src/Tests/ObjectLifetimeTests/CSWinRTInApp.props deleted file mode 100644 index af1874e8a..000000000 --- a/src/Tests/ObjectLifetimeTests/CSWinRTInApp.props +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - $([MSBuild]::NormalizeDirectory('$(MSBuildProjectDirectory)'))Package.appxmanifest - false - - - - - true - false - - - - - false - - <_AddMrtCoreAssembliesToReferenceCopyLocalPaths>false - - - \ No newline at end of file diff --git a/src/Tests/ObjectLifetimeTests/CSWinRTInApp.targets b/src/Tests/ObjectLifetimeTests/CSWinRTInApp.targets index 3236856bb..2e0b3d13f 100644 --- a/src/Tests/ObjectLifetimeTests/CSWinRTInApp.targets +++ b/src/Tests/ObjectLifetimeTests/CSWinRTInApp.targets @@ -1,125 +1,15 @@ - + - - - - $(GetCopyToOutputDirectoryItemsDependsOn); - AddMicrosoftCSWinRTPayloadFiles - - - - - - - - - - - x86 - $(Platform) - $([MSBuild]::NormalizeDirectory('$(ProjectDir)','$(OutDir)'))AppxContent - - - - - - - - - - <_PriFile Include="$(MicrosoftCSWinRTAppxContent)\Microsoft.UI.Xaml.Controls.pri"/> - - - - + + - - - - - $([System.String]::Copy('%(Identity)').Replace('$(MicrosoftCSWinRTAppxContent)\', '')) - - <_UnfilteredAppxPackagePayload Include ="@(MicrosoftCSWinRTFiles)"> - $([System.String]::Copy('%(Identity)').Replace('$(MicrosoftCSWinRTAppxContent)\', '')) - + - - - - - - - - - - - - - - - - - - /ns:Package/ns:Extensions - - @(AppExtensions->'%(Identity)',' ') - @(MicrosoftCSWinRTExtensions->'%(Identity)',' ') - - - - - - - /ns:Package - - @(AppPackageElements->'%(Identity)',' ') - <Extensions> - @(MicrosoftCSWinRTExtensions->'%(Identity)',' ') - </Extensions> - - - - - - - - \ No newline at end of file diff --git a/src/Tests/ObjectLifetimeTests/Directory.Build.props b/src/Tests/ObjectLifetimeTests/Directory.Build.props index 0754b8961..82357eb5f 100644 --- a/src/Tests/ObjectLifetimeTests/Directory.Build.props +++ b/src/Tests/ObjectLifetimeTests/Directory.Build.props @@ -4,5 +4,4 @@ true - diff --git a/src/Tests/ObjectLifetimeTests/ObjectLifetimeTests.Lifted.csproj b/src/Tests/ObjectLifetimeTests/ObjectLifetimeTests.Lifted.csproj index a6b9e6663..3326f18e3 100644 --- a/src/Tests/ObjectLifetimeTests/ObjectLifetimeTests.Lifted.csproj +++ b/src/Tests/ObjectLifetimeTests/ObjectLifetimeTests.Lifted.csproj @@ -6,13 +6,17 @@ ObjectLifetimeTests.Lifted app.manifest x86;x64;arm64 - win10-x86;win10-x64;win10-arm64 - True - win10-$(Platform).pubxml + win-x86;win-x64;win-arm64 + True + win-$(Platform).pubxml false - false - Desktop + false + MSIX + true + true + true + false @@ -33,6 +37,9 @@ + + + @@ -49,28 +56,25 @@ - 16.10.0-release-20210429-01 build - - 2.2.4-preview-20210513-02 - - - 2.2.4-preview-20210513-02 - - + + + - + - - 0.5.7 + compile - + + all + + - + \ No newline at end of file diff --git a/src/Tests/ObjectLifetimeTests/ObjectLifetimeTests.cs b/src/Tests/ObjectLifetimeTests/ObjectLifetimeTests.cs index 4a56fb6fc..e5b996939 100644 --- a/src/Tests/ObjectLifetimeTests/ObjectLifetimeTests.cs +++ b/src/Tests/ObjectLifetimeTests/ObjectLifetimeTests.cs @@ -6,14 +6,13 @@ using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.System; +using Microsoft.Windows.System; using Microsoft.UI.Xaml; using Microsoft.UI.Xaml.Controls; using Microsoft.UI.Xaml.Data; using Microsoft.UI.Xaml.Media; using Microsoft.UI.Xaml.Shapes; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Microsoft.VisualStudio.TestTools.UnitTesting.AppContainer; using Microsoft.VisualStudio.TestTools.UnitTesting.Logging; using WinRT.Interop; diff --git a/src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win10-arm64.pubxml b/src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win-arm64.pubxml similarity index 92% rename from src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win10-arm64.pubxml rename to src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win-arm64.pubxml index 5c3d986e4..7cb800162 100644 --- a/src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win10-arm64.pubxml +++ b/src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win-arm64.pubxml @@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem arm64 - win10-arm64 + win-arm64 bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ true False diff --git a/src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win10-x64.pubxml b/src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win-x64.pubxml similarity index 92% rename from src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win10-x64.pubxml rename to src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win-x64.pubxml index 352786e6f..a144a836f 100644 --- a/src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win10-x64.pubxml +++ b/src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win-x64.pubxml @@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem x64 - win10-x64 + win-x64 bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ true False diff --git a/src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win10-x86.pubxml b/src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win-x86.pubxml similarity index 92% rename from src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win10-x86.pubxml rename to src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win-x86.pubxml index 68ad82ae0..3d4af2dd6 100644 --- a/src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win10-x86.pubxml +++ b/src/Tests/ObjectLifetimeTests/Properties/PublishProfiles/win-x86.pubxml @@ -6,7 +6,7 @@ https://go.microsoft.com/fwlink/?LinkID=208121. FileSystem x86 - win10-x86 + win-x86 bin\$(Configuration)\$(TargetFramework)\$(RuntimeIdentifier)\publish\ true False diff --git a/src/Tests/ObjectLifetimeTests/Properties/launchSettings.json b/src/Tests/ObjectLifetimeTests/Properties/launchSettings.json index 65052eaa5..5a9f81269 100644 --- a/src/Tests/ObjectLifetimeTests/Properties/launchSettings.json +++ b/src/Tests/ObjectLifetimeTests/Properties/launchSettings.json @@ -1,7 +1,7 @@ { "profiles": { "ObjectLifetimeTests.Lifted": { - "commandName": "AppContainer", + "commandName": "MsixPackage", "DebugEngine": "Mixed" } } diff --git a/src/Tests/SourceGeneratorTest/DiagnosticAnalyzerTests.cs b/src/Tests/SourceGeneratorTest/DiagnosticAnalyzerTests.cs new file mode 100644 index 000000000..186335f60 --- /dev/null +++ b/src/Tests/SourceGeneratorTest/DiagnosticAnalyzerTests.cs @@ -0,0 +1,1528 @@ +using System.Threading.Tasks; +using Generator; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using SourceGeneratorTest.Helpers; + +namespace SourceGeneratorTest; + +[TestClass] +public class DiagnosticAnalyzerTests +{ + [TestMethod] + public async Task CollectionExpression_TargetingConcreteType_DoesNotWarn() + { + const string source = """ + using System.Collections.Generic; + + class Test + { + void M() + { + List a = []; + List b = [1, 2, 3]; + int[] c = []; + int[] d = [1, 2, 3]; + } + } + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotOptimizerEnabled", "auto")]); + } + + [TestMethod] + public async Task CollectionExpression_TargetingInterface_Empty_DoesNotWarn() + { + const string source = """ + using System.Collections.Generic; + + class Test + { + void M() + { + IEnumerable a = []; + ICollection b = []; + IReadOnlyCollection c = []; + IList d = []; + IReadOnlyList e = []; + } + } + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotOptimizerEnabled", "auto")]); + } + + [TestMethod] + public async Task CollectionExpression_TargetingInterface_Mutable_NotEmpty_DoesNotWarn() + { + const string source = """ + using System.Collections.Generic; + + class Test + { + void M(int x, IEnumerable y) + { + ICollection a = [1, 2, 3]; + ICollection b = [x]; + ICollection c = [1, x, ..y]; + IList d = [1, 2, 3]; + IList e = [x]; + IList f = [1, x, ..y]; + } + } + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotOptimizerEnabled", "auto")]); + } + + [TestMethod] + public async Task CollectionExpression_TargetingInterface_WithCollectionBuilder_DoesNotWarn() + { + const string source = """ + using System; + using System.Collections; + using System.Collections.Generic; + using System.Runtime.CompilerServices; + + class Test + { + void M(int x, IEnumerable y) + { + IMyInterface a = []; + IMyInterface b = [1, 2, 3]; + IMyInterface c = [x]; + IMyInterface d = [1, x, ..y]; + } + } + + [CollectionBuilder(typeof(MyInterfaceBuilder), nameof(MyInterfaceBuilder.Create))] + interface IMyInterface : IEnumerable + { + } + + class MyInterface : IMyInterface + { + public IEnumerator GetEnumerator() + { + throw new NotImplementedException(); + } + + IEnumerator IEnumerable.GetEnumerator() + { + throw new NotImplementedException(); + } + } + + class MyInterfaceBuilder + { + public static IMyInterface Create(ReadOnlySpan span) + { + return new MyInterface(); + } + } + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotOptimizerEnabled", "auto")]); + } + + [TestMethod] + public async Task CollectionExpression_TargetingInterface_ReadOnly_NotEmpty_Warns() + { + const string source = """ + using System.Collections.Generic; + + class Test + { + void M(int x, IEnumerable y) + { + IEnumerable a = {|CsWinRT1032:[1, 2, 3]|}; + IEnumerable b = {|CsWinRT1032:[x]|}; + IEnumerable c = {|CsWinRT1032:[1, x, ..y]|}; + IReadOnlyCollection d = {|CsWinRT1032:[1, 2, 3]|}; + IReadOnlyCollection e = {|CsWinRT1032:[x]|}; + IReadOnlyCollection f = {|CsWinRT1032:[1, x, ..y]|}; + IReadOnlyList g = {|CsWinRT1032:[1, 2, 3]|}; + IReadOnlyList h = {|CsWinRT1032:[x]|}; + IReadOnlyList i = {|CsWinRT1032:[1, x, ..y]|}; + } + } + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotOptimizerEnabled", "auto")]); + } + + [TestMethod] + public async Task CollectionExpression_TargetingInterface_ReadOnly_NotEmpty_WithMultipleBuilderTypes_Warns() + { + const string source = """ + using System.Collections.Generic; + + namespace MyApp + { + class Test + { + void M(int x, IEnumerable y) + { + IEnumerable a = {|CsWinRT1032:[1, 2, 3]|}; + } + } + } + + namespace System.Runtime.CompilerServices + { + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, Inherited = false)] + internal sealed class CollectionBuilderAttribute : Attribute + { + public CollectionBuilderAttribute(Type builderType, string methodName) + { + BuilderType = builderType; + MethodName = methodName; + } + + public Type BuilderType { get; } + public string MethodName { get; } + } + } + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotOptimizerEnabled", "auto")]); + } + + [TestMethod] + public async Task ComImportInterfaceCast_ValidCast_DoesNotWarn() + { + const string source = """ + class Test + { + void M(object obj) + { + IC c1 = (IC)obj; + IC c2 = obj as IC; + + if (obj is IC) + { + } + + if (obj is IC c3) + { + } + + if ((object[])obj is [IC c4]) + { + } + + if ((object[])obj is [IC]) + { + } + } + } + + interface IC; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotOptimizerEnabled", "auto"), ("EnableAotAnalyzer", "true")]); + } + + [TestMethod] + [DataRow("true")] + [DataRow("false")] + [DataRow("OptIn")] + public async Task ComImportInterfaceCast_InvalidCast_NotAutoMode_DoesNotWarn(string propertyValue) + { + const string source = """ + using System.Runtime.InteropServices; + + class Test + { + void M(object obj) + { + IC c1 = (IC)obj; + IC c2 = obj as IC; + + if (obj is IC) + { + } + + if (obj is IC c3) + { + } + + if ((object[])obj is [IC c4]) + { + } + + if ((object[])obj is [IC]) + { + } + } + } + + [Guid("8FA8A526-F93B-4891-97D2-E1CC83D1C463")] + [ComImport] + interface IC; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotOptimizerEnabled", propertyValue), ("EnableAotAnalyzer", "true")]); + } + + [TestMethod] + public async Task ComImportInterfaceCast_InvalidCast_NoEnableAotAnalyzer_DoesNotWarn() + { + const string source = """ + using System.Runtime.InteropServices; + + class Test + { + void M(object obj) + { + IC c1 = (IC)obj; + IC c2 = obj as IC; + + if (obj is IC) + { + } + + if (obj is IC c3) + { + } + + if ((object[])obj is [IC c4]) + { + } + + if ((object[])obj is [IC]) + { + } + } + } + + [Guid("8FA8A526-F93B-4891-97D2-E1CC83D1C463")] + [ComImport] + interface IC; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotOptimizerEnabled", "auto"), ("EnableAotAnalyzer", "false")]); + } + + [TestMethod] + public async Task ComImportInterfaceCast_InvalidCast_NoProperty_DoesNotWarn() + { + const string source = """ + using System.Runtime.InteropServices; + + class Test + { + void M(object obj) + { + IC c1 = (IC)obj; + IC c2 = obj as IC; + + if (obj is IC) + { + } + + if (obj is IC c3) + { + } + + if ((object[])obj is [IC c4]) + { + } + + if ((object[])obj is [IC]) + { + } + } + } + + [Guid("8FA8A526-F93B-4891-97D2-E1CC83D1C463")] + [ComImport] + interface IC; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source); + } + + [TestMethod] + public async Task ComImportInterfaceCast_InvalidCast_Warns() + { + const string source = """ + using System.Runtime.InteropServices; + + class Test + { + void M(object obj) + { + IC c1 = {|CsWinRT1033:(IC)obj|}; + IC c2 = {|CsWinRT1033:obj as IC|}; + + if ({|CsWinRT1033:obj is IC|}) + { + } + + if ({|CsWinRT1033:obj is IC c3|}) + { + } + + if ((object[])obj is [{|CsWinRT1033:IC c4|}]) + { + } + + if ((object[])obj is [{|CsWinRT1033:IC|}]) + { + } + } + } + + [Guid("8FA8A526-F93B-4891-97D2-E1CC83D1C463")] + [ComImport] + interface IC; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotOptimizerEnabled", "auto"), ("EnableAotAnalyzer", "true")]); + } + + [TestMethod] + public async Task RuntimeClassCast_ValidCast_DoesNotWarn() + { + const string source = """ + class Test + { + void M(object obj) + { + C c1 = (C)obj; + C c2 = obj as C; + + if (obj is C) + { + } + + if (obj is C c3) + { + } + + if ((object[])obj is [C c4]) + { + } + + if ((object[])obj is [C]) + { + } + } + } + + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + [DataRow("")] + [DataRow("0")] + [DataRow("1")] + [DataRow("2")] + public async Task RuntimeClassCast_InvalidCast_NotLevel3_DoesNotWarn(string propertyValue) + { + const string source = """ + using WinRT; + + class Test + { + void M(object obj) + { + C c1 = (C)obj; + C c2 = obj as C; + + if (obj is C) + { + } + + if (obj is C c3) + { + } + + if ((object[])obj is [C c4]) + { + } + + if ((object[])obj is [C]) + { + } + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", propertyValue)]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_NoProperty_DoesNotWarn() + { + const string source = """ + using WinRT; + + class Test + { + void M(object obj) + { + C c1 = (C)obj; + C c2 = obj as C; + + if (obj is C) + { + } + + if (obj is C c3) + { + } + + if ((object[])obj is [C c4]) + { + } + + if ((object[])obj is [C]) + { + } + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source); + } + + [TestMethod] + public async Task RuntimeClassCast_VerifyNoFalsePositives_DoesNotWarn() + { + const string source = """ + using WinRT; + + class Test + { + void M(object obj) + { + A a1 = null; + A a2 = (A)null; + B b1 = null; + B b2 = (B)null; + B b3 = (B)(A)null; + + if (a1 is null) + { + } + + if (a1 is not null) + { + } + + if (a1 == null) + { + } + + if (a1 != null) + { + } + + A a3 = new(); + B b4 = new(); + + a3 = b4; + a3 = new B(); + a3 = (A)b4; + + object obj2 = (A)b4; + + if (a3 == b4) + { + } + + int i = 42; + E e = (E)i; + int i2 = (int)e; + E e2 = (E)(int)obj; + + E? ne1 = (E?)null; + E? ne2 = (E?)E.A; + + if (ne1 is E) + { + } + + if (ne1 is E e3) + { + } + + if ((E?[])obj is [E]) + { + } + + if ((E?[])obj is [E e4]) + { + } + } + } + + [WindowsRuntimeType("SomeContract")] + class A; + + [WindowsRuntimeType("SomeContract")] + class B : A; + + [WindowsRuntimeType("SomeContract")] + enum E + { + A, + B + } + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_WithDynamicWindowsRuntimeCast_Method_DoesNotWarn() + { + const string source = """ + using System.Diagnostics.CodeAnalysis; + using WinRT; + + class Test + { + [DynamicWindowsRuntimeCast(typeof(C))] + void M(object obj) + { + C c1 = (C)obj; + C c2 = obj as C; + + if (obj is C) + { + } + + if (obj is C c3) + { + } + + if ((object[])obj is [C c4]) + { + } + + if ((object[])obj is [C]) + { + } + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_EnumType_WithDynamicWindowsRuntimeCast_Method_DoesNotWarn() + { + const string source = """ + using System.Diagnostics.CodeAnalysis; + using WinRT; + + class Test + { + [DynamicWindowsRuntimeCast(typeof(E))] + void M(object obj) + { + E e1 = (E)obj; + + if (obj is E) + { + } + + if (obj is E e2) + { + } + + if ((object[])obj is [E e3]) + { + } + + if ((object[])obj is [E]) + { + } + } + } + + [WindowsRuntimeType("SomeContract")] + enum E + { + A, + B + } + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_WithDynamicWindowsRuntimeCast_Lambda_DoesNotWarn() + { + const string source = """ + using System; + using System.Diagnostics.CodeAnalysis; + using WinRT; + + class Test + { + void M() + { + Action l = [DynamicWindowsRuntimeCast(typeof(C))] (obj) => + { + C c1 = (C)obj; + C c2 = obj as C; + + if (obj is C) + { + } + + if (obj is C c3) + { + } + + if ((object[])obj is [C c4]) + { + } + + if ((object[])obj is [C]) + { + } + }; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_WithDynamicWindowsRuntimeCast_Lambda_AttributeOnParent_DoesNotWarn() + { + const string source = """ + using System; + using System.Diagnostics.CodeAnalysis; + using WinRT; + + class Test + { + [DynamicWindowsRuntimeCast(typeof(C))] + void M() + { + Action l = obj => + { + C c1 = (C)obj; + C c2 = obj as C; + + if (obj is C) + { + } + + if (obj is C c3) + { + } + + if ((object[])obj is [C c4]) + { + } + + if ((object[])obj is [C]) + { + } + }; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_WithDynamicWindowsRuntimeCast_LocalMethod_DoesNotWarn() + { + const string source = """ + using System.Diagnostics.CodeAnalysis; + using WinRT; + + class Test + { + void M() + { + [DynamicWindowsRuntimeCast(typeof(C))] + void F(object obj) + { + C c1 = (C)obj; + C c2 = obj as C; + + if (obj is C) + { + } + + if (obj is C c3) + { + } + + if ((object[])obj is [C c4]) + { + } + + if ((object[])obj is [C]) + { + } + }; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_WithDynamicWindowsRuntimeCast_LocalMethod_AttributeOnParent_DoesNotWarn() + { + const string source = """ + using System.Diagnostics.CodeAnalysis; + using WinRT; + + class Test + { + [DynamicWindowsRuntimeCast(typeof(C))] + void M() + { + void F(object obj) + { + C c1 = (C)obj; + C c2 = obj as C; + + if (obj is C) + { + } + + if (obj is C c3) + { + } + + if ((object[])obj is [C c4]) + { + } + + if ((object[])obj is [C]) + { + } + }; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_WithDynamicWindowsRuntimeCast_LambdaInDictionaryInitializer_AttributeOnParentMethod_DoesNotWarn() + { + const string source = """ + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using WinRT; + + class Test + { + [DynamicWindowsRuntimeCast(typeof(C))] + void M1() + { + var x = new Dictionary> + { + { 42, obj => Console.WriteLine(obj is C) } + }; + } + + [DynamicWindowsRuntimeCast(typeof(C))] + void M2() + { + var x = new Dictionary> + { + [42] = obj => Console.WriteLine(obj is C) + }; + } + + [DynamicWindowsRuntimeCast(typeof(C))] + void M3() + { + var x = new Dictionary)> + { + { 42, (typeof(int), obj => Console.WriteLine(obj is C)) } + }; + } + + [DynamicWindowsRuntimeCast(typeof(C))] + void M4() + { + var x = new Dictionary)> + { + [42] = (typeof(int), obj => Console.WriteLine(obj is C)) + }; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_WithDynamicWindowsRuntimeCast_LambdaInDictionaryInitializer_AttributeOnParentField_DoesNotWarn() + { + const string source = """ + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using WinRT; + + class Test + { + [DynamicWindowsRuntimeCast(typeof(C))] + private static readonly Dictionary> F1 = new() + { + { 42, obj => Console.WriteLine(obj is C) } + }; + + [DynamicWindowsRuntimeCast(typeof(C))] + private static readonly Dictionary> F2 = new() + { + [42] = obj => Console.WriteLine(obj is C) + }; + + [DynamicWindowsRuntimeCast(typeof(C))] + private static readonly Dictionary)> F3 = new() + { + { 42, (typeof(int), obj => Console.WriteLine(obj is C)) } + }; + + [DynamicWindowsRuntimeCast(typeof(C))] + private static readonly Dictionary)> F4 = new Dictionary)> + { + [42] = (typeof(int), obj => Console.WriteLine(obj is C)) + }; + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_WithDynamicWindowsRuntimeCast_PropertyAccessors_DoesNotWarn() + { + const string source = """ + using System; + using System.Collections.Generic; + using System.Diagnostics.CodeAnalysis; + using WinRT; + + class Test + { + private object _obj; + private C _c; + + public C P1 + { + [DynamicWindowsRuntimeCast(typeof(C))] + get => (C)_obj; + } + + public C P2 + { + [DynamicWindowsRuntimeCast(typeof(C))] + get => (C)_obj; + + [DynamicWindowsRuntimeCast(typeof(C))] + set => _c = (C)_obj; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InterpolatedHandlerArgument_DoesNotWarn() + { + const string source = """ + using System.Runtime.CompilerServices; + using WinRT; + + class Test + { + public void M() + { + D d = null; + + d.UseC($""); + } + } + + public static class DExtensions + { + public static void UseC(this D d, [InterpolatedStringHandlerArgument("d")] ref CHandler handler) + { + } + } + + [InterpolatedStringHandler] + public ref struct CHandler + { + public CHandler(int literalLength, int formattedCount, C arg2) + { + } + } + + [WindowsRuntimeType("SomeContract")] + public class C; + + [WindowsRuntimeType("SomeContract")] + public class D : C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_WithDynamicWindowsRuntimeCast_Method_WrongType_Warns() + { + const string source = """ + using System.Diagnostics.CodeAnalysis; + using WinRT; + + class Test + { + [DynamicWindowsRuntimeCast(typeof(string))] + void M(object obj) + { + C c1 = {|CsWinRT1034:(C)obj|}; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_Warns() + { + const string source = """ + using WinRT; + + class Test + { + private object _obj; + private C _c; + + public C P1 => {|CsWinRT1034:(C)_obj|}; + + public C P2 + { + get => {|CsWinRT1034:(C)_obj|}; + set => _c = {|CsWinRT1034:(C)_obj|}; + } + + void M(object obj) + { + C c1 = {|CsWinRT1034:(C)obj|}; + C c2 = {|CsWinRT1034:obj as C|}; + + if ({|CsWinRT1034:obj is C|}) + { + } + + if ({|CsWinRT1034:obj is C c3|}) + { + } + + if ((object[])obj is [{|CsWinRT1034:C c4|}]) + { + } + + if ((object[])obj is [{|CsWinRT1034:C|}]) + { + } + + D d1 = {|CsWinRT1034:(D)c1|}; + D d2 = {|CsWinRT1034:c1 as D|}; + + if ({|CsWinRT1034:c1 is D|}) + { + } + + if ({|CsWinRT1034:c1 is D d3|}) + { + } + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + + [WindowsRuntimeType("SomeContract")] + class D : C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_EnumType_Warns() + { + const string source = """ + using WinRT; + + class Test + { + void M(object obj) + { + E e1 = {|CsWinRT1035:(E)obj|}; + + if ({|CsWinRT1035:obj is E|}) + { + } + + if ({|CsWinRT1035:obj is E e2|}) + { + } + + if ((object[])obj is [{|CsWinRT1035:E e3|}]) + { + } + + if ((object[])obj is [{|CsWinRT1035:E|}]) + { + } + } + } + + [WindowsRuntimeType("SomeContract")] + enum E + { + A, + B + } + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_EnumType_Nullable_Warns() + { + const string source = """ + using WinRT; + + class Test + { + void M(object obj) + { + E? e1 = {|CsWinRT1035:(E?)obj|}; + } + } + + [WindowsRuntimeType("SomeContract")] + enum E + { + A, + B + } + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_SwitchStatement_NoAttribute_Warns() + { + const string source = """ + using WinRT; + + class Test + { + int M(object obj) + { + switch (obj) + { + case {|CsWinRT1034:C|}: return 42; + default: return 0; + } + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_SwitchStatement_WithAttribute_DoesNotWarn() + { + const string source = """ + using WinRT; + + class Test + { + [DynamicWindowsRuntimeCast(typeof(C))] + int M(object obj) + { + switch (obj) + { + case C: return 42; + default: return 0; + } + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_SwitchStatement_WithDeclaration_NoAttribute_Warns() + { + const string source = """ + using WinRT; + + class Test + { + int M(object obj) + { + switch (obj) + { + case {|CsWinRT1034:C c|}: return c.GetHashCode(); + default: return 0; + } + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_SwitchStatement_WithDeclaration_WithAttribute_DoesNotWarn() + { + const string source = """ + using WinRT; + + class Test + { + [DynamicWindowsRuntimeCast(typeof(C))] + int M(object obj) + { + switch (obj) + { + case C c: return c.GetHashCode(); + default: return 0; + } + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_SwitchExpression_NoAttribute_Warns() + { + const string source = """ + using WinRT; + + class Test + { + int M(object obj) + { + return obj switch + { + {|CsWinRT1034:C|} => 42, + _ => 0 + }; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_SwitchExpression_WithAttribute_DoesNotWarn() + { + const string source = """ + using WinRT; + + class Test + { + [DynamicWindowsRuntimeCast(typeof(C))] + int M(object obj) + { + return obj switch + { + C => 42, + _ => 0 + }; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_SwitchExpression_WithCondition_NoAttribute_Warns() + { + const string source = """ + using WinRT; + + class Test + { + int M(object obj) + { + return obj switch + { + { } when {|CsWinRT1034:obj is C|} => 42, + _ => 0 + }; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_SwitchExpression_WithCondition_WithAttribute_DoesNotWarn() + { + const string source = """ + using WinRT; + + class Test + { + [DynamicWindowsRuntimeCast(typeof(C))] + int M(object obj) + { + return obj switch + { + { } when obj is C => 42, + _ => 0 + }; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_SwitchExpression_WithConditionAndDeclaration_NoAttribute_Warns() + { + const string source = """ + using WinRT; + + class Test + { + int M(object obj) + { + return obj switch + { + { } when {|CsWinRT1034:obj is C c|} => c.GetHashCode(), + _ => 0 + }; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_SwitchExpression_WithConditionAndDeclaration_WithAttribute_DoesNotWarn() + { + const string source = """ + using WinRT; + + class Test + { + [DynamicWindowsRuntimeCast(typeof(C))] + int M(object obj) + { + return obj switch + { + { } when obj is C c => c.GetHashCode(), + _ => 0 + }; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_SwitchExpression_WithTuple_NoAttribute_Warns() + { + const string source = """ + using WinRT; + + class Test + { + int M(object obj) + { + return obj switch + { + ({|CsWinRT1034:C|}, _) => 42, + _ => 0 + }; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_SwitchExpression_WithTuple_WithAttribute_DoesNotWarn() + { + const string source = """ + using WinRT; + + class Test + { + [DynamicWindowsRuntimeCast(typeof(C))] + int M(object obj) + { + return obj switch + { + (C, _) => 42, + _ => 0 + }; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_SwitchExpression_WithTuple2_NoAttribute_Warns() + { + const string source = """ + using WinRT; + + class Test + { + int M(object obj, object obj2) + { + return (obj, obj2) switch + { + ({|CsWinRT1034:C c|}, _) => c.GetHashCode(), + _ => 0 + }; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } + + [TestMethod] + public async Task RuntimeClassCast_InvalidCast_SwitchExpression_WithTuple2_WithAttribute_DoesNotWarn() + { + const string source = """ + using WinRT; + + class Test + { + [DynamicWindowsRuntimeCast(typeof(C))] + int M(object obj, object obj2) + { + return (obj, obj2) switch + { + (C c, _) => c.GetHashCode(), + _ => 0 + }; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + await CSharpAnalyzerTest.VerifyAnalyzerAsync(source, editorconfig: [("CsWinRTAotWarningLevel", "3")]); + } +} \ No newline at end of file diff --git a/src/Tests/SourceGeneratorTest/Helpers/CSharpAnalyzerTest{TAnalyzer}.cs b/src/Tests/SourceGeneratorTest/Helpers/CSharpAnalyzerTest{TAnalyzer}.cs new file mode 100644 index 000000000..64ee9aaec --- /dev/null +++ b/src/Tests/SourceGeneratorTest/Helpers/CSharpAnalyzerTest{TAnalyzer}.cs @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// Ported from 'CSharpAnalyzerTest' 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/tests/ComputeSharp.Tests.SourceGenerators/Helpers/CSharpAnalyzerTest%7BTAnalyzer%7D.cs. + +using System; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.CodeAnalysis.Text; +using WinRT; + +namespace SourceGeneratorTest.Helpers; + +/// +/// A custom that uses a specific C# language version to parse code. +/// +/// The type of the analyzer to test. +internal sealed class CSharpAnalyzerTest : CSharpAnalyzerTest + where TAnalyzer : DiagnosticAnalyzer, new() +{ + /// + /// Whether to enable unsafe blocks. + /// + private readonly bool _allowUnsafeBlocks; + + /// + /// The C# language version to use to parse code. + /// + private readonly LanguageVersion _languageVersion; + + /// + /// Creates a new instance with the specified paramaters. + /// + /// Whether to enable unsafe blocks. + /// The C# language version to use to parse code. + private CSharpAnalyzerTest(bool allowUnsafeBlocks, LanguageVersion languageVersion) + { + _allowUnsafeBlocks = allowUnsafeBlocks; + _languageVersion = languageVersion; + } + + /// + protected override CompilationOptions CreateCompilationOptions() + { + return new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: _allowUnsafeBlocks); + } + + /// + protected override ParseOptions CreateParseOptions() + { + return new CSharpParseOptions(_languageVersion, DocumentationMode.Diagnose); + } + + /// + /// The source code to analyze. + /// The .editorconfig properties to use. + public static Task VerifyAnalyzerAsync(string source, params (string PropertyName, object PropertyValue)[] editorconfig) + { + CSharpAnalyzerTest test = new(true, LanguageVersion.Latest) { TestCode = source }; + + test.TestState.ReferenceAssemblies = ReferenceAssemblies.Net.Net80; + test.TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(ComWrappersSupport).Assembly.Location)); + + // Add any editorconfig properties, if present + if (editorconfig.Length > 0) + { + test.SolutionTransforms.Add((solution, projectId) => + solution.AddAnalyzerConfigDocument( + DocumentId.CreateNewId(projectId), + "CsWinRTSourceGeneratorTest.editorconfig", + SourceText.From($""" + is_global = true + {string.Join(Environment.NewLine, editorconfig.Select(static p => $"build_property.{p.PropertyName} = {p.PropertyValue}"))} + """, + Encoding.UTF8), + filePath: "/CsWinRTSourceGeneratorTest.editorconfig")); + } + + return test.RunAsync(CancellationToken.None); + } +} \ No newline at end of file diff --git a/src/Tests/SourceGeneratorTest/Helpers/CSharpCodeFixTest{TAnalyzer, TCodeFixer}.cs b/src/Tests/SourceGeneratorTest/Helpers/CSharpCodeFixTest{TAnalyzer, TCodeFixer}.cs new file mode 100644 index 000000000..a79999a0d --- /dev/null +++ b/src/Tests/SourceGeneratorTest/Helpers/CSharpCodeFixTest{TAnalyzer, TCodeFixer}.cs @@ -0,0 +1,60 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CodeFixes; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Testing; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Testing; +using WinRT; + +namespace SourceGeneratorTest.Helpers; + +/// +/// A custom that uses a specific C# language version to parse code. +/// +/// The type of the analyzer to produce diagnostics. +/// The type of code fix to test. +internal sealed class CSharpCodeFixTest : CSharpCodeFixTest + where TAnalyzer : DiagnosticAnalyzer, new() + where TCodeFixer : CodeFixProvider, new() +{ + /// + /// The C# language version to use to parse code. + /// + private readonly LanguageVersion languageVersion; + + /// + /// Creates a new instance with the specified parameters. + /// + /// The C# language version to use to parse code. + /// The .editorconfig properties to use. + public CSharpCodeFixTest(LanguageVersion languageVersion, params (string PropertyName, object PropertyValue)[] editorconfig) + { + this.languageVersion = languageVersion; + + ReferenceAssemblies = ReferenceAssemblies.Net.Net80; + TestState.AdditionalReferences.Add(MetadataReference.CreateFromFile(typeof(ComWrappersSupport).Assembly.Location)); + + // Add any editorconfig properties, if present + if (editorconfig.Length > 0) + { + TestState.AnalyzerConfigFiles.Add(( + filename: "/CsWinRTSourceGeneratorTest.editorconfig", + content: $""" + is_global = true + {string.Join(Environment.NewLine, editorconfig.Select(static p => $"build_property.{p.PropertyName} = {p.PropertyValue}"))} + """)); + } + } + + /// + protected override ParseOptions CreateParseOptions() + { + return new CSharpParseOptions(this.languageVersion, DocumentationMode.Diagnose); + } +} diff --git a/src/Tests/SourceGeneratorTest/RuntimeClassCastCodeFixerTests.cs b/src/Tests/SourceGeneratorTest/RuntimeClassCastCodeFixerTests.cs new file mode 100644 index 000000000..00c7dfb9a --- /dev/null +++ b/src/Tests/SourceGeneratorTest/RuntimeClassCastCodeFixerTests.cs @@ -0,0 +1,765 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using CSharpCodeFixTest = SourceGeneratorTest.Helpers.CSharpCodeFixTest< + Generator.RuntimeClassCastAnalyzer, + Generator.RuntimeClassCastCodeFixer>; + +namespace SourceGeneratorTest; + +[TestClass] +public class RuntimeClassCastCodeFixerTests +{ + [TestMethod] + public async Task SingleCast_Method() + { + const string original = """ + using WinRT; + + namespace MyApp; + + public class Program + { + public void M(object obj) + { + C c = {|CsWinRT1034:(C)obj|}; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + const string @fixed = """ + using WinRT; + + namespace MyApp; + + public class Program + { + [DynamicWindowsRuntimeCast(typeof(C))] + public void M(object obj) + { + C c = (C)obj; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + CSharpCodeFixTest test = new(LanguageVersion.CSharp13, editorconfig: [("CsWinRTAotWarningLevel", "3")]) + { + TestCode = original, + FixedCode = @fixed + }; + + await test.RunAsync(); + } + + [TestMethod] + public async Task MultipleCasts_Method() + { + const string original = """ + using WinRT; + + namespace MyApp; + + public class Program + { + public void M(object obj) + { + C c = {|CsWinRT1034:(C)obj|}; + D d = {|CsWinRT1034:(D)obj|}; + E e = {|CsWinRT1035:(E)obj|}; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + + [WindowsRuntimeType("SomeContract")] + class D; + + [WindowsRuntimeType("SomeContract")] + enum E + { + A, + B + } + """; + + const string @fixed = """ + using WinRT; + + namespace MyApp; + + public class Program + { + [DynamicWindowsRuntimeCast(typeof(C))] + [DynamicWindowsRuntimeCast(typeof(D))] + [DynamicWindowsRuntimeCast(typeof(E))] + public void M(object obj) + { + C c = (C)obj; + D d = (D)obj; + E e = (E)obj; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + + [WindowsRuntimeType("SomeContract")] + class D; + + [WindowsRuntimeType("SomeContract")] + enum E + { + A, + B + } + """; + + CSharpCodeFixTest test = new(LanguageVersion.CSharp13, editorconfig: [("CsWinRTAotWarningLevel", "3")]) + { + TestCode = original, + FixedCode = @fixed + }; + + await test.RunAsync(); + } + + [TestMethod] + public async Task MultipleCasts_Method_WithDuplicateTypes() + { + const string original = """ + using WinRT; + + namespace MyApp; + + public class Program + { + public void M(object obj) + { + C c1 = {|CsWinRT1034:(C)obj|}; + C c2 = {|CsWinRT1034:(C)obj|}; + D d = {|CsWinRT1034:(D)obj|}; + E e1 = {|CsWinRT1035:(E)obj|}; + E e2 = {|CsWinRT1035:(E)obj|}; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + + [WindowsRuntimeType("SomeContract")] + class D; + + [WindowsRuntimeType("SomeContract")] + enum E + { + A, + B + } + """; + + const string @fixed = """ + using WinRT; + + namespace MyApp; + + public class Program + { + [DynamicWindowsRuntimeCast(typeof(C))] + [DynamicWindowsRuntimeCast(typeof(D))] + [DynamicWindowsRuntimeCast(typeof(E))] + public void M(object obj) + { + C c1 = (C)obj; + C c2 = (C)obj; + D d = (D)obj; + E e1 = (E)obj; + E e2 = (E)obj; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + + [WindowsRuntimeType("SomeContract")] + class D; + + [WindowsRuntimeType("SomeContract")] + enum E + { + A, + B + } + """; + + CSharpCodeFixTest test = new(LanguageVersion.CSharp13, editorconfig: [("CsWinRTAotWarningLevel", "3")]) + { + TestCode = original, + FixedCode = @fixed + }; + + await test.RunAsync(); + } + + [TestMethod] + public async Task MultipleCasts_Method_WithTriviaAndLeadingAttributes() + { + const string original = """ + using System; + using WinRT; + + namespace MyApp; + + public class Program + { + /// + /// Blah. + /// + [Dummy] + public void M(object obj) + { + C c = {|CsWinRT1034:(C)obj|}; + D d = {|CsWinRT1034:(D)obj|}; + E e = {|CsWinRT1035:(E)obj|}; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + + [WindowsRuntimeType("SomeContract")] + class D; + + [WindowsRuntimeType("SomeContract")] + enum E + { + A, + B + } + + public class DummyAttribute : Attribute; + """; + + const string @fixed = """ + using System; + using WinRT; + + namespace MyApp; + + public class Program + { + /// + /// Blah. + /// + [Dummy] + [DynamicWindowsRuntimeCast(typeof(C))] + [DynamicWindowsRuntimeCast(typeof(D))] + [DynamicWindowsRuntimeCast(typeof(E))] + public void M(object obj) + { + C c = (C)obj; + D d = (D)obj; + E e = (E)obj; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + + [WindowsRuntimeType("SomeContract")] + class D; + + [WindowsRuntimeType("SomeContract")] + enum E + { + A, + B + } + + public class DummyAttribute : Attribute; + """; + + CSharpCodeFixTest test = new(LanguageVersion.CSharp13, editorconfig: [("CsWinRTAotWarningLevel", "3")]) + { + TestCode = original, + FixedCode = @fixed + }; + + await test.RunAsync(); + } + + [TestMethod] + public async Task SingleCast_Constructor() + { + const string original = """ + using WinRT; + + namespace MyApp; + + public class Program + { + public Program(object obj) + { + C c = {|CsWinRT1034:(C)obj|}; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + const string @fixed = """ + using WinRT; + + namespace MyApp; + + public class Program + { + [DynamicWindowsRuntimeCast(typeof(C))] + public Program(object obj) + { + C c = (C)obj; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + CSharpCodeFixTest test = new(LanguageVersion.CSharp13, editorconfig: [("CsWinRTAotWarningLevel", "3")]) + { + TestCode = original, + FixedCode = @fixed + }; + + await test.RunAsync(); + } + + [TestMethod] + public async Task SingleCast_LocalMethod() + { + const string original = """ + using WinRT; + + namespace MyApp; + + public class Program + { + public void M(object obj) + { + void N(object obj) + { + C c = {|CsWinRT1034:(C)obj|}; + } + + N(obj); + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + const string @fixed = """ + using WinRT; + + namespace MyApp; + + public class Program + { + [DynamicWindowsRuntimeCast(typeof(C))] + public void M(object obj) + { + void N(object obj) + { + C c = (C)obj; + } + + N(obj); + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + CSharpCodeFixTest test = new(LanguageVersion.CSharp13, editorconfig: [("CsWinRTAotWarningLevel", "3")]) + { + TestCode = original, + FixedCode = @fixed + }; + + await test.RunAsync(); + } + + [TestMethod] + public async Task SingleCast_FieldInitializer() + { + const string original = """ + using System; + using WinRT; + + namespace MyApp; + + public class Program + { + private static readonly object obj = Register(obj => {|CsWinRT1034:(C)obj|}); + + private static object Register(Func action) + { + return action(new object()); + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + const string @fixed = """ + using System; + using WinRT; + + namespace MyApp; + + public class Program + { + [DynamicWindowsRuntimeCast(typeof(C))] + private static readonly object obj = Register(obj => (C)obj); + + private static object Register(Func action) + { + return action(new object()); + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + CSharpCodeFixTest test = new(LanguageVersion.CSharp13, editorconfig: [("CsWinRTAotWarningLevel", "3")]) + { + TestCode = original, + FixedCode = @fixed + }; + + await test.RunAsync(); + } + + [TestMethod] + public async Task SingleCast_Property_OneAccessor() + { + const string original = """ + using WinRT; + + namespace MyApp; + + public class Program + { + object _obj; + + C P1 + { + get => {|CsWinRT1034:(C)_obj|}; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + const string @fixed = """ + using WinRT; + + namespace MyApp; + + public class Program + { + object _obj; + + C P1 + { + [DynamicWindowsRuntimeCast(typeof(C))] + get => (C)_obj; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + CSharpCodeFixTest test = new(LanguageVersion.CSharp13, editorconfig: [("CsWinRTAotWarningLevel", "3")]) + { + TestCode = original, + FixedCode = @fixed + }; + + await test.RunAsync(); + } + + [TestMethod] + public async Task SingleCast_Property_TwoAccessor_OnlyOneWarns() + { + const string original = """ + using WinRT; + + namespace MyApp; + + public class Program + { + object _obj; + + C P1 + { + get => {|CsWinRT1034:(C)_obj|}; + set => _obj = value; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + const string @fixed = """ + using WinRT; + + namespace MyApp; + + public class Program + { + object _obj; + + C P1 + { + [DynamicWindowsRuntimeCast(typeof(C))] + get => (C)_obj; + set => _obj = value; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + """; + + CSharpCodeFixTest test = new(LanguageVersion.CSharp13, editorconfig: [("CsWinRTAotWarningLevel", "3")]) + { + TestCode = original, + FixedCode = @fixed + }; + + await test.RunAsync(); + } + + [TestMethod] + public async Task SingleCast_Property_TwoAccessor_BothWarn() + { + const string original = """ + using WinRT; + + namespace MyApp; + + public class Program + { + object _obj; + D _d; + + C P1 + { + get => {|CsWinRT1034:(C)_obj|}; + set => _d = {|CsWinRT1034:(D)value|}; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + + [WindowsRuntimeType("SomeContract")] + class D : C; + """; + + const string @fixed = """ + using WinRT; + + namespace MyApp; + + public class Program + { + object _obj; + D _d; + + C P1 + { + [DynamicWindowsRuntimeCast(typeof(C))] + get => (C)_obj; + [DynamicWindowsRuntimeCast(typeof(D))] + set => _d = (D)value; + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + + [WindowsRuntimeType("SomeContract")] + class D : C; + """; + + CSharpCodeFixTest test = new(LanguageVersion.CSharp13, editorconfig: [("CsWinRTAotWarningLevel", "3")]) + { + TestCode = original, + FixedCode = @fixed + }; + + await test.RunAsync(); + } + + [TestMethod] + public async Task MultipleCasts_MixedMembers_WithDuplicateTypes() + { + const string original = """ + using WinRT; + + namespace MyApp; + + public class Program + { + private object _obj; + + public void M(object obj) + { + C c1 = {|CsWinRT1034:(C)obj|}; + C c2 = {|CsWinRT1034:(C)obj|}; + D d = {|CsWinRT1034:(D)obj|}; + E e1 = {|CsWinRT1035:(E)obj|}; + E e2 = {|CsWinRT1035:(E)obj|}; + F f1 = {|CsWinRT1034:(F)obj|}; + F f2 = {|CsWinRT1034:(F)obj|}; + E e3 = {|CsWinRT1035:(E)obj|}; + } + + C P1 + { + get => {|CsWinRT1034:(C)_obj|}; + } + + C P2 + { + get => {|CsWinRT1034:(C)_obj|}; + set + { + E e = {|CsWinRT1035:(E)_obj|}; + F f1 = {|CsWinRT1034:(F)_obj|}; + F f2 = {|CsWinRT1034:(F)_obj|}; + G g = {|CsWinRT1034:(G)_obj|}; + + _obj = value; + } + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + + [WindowsRuntimeType("SomeContract")] + class D; + + [WindowsRuntimeType("SomeContract")] + enum E + { + A, + B + } + + [WindowsRuntimeType("SomeContract")] + class F : C; + + [WindowsRuntimeType("SomeContract")] + class G : F; + """; + + const string @fixed = """ + using WinRT; + + namespace MyApp; + + public class Program + { + private object _obj; + + [DynamicWindowsRuntimeCast(typeof(C))] + [DynamicWindowsRuntimeCast(typeof(D))] + [DynamicWindowsRuntimeCast(typeof(E))] + [DynamicWindowsRuntimeCast(typeof(F))] + public void M(object obj) + { + C c1 = (C)obj; + C c2 = (C)obj; + D d = (D)obj; + E e1 = (E)obj; + E e2 = (E)obj; + F f1 = (F)obj; + F f2 = (F)obj; + E e3 = (E)obj; + } + + C P1 + { + [DynamicWindowsRuntimeCast(typeof(C))] + get => (C)_obj; + } + + C P2 + { + [DynamicWindowsRuntimeCast(typeof(C))] + get => (C)_obj; + [DynamicWindowsRuntimeCast(typeof(E))] + [DynamicWindowsRuntimeCast(typeof(F))] + [DynamicWindowsRuntimeCast(typeof(G))] + set + { + E e = (E)_obj; + F f1 = (F)_obj; + F f2 = (F)_obj; + G g = (G)_obj; + + _obj = value; + } + } + } + + [WindowsRuntimeType("SomeContract")] + class C; + + [WindowsRuntimeType("SomeContract")] + class D; + + [WindowsRuntimeType("SomeContract")] + enum E + { + A, + B + } + + [WindowsRuntimeType("SomeContract")] + class F : C; + + [WindowsRuntimeType("SomeContract")] + class G : F; + """; + + CSharpCodeFixTest test = new(LanguageVersion.CSharp13, editorconfig: [("CsWinRTAotWarningLevel", "3")]) + { + TestCode = original, + FixedCode = @fixed + }; + + await test.RunAsync(); + } +} diff --git a/src/Tests/SourceGeneratorTest/SourceGeneratorTest.csproj b/src/Tests/SourceGeneratorTest/SourceGeneratorTest.csproj new file mode 100644 index 000000000..423408731 --- /dev/null +++ b/src/Tests/SourceGeneratorTest/SourceGeneratorTest.csproj @@ -0,0 +1,27 @@ + + + net8.0 + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Tests/TestComponentCSharp/Class.cpp b/src/Tests/TestComponentCSharp/Class.cpp index d299fa692..9c29ceecb 100644 --- a/src/Tests/TestComponentCSharp/Class.cpp +++ b/src/Tests/TestComponentCSharp/Class.cpp @@ -104,6 +104,81 @@ namespace winrt::TestComponentCSharp::implementation } }; + struct bindable_observable_vector : winrt::implements + { + IBindableObservableVector _wrapped; + + bindable_observable_vector(IBindableObservableVector wrapped) + { + _wrapped = wrapped; + } + + IBindableIterator First() + { + return _wrapped.First(); + } + + WF::IInspectable GetAt(uint32_t index) + { + return _wrapped.GetAt(index); + } + + uint32_t Size() + { + return _wrapped.Size(); + } + + IBindableVectorView GetView() + { + return _wrapped.GetView(); + } + + bool IndexOf(WF::IInspectable const& value, uint32_t& index) + { + return _wrapped.IndexOf(value, index); + } + + void SetAt(uint32_t index, WF::IInspectable const& value) + { + return _wrapped.SetAt(index, value); + } + + void InsertAt(uint32_t index, WF::IInspectable const& value) + { + return _wrapped.InsertAt(index, value); + } + + void RemoveAt(uint32_t index) + { + return _wrapped.RemoveAt(index); + } + + void Append(WF::IInspectable const& value) + { + _wrapped.Append(value); + } + + void RemoveAtEnd() + { + _wrapped.RemoveAtEnd(); + } + + void Clear() + { + _wrapped.Clear(); + } + + winrt::event_token VectorChanged(BindableVectorChangedEventHandler const& handler) + { + return _wrapped.VectorChanged(handler); + } + + void VectorChanged(winrt::event_token const& token) noexcept + { + _wrapped.VectorChanged(token); + } + }; + struct data_errors_changed_event_args : implements { data_errors_changed_event_args(winrt::hstring name) : @@ -298,6 +373,18 @@ namespace winrt::TestComponentCSharp::implementation { _collectionEvent(sender, arg0, arg1); } + winrt::event_token Class::GuidEvent(TestComponentCSharp::EventWithGuid const& handler) + { + return _guidEvent.add(handler); + } + void Class::GuidEvent(winrt::event_token const& token) noexcept + { + _guidEvent.remove(token); + } + void Class::InvokeGuidEvent(winrt::guid const& correlationGuid) + { + _guidEvent(correlationGuid); + } winrt::event_token Class::NestedEvent(EventHandler> const& handler) { return _nestedEvent.add(handler); @@ -335,6 +422,20 @@ namespace winrt::TestComponentCSharp::implementation _returnEvent(arg0); return arg0; } + + winrt::event_token Class::PropertyChangedEventHandler(winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler) + { + return _propertyChangedEventHandler.add(handler); + } + void Class::PropertyChangedEventHandler(winrt::event_token const& token) noexcept + { + _propertyChangedEventHandler.remove(token); + } + + winrt::guid Class::TestReturnGuid(winrt::guid const& arg) + { + return arg; + } int32_t Class::IntProperty() { return _int; @@ -392,6 +493,10 @@ namespace winrt::TestComponentCSharp::implementation _bool = provideBool(); _boolChanged(*this, _bool); } + void Class::InvokeBoolChanged(winrt::Windows::Foundation::EventHandler const& boolChanged) + { + boolChanged(*this, _bool); + } TestComponentCSharp::EnumValue Class::EnumProperty() { @@ -730,6 +835,18 @@ namespace winrt::TestComponentCSharp::implementation _stringPairChanged.remove(token); } + Windows::Foundation::Collections::IKeyValuePair Class::EnumPairProperty() + { + return _enumPair; + } + + void Class::EnumPairProperty(Windows::Foundation::Collections::IKeyValuePair const& value) + { + _enumPair = value; + _enumPair.Key(); + _enumPair.Value(); + } + TestComponentCSharp::ProvideUri Class::GetUriDelegate() noexcept { TestComponentCSharp::ProvideUri handler = [] { return Windows::Foundation::Uri(L"http://microsoft.com"); }; @@ -907,6 +1024,21 @@ namespace winrt::TestComponentCSharp::implementation std::copy(_ints.begin(), _ints.end(), ints.begin()); } + com_array Class::GetAndSetHResults(array_view hresults) + { + return com_array(hresults.begin(), hresults.end()); + } + + com_array Class::GetAndSetUris(array_view uris) + { + return com_array(uris.begin(), uris.end()); + } + + com_array Class::GetAndSetDateTimes(array_view datetime) + { + return com_array(datetime.begin(), datetime.end()); + } + IVectorView Class::GetIntVector() { return winrt::single_threaded_vector_view(std::vector{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); @@ -953,6 +1085,76 @@ namespace winrt::TestComponentCSharp::implementation return winrt::single_threaded_vector_view(std::vector{ *this, *this, *this }); } + IMap Class::GetIntToIntDictionary() + { + return single_threaded_map(std::map{ {1, 4}, { 2, 8 }, { 3, 12 } }); + } + + IMap Class::GetStringToBlittableDictionary() + { + return single_threaded_map(std::map + { + { L"alpha", ComposedBlittableStruct{ 5 } }, + { L"beta", ComposedBlittableStruct{ 4 } }, + { L"charlie", ComposedBlittableStruct{ 7 } } + }); + } + + IMap Class::GetStringToNonBlittableDictionary() + { + return single_threaded_map(std::map + { + { L"String0", ComposedNonBlittableStruct{ { 0 }, { L"String0" }, { true, false, true, false }, { 0 } } }, + { L"String1", ComposedNonBlittableStruct{ { 1 }, { L"String1" }, { false, true, false, true }, { 1 } } }, + { L"String2", ComposedNonBlittableStruct{ { 2 }, { L"String2" }, { true, false, true, false }, { 2 } } } + }); + } + + IMap> Class::GetIntToListDictionary() + { + auto vector = winrt::single_threaded_vector(std::vector { TestComponentCSharp::EnumValue::One }); + return single_threaded_map>(std::map>{ {1, vector}}); + } + + struct ComposedBlittableStructComparer + { + bool operator() (const TestComponentCSharp::ComposedBlittableStruct& l, const TestComponentCSharp::ComposedBlittableStruct& r) const + { + return (l.blittable.i32 < r.blittable.i32); + } + }; + + IMap Class::GetBlittableToObjectDictionary() + { + return single_threaded_map(std::map + { + { ComposedBlittableStruct{ 1 }, winrt::box_value(0) }, + { ComposedBlittableStruct{ 4 }, winrt::box_value(L"box") }, + { ComposedBlittableStruct{ 8 }, *this } + }); + } + + IVector Class::GetIntVector2() + { + return winrt::single_threaded_vector(std::vector{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }); + } + + IVector Class::GetBlittableStructVector2() + { + return winrt::single_threaded_vector(std::vector{ ComposedBlittableStruct{0}, ComposedBlittableStruct{1}, + ComposedBlittableStruct{2}, ComposedBlittableStruct{3}, ComposedBlittableStruct{4} }); + } + + IVector Class::GetNonBlittableStructVector2() + { + return winrt::single_threaded_vector(std::vector + { + ComposedNonBlittableStruct{ { 0 }, { L"String0" }, { true, false, true, false }, { 0 } }, + ComposedNonBlittableStruct{ { 1 }, { L"String1" }, { false, true, false, true }, { 1 } }, + ComposedNonBlittableStruct{ { 2 }, { L"String2" }, { true, false, true, false }, { 2 } }, + }); + } + // Test IIDOptimizer IVectorView Class::GetEventArgsVector() { @@ -1217,6 +1419,14 @@ namespace winrt::TestComponentCSharp::implementation { _vector3 = value; } + Windows::Foundation::IReference Class::Vector3NullableProperty() + { + return _vector3; + } + void Class::Vector3NullableProperty(Windows::Foundation::IReference const& value) + { + _vector3 = value.Value(); + } Numerics::float4 Class::Vector4Property() { return _vector4; @@ -1294,6 +1504,28 @@ namespace winrt::TestComponentCSharp::implementation _intColl = value; } + void Class::SetCharIterable(IIterable const& value) + { + _charColl = value; + } + + IIterable Class::GetEnumIterable() + { + return winrt::single_threaded_vector(std::vector{ EnumValue::One, EnumValue::Two }); + } + + IIterable Class::GetClassIterable() + { + TestComponentCSharp::CustomDisposableTest first; + TestComponentCSharp::CustomDisposableTest second; + return winrt::single_threaded_vector(std::vector{ first, second }); + } + + IIterator Class::GetIteratorForCollection(IIterable iterable) + { + return iterable.First(); + } + IBindableIterable Class::BindableIterableProperty() { return _bindableIterable; @@ -1381,6 +1613,82 @@ namespace winrt::TestComponentCSharp::implementation }); } + IBindableObservableVector Class::GetBindableObservableVector(IBindableObservableVector vector) + { + return winrt::make(vector); + } + + bool Class::ValidateBindableProperty( + WF::IInspectable const& bindableObject, + hstring property, + Windows::UI::Xaml::Interop::TypeName const& indexerType, + bool validateOnlyExists, + bool canRead, + bool canWrite, + bool isIndexer, + Windows::UI::Xaml::Interop::TypeName const& type, + WF::IInspectable const& indexerValue, + WF::IInspectable const& setValue, + WF::IInspectable& retrievedValue) + { + auto customPropertyProvider = bindableObject.as(); + auto customProperty = !isIndexer ? customPropertyProvider.GetCustomProperty(property) : customPropertyProvider.GetIndexedProperty(property, indexerType); + if (customProperty == nullptr) + { + return false; + } + + if (validateOnlyExists) + { + return true; + } + + if (customProperty.Name() != property || + customProperty.CanRead() != canRead || + customProperty.CanWrite() != canWrite || + customProperty.Type() != type) + { + return false; + } + + if (!isIndexer) + { + if (customProperty.CanRead()) + { + retrievedValue = customProperty.GetValue(bindableObject); + } + + if (customProperty.CanWrite()) + { + customProperty.SetValue(bindableObject, setValue); + } + } + else + { + if (customProperty.CanRead()) + { + retrievedValue = customProperty.GetIndexedValue(bindableObject, indexerValue); + } + + if (customProperty.CanWrite()) + { + customProperty.SetIndexedValue(bindableObject, setValue, indexerValue); + } + } + + return true; + } + + bool Class::CheckForBindableObjectInterface(Microsoft::UI::Xaml::Interop::IBindableIterable const& iterable) + { + if (auto iterableObject = iterable.try_as>()) + { + return iterableObject.First() != nullptr; + } + + return false; + } + void Class::CopyProperties(winrt::TestComponentCSharp::IProperties1 const& src) { ReadWriteProperty(src.ReadWriteProperty()); @@ -1417,6 +1725,11 @@ namespace winrt::TestComponentCSharp::implementation return winrt::unbox_value(obj); } + Windows::UI::Xaml::Interop::TypeName Class::UnboxType(WF::IInspectable const& obj) + { + return winrt::unbox_value(obj); + } + com_array Class::UnboxInt32Array(WF::IInspectable const& obj) { return obj.as>().Value(); @@ -1432,6 +1745,82 @@ namespace winrt::TestComponentCSharp::implementation return obj.as>().Value(); } + int32_t Class::UnboxInt32UsingPropertyValue(IInspectable const& obj) + { + if (auto ipv = obj.try_as()) + { + return ipv.GetInt32(); + } + return -1; + } + + hstring Class::UnboxStringUsingPropertyValue(IInspectable const& obj) + { + if (auto ipv = obj.try_as()) + { + return ipv.GetString(); + } + + return L""; + } + + Rect Class::UnboxRectUsingPropertyValue(IInspectable const& obj) + { + if (auto ipv = obj.try_as()) + { + return ipv.GetRect(); + } + + return Rect(-1, -1, -1, -1); + } + + com_array Class::UnboxInt32ArrayUsingPropertyValue(IInspectable const& obj) + { + com_array arr; + if (auto ipv = obj.try_as()) + { + ipv.GetInt32Array(arr); + } + + return arr; + } + + com_array Class::UnboxBooleanArrayUsingPropertyValue(IInspectable const& obj) + { + com_array arr; + if (auto ipv = obj.try_as()) + { + ipv.GetBooleanArray(arr); + } + + return arr; + } + + com_array Class::UnboxPointArrayUsingPropertyValue(IInspectable const& obj) + { + com_array arr; + if (auto ipv = obj.try_as()) + { + ipv.GetPointArray(arr); + } + + return arr; + } + + int32_t Class::GetPropertyType(IInspectable const& obj) + { + if (auto ipv = obj.try_as()) + { + return static_cast(ipv.Type()); + } + return -1; + } + + hstring Class::GetName(IInspectable const& obj) + { + return get_class_name(obj); + } + TypeName Class::Int32Type() { return winrt::xaml_typename(); @@ -1447,6 +1836,16 @@ namespace winrt::TestComponentCSharp::implementation return winrt::xaml_typename(); } + WF::IInspectable Class::BoxedType() + { + return winrt::box_value(winrt::xaml_typename()); + } + + IVector Class::ListOfTypes() + { + return single_threaded_vector({ winrt::xaml_typename(), winrt::xaml_typename>() }); + } + bool Class::VerifyTypeIsInt32Type(TypeName const& type_name) { return winrt::xaml_typename() == type_name; @@ -1484,12 +1883,93 @@ namespace winrt::TestComponentCSharp::implementation return winrt::box_value(val); } + WF::IInspectable Class::BoxedEventHandler() + { + Windows::Foundation::EventHandler handler = [](auto&&...) { }; + return winrt::box_value(handler); + } + + WF::IInspectable Class::BoxedStringArray() + { + hstring arr[] = { hstring{ L"one" }, hstring{ L"two" }, hstring{ L"three" } }; + return WF::PropertyValue::CreateStringArray(arr); + } + + WF::IInspectable Class::BoxedInt32Array() + { + int arr[] = { 1, 2, 3 }; + return WF::PropertyValue::CreateInt32Array(arr); + } + + WF::IInspectable Class::BoxedTimeSpanArray() + { + TimeSpan arr[] = { 10s, 20s, }; + return WF::PropertyValue::CreateTimeSpanArray(arr); + } + + WF::IInspectable Class::BoxedObjectArray() + { + WF::IInspectable arr[] = {*this, *this}; + return WF::PropertyValue::CreateInspectableArray(arr); + } + hstring Class::Catch(hstring const& /*params*/, hstring& /*lock*/) { // Compile-only test for keyword escaping throw hresult_not_implemented(); } + hstring Class::ThrowExceptionWithMessage(hstring message, bool throwNonMappedError) + { + if (throwNonMappedError) + { + throw hresult_wrong_thread(message.c_str()); + } + else + { + throw hresult_invalid_argument(message.c_str()); + } + } + + extern "C" BOOL __stdcall RoOriginateLanguageException(HRESULT error, void* message, void* languageException); + + hstring Class::OriginateAndThrowExceptionWithMessage(hstring message) + { + struct language_exception : winrt::implements + { + }; + + RoOriginateLanguageException(winrt::impl::error_invalid_argument, get_abi(message), get_abi(winrt::make())); + throw hresult_invalid_argument(hresult_error::from_abi); + } + + void Class::UnboxAndCallProgressHandler(WF::IInspectable const& httpProgressHandler) + { + Windows::Web::Http::HttpProgress progress; + progress.BytesReceived = 3; + progress.TotalBytesToReceive = 4; + + winrt::unbox_value>(httpProgressHandler)(nullptr, progress); + } + + double Class::Calculate(winrt::Windows::Foundation::Collections::IVector> const& values) + { + double result = 0; + for (auto val : values) + { + if (val) + { + result += val.Value(); + } + } + return result; + } + + winrt::Windows::Foundation::Collections::IVector> Class::GetNullableIntList() + { + return single_threaded_vector>({ 1, nullptr, 2 }); + } + TestComponentCSharp::IProperties1 Class::NativeProperties1() { struct native_properties1 : winrt::implements @@ -1560,6 +2040,19 @@ namespace winrt::TestComponentCSharp::implementation return winrt::make(); } + WF::Collections::IPropertySet Class::PropertySet() + { + WF::Collections::PropertySet propertySet; + propertySet.Insert(L"alpha", winrt::box_value(L"first")); + propertySet.Insert(L"beta", winrt::box_value(L"second")); + propertySet.Insert(L"charlie", winrt::box_value(L"third")); + auto arr = com_array({ 1, 2, 3, 4 }); + propertySet.Insert(L"delta", winrt::box_value(arr)); + auto arr2 = com_array({ {1, 1}, {2, 2}, {3, 3} }); + propertySet.Insert(L"echo", winrt::box_value(arr2)); + return propertySet; + } + // INotifyDataErrorInfo bool Class::HasErrors() { diff --git a/src/Tests/TestComponentCSharp/Class.h b/src/Tests/TestComponentCSharp/Class.h index 6604e896f..1aec3df13 100644 --- a/src/Tests/TestComponentCSharp/Class.h +++ b/src/Tests/TestComponentCSharp/Class.h @@ -20,9 +20,11 @@ namespace winrt::TestComponentCSharp::implementation winrt::event _event2; winrt::event _event3; winrt::event _collectionEvent; + winrt::event _guidEvent; winrt::event>> _nestedEvent; winrt::event>> _nestedTypedEvent; winrt::event _returnEvent; + winrt::event _propertyChangedEventHandler; Windows::Foundation::Collections::IVector GetUriVectorAsIInspectableVector(); @@ -57,10 +59,12 @@ namespace winrt::TestComponentCSharp::implementation winrt::event> _uriChanged; Windows::Foundation::Collections::IKeyValuePair _stringPair; winrt::event>> _stringPairChanged; + Windows::Foundation::Collections::IKeyValuePair _enumPair; ComposedBlittableStruct _blittableStruct{}; ComposedNonBlittableStruct _nonBlittableStruct{}; std::vector _ints{ 1, 2, 3 }; Windows::Foundation::Collections::IIterable _intColl; + Windows::Foundation::Collections::IIterable _charColl; Microsoft::UI::Xaml::Interop::IBindableIterable _bindableIterable; Microsoft::UI::Xaml::Interop::IBindableVector _bindableVector; Microsoft::UI::Xaml::Interop::IBindableObservableVector _bindableObservable; @@ -124,6 +128,9 @@ namespace winrt::TestComponentCSharp::implementation winrt::event_token CollectionEvent(TestComponentCSharp::EventHandlerCollection const& handler); void CollectionEvent(winrt::event_token const& token) noexcept; void InvokeCollectionEvent(TestComponentCSharp::Class const& sender, Windows::Foundation::Collections::IVector const& arg0, Windows::Foundation::Collections::IMap const& arg1); + winrt::event_token GuidEvent(TestComponentCSharp::EventWithGuid const& handler); + void GuidEvent(winrt::event_token const& token) noexcept; + void InvokeGuidEvent(winrt::guid const& correlationGuid); winrt::event_token NestedEvent(Windows::Foundation::EventHandler> const& handler); void NestedEvent(winrt::event_token const& token) noexcept; void InvokeNestedEvent(TestComponentCSharp::Class const& sender, Windows::Foundation::Collections::IVector const& arg0); @@ -133,6 +140,10 @@ namespace winrt::TestComponentCSharp::implementation winrt::event_token ReturnEvent(TestComponentCSharp::EventWithReturn const& handler); void ReturnEvent(winrt::event_token const& token) noexcept; int32_t InvokeReturnEvent(int32_t const& arg0); + winrt::guid TestReturnGuid(winrt::guid const& arg); + + winrt::event_token PropertyChangedEventHandler(winrt::Microsoft::UI::Xaml::Data::PropertyChangedEventHandler const& handler); + void PropertyChangedEventHandler(winrt::event_token const& token) noexcept; int32_t IntProperty(); void IntProperty(int32_t value); @@ -147,6 +158,7 @@ namespace winrt::TestComponentCSharp::implementation void BoolPropertyChanged(winrt::event_token const& token) noexcept; void RaiseBoolChanged(); void CallForBool(TestComponentCSharp::ProvideBool const& provideBool); + void InvokeBoolChanged(winrt::Windows::Foundation::EventHandler const& boolChanged); TestComponentCSharp::EnumValue EnumProperty(); void EnumProperty(TestComponentCSharp::EnumValue const& value); winrt::event_token EnumPropertyChanged(Windows::Foundation::EventHandler const& handler); @@ -222,6 +234,8 @@ namespace winrt::TestComponentCSharp::implementation void CallForStringPair(TestComponentCSharp::ProvideStringPair const& provideStringPair); winrt::event_token StringPairPropertyChanged(Windows::Foundation::EventHandler> const& handler); void StringPairPropertyChanged(winrt::event_token const& token) noexcept; + Windows::Foundation::Collections::IKeyValuePair EnumPairProperty(); + void EnumPairProperty(Windows::Foundation::Collections::IKeyValuePair const& value); TestComponentCSharp::ProvideUri GetUriDelegate() noexcept; BlittableStruct BlittableStructProperty(); void BlittableStructProperty(BlittableStruct const& value); @@ -257,6 +271,10 @@ namespace winrt::TestComponentCSharp::implementation com_array GetInts(); void FillInts(array_view ints); + com_array GetAndSetHResults(array_view hresults); + com_array GetAndSetUris(array_view uris); + com_array GetAndSetDateTimes(array_view datetime); + Windows::Foundation::IAsyncOperation GetIntAsync(); Windows::Foundation::IAsyncOperationWithProgress GetStringAsync(); @@ -268,13 +286,27 @@ namespace winrt::TestComponentCSharp::implementation Windows::Foundation::Collections::IVectorView GetObjectVector(); Windows::Foundation::Collections::IVectorView GetInterfaceVector(); Windows::Foundation::Collections::IVectorView GetClassVector() noexcept; - + Windows::Foundation::Collections::IVector GetIntVector2(); + Windows::Foundation::Collections::IVector GetBlittableStructVector2(); + Windows::Foundation::Collections::IVector GetNonBlittableStructVector2(); + + Windows::Foundation::Collections::IMap GetIntToIntDictionary(); + Windows::Foundation::Collections::IMap GetStringToBlittableDictionary(); + Windows::Foundation::Collections::IMap GetStringToNonBlittableDictionary(); + Windows::Foundation::Collections::IMap GetBlittableToObjectDictionary(); + Windows::Foundation::Collections::IMap> GetIntToListDictionary(); + // Test IIDOptimizer -- testing the windows projection covers most code paths, and these two types exercise the rest. Windows::Foundation::Collections::IVectorView GetEventArgsVector(); Windows::Foundation::Collections::IVectorView GetNonGenericDelegateVector(); Windows::Foundation::Collections::IIterable GetIntIterable(); void SetIntIterable(Windows::Foundation::Collections::IIterable const& value); + void SetCharIterable(Windows::Foundation::Collections::IIterable const& value); + Windows::Foundation::Collections::IIterable GetEnumIterable(); + Windows::Foundation::Collections::IIterable GetClassIterable(); + + Windows::Foundation::Collections::IIterator GetIteratorForCollection(Windows::Foundation::Collections::IIterable iterable); Microsoft::UI::Xaml::Interop::IBindableIterable BindableIterableProperty(); void BindableIterableProperty(Microsoft::UI::Xaml::Interop::IBindableIterable const& value); @@ -290,10 +322,26 @@ namespace winrt::TestComponentCSharp::implementation void BindableVectorPropertyChanged(winrt::event_token const& token) noexcept; Microsoft::UI::Xaml::Interop::IBindableObservableVector BindableObservableVectorProperty(); void BindableObservableVectorProperty(Microsoft::UI::Xaml::Interop::IBindableObservableVector const& value); + Microsoft::UI::Xaml::Interop::IBindableObservableVector GetBindableObservableVector(Microsoft::UI::Xaml::Interop::IBindableObservableVector vector); + + bool ValidateBindableProperty( + IInspectable const& bindableObject, + hstring property, + Windows::UI::Xaml::Interop::TypeName const& indexerType, + bool validateOnlyExists, + bool canRead, + bool canWrite, + bool isIndexer, + Windows::UI::Xaml::Interop::TypeName const& type, + IInspectable const& indexerValue, + IInspectable const& setValue, + IInspectable& retrievedValue); void CopyProperties(TestComponentCSharp::IProperties1 const& src); void CopyPropertiesViaWeakReference(TestComponentCSharp::IProperties1 const& src); + bool CheckForBindableObjectInterface(Microsoft::UI::Xaml::Interop::IBindableIterable const& iterable); + void CompleteAsync(); void CompleteAsync(int32_t hr); void AdvanceAsync(int32_t delta); @@ -340,6 +388,8 @@ namespace winrt::TestComponentCSharp::implementation void Vector2Property(Windows::Foundation::Numerics::float2 const& value); Windows::Foundation::Numerics::float3 Vector3Property(); void Vector3Property(Windows::Foundation::Numerics::float3 const& value); + Windows::Foundation::IReference Vector3NullableProperty(); + void Vector3NullableProperty(Windows::Foundation::IReference const& value); Windows::Foundation::Numerics::float4 Vector4Property(); void Vector4Property(Windows::Foundation::Numerics::float4 const& value); Windows::Foundation::IReference GetPointReference(); @@ -357,13 +407,31 @@ namespace winrt::TestComponentCSharp::implementation static hstring UnboxString(IInspectable const& obj); static EnumValue UnboxEnum(IInspectable const& obj); static TestComponentCSharp::ProvideInt UnboxDelegate(IInspectable const& obj); + static Windows::UI::Xaml::Interop::TypeName UnboxType(IInspectable const& obj); static com_array UnboxInt32Array(IInspectable const& obj); static com_array UnboxBooleanArray(IInspectable const& obj); static com_array UnboxStringArray(IInspectable const& obj); + static int32_t UnboxInt32UsingPropertyValue(IInspectable const& obj); + static hstring UnboxStringUsingPropertyValue(IInspectable const& obj); + static Windows::Foundation::Rect UnboxRectUsingPropertyValue(IInspectable const& obj); + static com_array UnboxInt32ArrayUsingPropertyValue(IInspectable const& obj); + static com_array UnboxBooleanArrayUsingPropertyValue(IInspectable const& obj); + static com_array UnboxPointArrayUsingPropertyValue(IInspectable const& obj); + + static void UnboxAndCallProgressHandler(IInspectable const& httpProgressHandler); + double Calculate(winrt::Windows::Foundation::Collections::IVector> const& values); + winrt::Windows::Foundation::Collections::IVector> GetNullableIntList(); + + static int GetPropertyType(Windows::Foundation::IInspectable const& obj); + static hstring GetName(Windows::Foundation::IInspectable const& obj); + static Windows::UI::Xaml::Interop::TypeName Int32Type(); static Windows::UI::Xaml::Interop::TypeName ReferenceInt32Type(); static Windows::UI::Xaml::Interop::TypeName ThisClassType(); + static Windows::Foundation::IInspectable BoxedType(); + static Windows::Foundation::Collections::IVector ListOfTypes(); + static bool VerifyTypeIsInt32Type(Windows::UI::Xaml::Interop::TypeName const& type_name); static bool VerifyTypeIsReferenceInt32Type(Windows::UI::Xaml::Interop::TypeName const& type_name); static bool VerifyTypeIsThisClassType(Windows::UI::Xaml::Interop::TypeName const& type_name); @@ -372,12 +440,21 @@ namespace winrt::TestComponentCSharp::implementation static Windows::Foundation::IInspectable EmptyString(); static Windows::Foundation::IInspectable BoxedDelegate(); static Windows::Foundation::IInspectable BoxedEnum(); + static Windows::Foundation::IInspectable BoxedEventHandler(); + static Windows::Foundation::IInspectable BoxedStringArray(); + static Windows::Foundation::IInspectable BoxedInt32Array(); + static Windows::Foundation::IInspectable BoxedTimeSpanArray(); + Windows::Foundation::IInspectable BoxedObjectArray(); hstring Catch(hstring const& params, hstring& locks); + hstring ThrowExceptionWithMessage(hstring message, bool throwNonMappedError); + hstring OriginateAndThrowExceptionWithMessage(hstring message); + static IProperties1 NativeProperties1(); static Windows::Foundation::IInspectable ServiceProvider(); static winrt::Windows::Foundation::IInspectable ComInterop(); + static winrt::Windows::Foundation::Collections::IPropertySet PropertySet(); // IStringable hstring ToString(); diff --git a/src/Tests/TestComponentCSharp/ClassWithExplicitIUnknown.cpp b/src/Tests/TestComponentCSharp/ClassWithExplicitIUnknown.cpp new file mode 100644 index 000000000..472fed297 --- /dev/null +++ b/src/Tests/TestComponentCSharp/ClassWithExplicitIUnknown.cpp @@ -0,0 +1,21 @@ +#include "pch.h" +#include "ClassWithExplicitIUnknown.h" +#include "ClassWithExplicitIUnknown.g.cpp" + +namespace winrt::TestComponentCSharp::implementation +{ + ClassWithExplicitIUnknown::ClassWithExplicitIUnknown(int32_t defaultValue) + { + m_value = defaultValue; + } + + int32_t ClassWithExplicitIUnknown::Value() + { + return m_value; + } + + void ClassWithExplicitIUnknown::Value(int32_t value) + { + m_value = value; + } +} diff --git a/src/Tests/TestComponentCSharp/ClassWithExplicitIUnknown.h b/src/Tests/TestComponentCSharp/ClassWithExplicitIUnknown.h new file mode 100644 index 000000000..31e2c01da --- /dev/null +++ b/src/Tests/TestComponentCSharp/ClassWithExplicitIUnknown.h @@ -0,0 +1,24 @@ +#pragma once +#include "ClassWithExplicitIUnknown.g.h" + +namespace winrt::TestComponentCSharp::implementation +{ + // Explicity referencing IUnknown. + struct ClassWithExplicitIUnknown : ClassWithExplicitIUnknownT + { + ClassWithExplicitIUnknown() = default; + + ClassWithExplicitIUnknown(int32_t defaultValue); + int32_t Value(); + void Value(int32_t value); + + private: + int32_t m_value{ 0 }; + }; +} +namespace winrt::TestComponentCSharp::factory_implementation +{ + struct ClassWithExplicitIUnknown : ClassWithExplicitIUnknownT + { + }; +} diff --git a/src/Tests/TestComponentCSharp/CustomIterableTest.cpp b/src/Tests/TestComponentCSharp/CustomIterableTest.cpp new file mode 100644 index 000000000..747ba11c4 --- /dev/null +++ b/src/Tests/TestComponentCSharp/CustomIterableTest.cpp @@ -0,0 +1,39 @@ +#include "pch.h" +#include "CustomIterableTest.h" +#include "CustomIterableTest.g.cpp" + +namespace winrt::TestComponentCSharp::implementation +{ + CustomIterableTest::CustomIterableTest() + { + _iterable = winrt::single_threaded_vector(std::vector{ 1, 2, 4 }); + } + + CustomIterableTest::CustomIterableTest(winrt::Windows::Foundation::Collections::IIterable const& iterable) + { + _iterable = iterable; + } + + CustomIterableTest::CustomIterableTest(bool useCustomIterator) + :CustomIterableTest() + { + _useCustomIterator = useCustomIterator; + } + + winrt::TestComponentCSharp::CustomIterableTest CustomIterableTest::CreateWithCustomIterator() + { + return winrt::make(true); + } + + winrt::Windows::Foundation::Collections::IIterator CustomIterableTest::First() + { + if (_useCustomIterator) + { + return winrt::TestComponentCSharp::CustomIteratorTest(_iterable.First()); + } + else + { + return _iterable.First(); + } + } +} diff --git a/src/Tests/TestComponentCSharp/CustomIterableTest.h b/src/Tests/TestComponentCSharp/CustomIterableTest.h new file mode 100644 index 000000000..e2ead69f8 --- /dev/null +++ b/src/Tests/TestComponentCSharp/CustomIterableTest.h @@ -0,0 +1,25 @@ +#pragma once +#include "CustomIterableTest.g.h" + +namespace winrt::TestComponentCSharp::implementation +{ + struct CustomIterableTest : CustomIterableTestT + { + CustomIterableTest(); + CustomIterableTest(winrt::Windows::Foundation::Collections::IIterable const& iterable); + CustomIterableTest(bool useCustomIterator); + + static winrt::TestComponentCSharp::CustomIterableTest CreateWithCustomIterator(); + winrt::Windows::Foundation::Collections::IIterator First(); + + winrt::Windows::Foundation::Collections::IIterable _iterable; + bool _useCustomIterator = false; + }; +} + +namespace winrt::TestComponentCSharp::factory_implementation +{ + struct CustomIterableTest : CustomIterableTestT + { + }; +} diff --git a/src/Tests/TestComponentCSharp/ManualProjectionTestClasses.cpp b/src/Tests/TestComponentCSharp/ManualProjectionTestClasses.cpp index daa53e6ed..24b0763cc 100644 --- a/src/Tests/TestComponentCSharp/ManualProjectionTestClasses.cpp +++ b/src/Tests/TestComponentCSharp/ManualProjectionTestClasses.cpp @@ -3,6 +3,8 @@ #include "CustomBindableIteratorTest.g.cpp" #include "CustomDisposableTest.g.cpp" #include "CustomBindableVectorTest.g.cpp" +#include "CustomBindableObservableVectorTest.g.cpp" +#include "CustomIteratorTest.g.cpp" namespace winrt::TestComponentCSharp::implementation { @@ -75,4 +77,91 @@ namespace winrt::TestComponentCSharp::implementation { return winrt::Microsoft::UI::Xaml::Interop::IBindableIterator(); } + + winrt::Microsoft::UI::Xaml::Interop::IBindableIterator CustomBindableObservableVectorTest::First() + { + return winrt::Microsoft::UI::Xaml::Interop::IBindableIterator(); + } + winrt::Windows::Foundation::IInspectable CustomBindableObservableVectorTest::GetAt(uint32_t index) + { + return Windows::Foundation::PropertyValue::CreateInt32(1); + } + uint32_t CustomBindableObservableVectorTest::Size() + { + return 1; + } + winrt::Microsoft::UI::Xaml::Interop::IBindableVectorView CustomBindableObservableVectorTest::GetView() + { + return winrt::Microsoft::UI::Xaml::Interop::IBindableVectorView(); + } + bool CustomBindableObservableVectorTest::IndexOf(winrt::Windows::Foundation::IInspectable const& value, uint32_t& index) + { + return false; + } + void CustomBindableObservableVectorTest::SetAt(uint32_t index, winrt::Windows::Foundation::IInspectable const& value) + { + } + void CustomBindableObservableVectorTest::InsertAt(uint32_t index, winrt::Windows::Foundation::IInspectable const& value) + { + } + void CustomBindableObservableVectorTest::RemoveAt(uint32_t index) + { + } + void CustomBindableObservableVectorTest::Append(winrt::Windows::Foundation::IInspectable const& value) + { + } + void CustomBindableObservableVectorTest::RemoveAtEnd() + { + } + void CustomBindableObservableVectorTest::Clear() + { + } + winrt::event_token CustomBindableObservableVectorTest::VectorChanged(winrt::Microsoft::UI::Xaml::Interop::BindableVectorChangedEventHandler const& handler) + { + throw hresult_not_implemented(); + } + void CustomBindableObservableVectorTest::VectorChanged(winrt::event_token const& token) noexcept + { + } + + CustomIteratorTest::CustomIteratorTest(winrt::Windows::Foundation::Collections::IIterator iterator) + { + _iterator = iterator; + } + int32_t CustomIteratorTest::Current() + { + if (_iterator) + { + return _iterator.Current(); + } + + return 2; + } + bool CustomIteratorTest::HasCurrent() + { + if (_iterator) + { + return _iterator.HasCurrent(); + } + + return true; + } + bool CustomIteratorTest::MoveNext() + { + if (_iterator) + { + return _iterator.MoveNext(); + } + + return true; + } + uint32_t CustomIteratorTest::GetMany(array_view items) + { + if (_iterator) + { + return _iterator.GetMany(items); + } + + throw hresult_not_implemented(); + } } \ No newline at end of file diff --git a/src/Tests/TestComponentCSharp/ManualProjectionTestClasses.h b/src/Tests/TestComponentCSharp/ManualProjectionTestClasses.h index 374644556..ab37f5a75 100644 --- a/src/Tests/TestComponentCSharp/ManualProjectionTestClasses.h +++ b/src/Tests/TestComponentCSharp/ManualProjectionTestClasses.h @@ -2,6 +2,8 @@ #include "CustomBindableIteratorTest.g.h" #include "CustomDisposableTest.g.h" #include "CustomBindableVectorTest.g.h" +#include "CustomBindableObservableVectorTest.g.h" +#include "CustomIteratorTest.g.h" namespace winrt::TestComponentCSharp::implementation { @@ -34,6 +36,38 @@ namespace winrt::TestComponentCSharp::implementation int32_t Size(); winrt::Microsoft::UI::Xaml::Interop::IBindableIterator First(); }; + + struct CustomBindableObservableVectorTest : CustomBindableObservableVectorTestT + { + CustomBindableObservableVectorTest() = default; + + winrt::Microsoft::UI::Xaml::Interop::IBindableIterator First(); + winrt::Windows::Foundation::IInspectable GetAt(uint32_t index); + uint32_t Size(); + winrt::Microsoft::UI::Xaml::Interop::IBindableVectorView GetView(); + bool IndexOf(winrt::Windows::Foundation::IInspectable const& value, uint32_t& index); + void SetAt(uint32_t index, winrt::Windows::Foundation::IInspectable const& value); + void InsertAt(uint32_t index, winrt::Windows::Foundation::IInspectable const& value); + void RemoveAt(uint32_t index); + void Append(winrt::Windows::Foundation::IInspectable const& value); + void RemoveAtEnd(); + void Clear(); + winrt::event_token VectorChanged(winrt::Microsoft::UI::Xaml::Interop::BindableVectorChangedEventHandler const& handler); + void VectorChanged(winrt::event_token const& token) noexcept; + }; + + struct CustomIteratorTest : CustomIteratorTestT + { + CustomIteratorTest() = default; + CustomIteratorTest(winrt::Windows::Foundation::Collections::IIterator iterator); + + int32_t Current(); + bool HasCurrent(); + bool MoveNext(); + uint32_t GetMany(array_view items); + + winrt::Windows::Foundation::Collections::IIterator _iterator; + }; } namespace winrt::TestComponentCSharp::factory_implementation @@ -52,4 +86,12 @@ namespace winrt::TestComponentCSharp::factory_implementation { }; + + struct CustomBindableObservableVectorTest : CustomBindableObservableVectorTestT + { + }; + + struct CustomIteratorTest : CustomIteratorTestT + { + }; } \ No newline at end of file diff --git a/src/Tests/TestComponentCSharp/NonAgileClass.cpp b/src/Tests/TestComponentCSharp/NonAgileClass.cpp index 9eb0b7ba9..9b093da8e 100644 --- a/src/Tests/TestComponentCSharp/NonAgileClass.cpp +++ b/src/Tests/TestComponentCSharp/NonAgileClass.cpp @@ -91,4 +91,23 @@ namespace winrt::TestComponentCSharp::implementation *put_abi(handler) = make().detach(); vector.VectorChanged(handler); } + + winrt::event_token NonAgileClass::CanExecuteChanged(winrt::Windows::Foundation::EventHandler const& handler) + { + return _event.add(handler); + } + + void NonAgileClass::CanExecuteChanged(winrt::event_token const& token) noexcept + { + _event.remove(token); + } + + bool NonAgileClass::CanExecute(winrt::Windows::Foundation::IInspectable const& parameter) + { + return true; + } + + void NonAgileClass::Execute(winrt::Windows::Foundation::IInspectable const& parameter) + { + } } diff --git a/src/Tests/TestComponentCSharp/NonAgileClass.h b/src/Tests/TestComponentCSharp/NonAgileClass.h index 9aea09459..ba1592d34 100644 --- a/src/Tests/TestComponentCSharp/NonAgileClass.h +++ b/src/Tests/TestComponentCSharp/NonAgileClass.h @@ -5,10 +5,23 @@ namespace winrt::TestComponentCSharp::implementation { struct NonAgileClass : NonAgileClassT { + winrt::event> _event; public: NonAgileClass(); void Observe(Microsoft::UI::Xaml::Interop::IBindableObservableVector vector); void VectorChanged(Microsoft::UI::Xaml::Interop::IBindableObservableVector vector, Windows::Foundation::IInspectable e); + + winrt::event_token CanExecuteChanged(winrt::Windows::Foundation::EventHandler const& handler); + void CanExecuteChanged(winrt::event_token const& token) noexcept; + bool CanExecute(winrt::Windows::Foundation::IInspectable const& parameter); + void Execute(winrt::Windows::Foundation::IInspectable const& parameter); + + // Overriding runtime class name to allow marshaling as just the ICommand interface during proxy calls + // to avoid defining proxy stubs for this dll. + hstring GetRuntimeClassName() const + { + return L"Windows.UI.Xaml.Input.ICommand"; + } }; } namespace winrt::TestComponentCSharp::factory_implementation diff --git a/src/Tests/TestComponentCSharp/NonUniqueClass.cpp b/src/Tests/TestComponentCSharp/NonUniqueClass.cpp new file mode 100644 index 000000000..b56ccff54 --- /dev/null +++ b/src/Tests/TestComponentCSharp/NonUniqueClass.cpp @@ -0,0 +1,21 @@ +#include "pch.h" +#include "NonUniqueClass.h" +#include "TestPublicExclusiveTo.NonUniqueClass.g.cpp" + +using namespace winrt; + +namespace winrt::TestComponentCSharp::TestPublicExclusiveTo::implementation +{ + int32_t NonUniqueClass::StaticProperty() + { + throw hresult_not_implemented(); + } + hstring NonUniqueClass::Path() + { + throw hresult_not_implemented(); + } + int32_t NonUniqueClass::Type() + { + throw hresult_not_implemented(); + } +} diff --git a/src/Tests/TestComponentCSharp/NonUniqueClass.h b/src/Tests/TestComponentCSharp/NonUniqueClass.h new file mode 100644 index 000000000..065a87e08 --- /dev/null +++ b/src/Tests/TestComponentCSharp/NonUniqueClass.h @@ -0,0 +1,21 @@ +#pragma once +#include "TestPublicExclusiveTo.NonUniqueClass.g.h" + +namespace winrt::TestComponentCSharp::TestPublicExclusiveTo::implementation +{ + struct NonUniqueClass : NonUniqueClassT + { + public: + NonUniqueClass(); + + static int32_t StaticProperty(); + hstring Path(); + int32_t Type(); + }; +} +namespace winrt::TestComponentCSharp::TestPublicExclusiveTo::factory_implementation +{ + struct NonUniqueClass : NonUniqueClassT + { + }; +} diff --git a/src/Tests/TestComponentCSharp/TestComponentCSharp.idl b/src/Tests/TestComponentCSharp/TestComponentCSharp.idl index 17af1636b..f18ae60df 100644 --- a/src/Tests/TestComponentCSharp/TestComponentCSharp.idl +++ b/src/Tests/TestComponentCSharp/TestComponentCSharp.idl @@ -12,6 +12,7 @@ namespace TestComponentCSharp delegate void EventHandler3(Class sender, Int32 arg0, String arg1); delegate void EventHandlerCollection(Class sender, Windows.Foundation.Collections.IVector arg0, Windows.Foundation.Collections.IMap arg1); delegate Int32 EventWithReturn(Int32 arg); + delegate void EventWithGuid(Guid correlationGuid); [flags] enum FlagValue @@ -124,12 +125,22 @@ namespace TestComponentCSharp void AddUriHandler(ProvideUri provideUri); }; + interface IBoolChanged + { + void InvokeBoolChanged(Windows.Foundation.EventHandler boolChanged); + }; + static runtimeclass ComImports { static Object MakeObject(); static Int32 NumObjects{ get; }; } + interface IDerivedGenericInterface requires Microsoft.UI.Xaml.Input.ICommand, Microsoft.UI.Xaml.Interop.INotifyCollectionChanged, Windows.Foundation.Collections.IPropertySet + { + Int32 Number; + } + interface ISingleton { Int32 IntProperty; @@ -152,6 +163,7 @@ namespace TestComponentCSharp , Microsoft.UI.Xaml.Input.ICommand //, Windows.Foundation.Collections.IVector //, Windows.Foundation.Collections.IMap + , IBoolChanged { // factory Class(); @@ -184,13 +196,17 @@ namespace TestComponentCSharp event EventHandler3 Event3; void InvokeEvent3(Class sender, Int32 arg0, String arg1); event EventHandlerCollection CollectionEvent; + event EventWithGuid GuidEvent; void InvokeCollectionEvent(Class sender, Windows.Foundation.Collections.IVector arg0, Windows.Foundation.Collections.IMap arg1); + void InvokeGuidEvent(Guid correlationGuid); event Windows.Foundation.EventHandler > NestedEvent; void InvokeNestedEvent(Class sender, Windows.Foundation.Collections.IVector arg0); event Windows.Foundation.TypedEventHandler > NestedTypedEvent; void InvokeNestedTypedEvent(Class sender, Windows.Foundation.Collections.IVector arg0); event EventWithReturn ReturnEvent; Int32 InvokeReturnEvent(Int32 arg0); + Guid TestReturnGuid(Guid arg); + event Microsoft.UI.Xaml.Data.PropertyChangedEventHandler PropertyChangedEventHandler; Int32 IntProperty; event Windows.Foundation.EventHandler IntPropertyChanged; @@ -259,6 +275,8 @@ namespace TestComponentCSharp void CallForStringPair(ProvideStringPair provideStringPair); event Windows.Foundation.EventHandler > StringPairPropertyChanged; + Windows.Foundation.Collections.IKeyValuePair EnumPairProperty; + Windows.Foundation.Collections.IVector GetUriVectorAsIInspectableVector(); ProvideUri GetUriDelegate(); void AddUriHandler(IUriHandler uriHandler); @@ -299,6 +317,10 @@ namespace TestComponentCSharp Int32[] GetInts(); void FillInts(ref Int32[] ints); + Windows.Foundation.HResult[] GetAndSetHResults(Windows.Foundation.HResult[] hresults); + Windows.Foundation.Uri[] GetAndSetUris(Windows.Foundation.Uri[] uris); + Windows.Foundation.DateTime[] GetAndSetDateTimes(Windows.Foundation.DateTime[] datetime); + // Generics Windows.Foundation.IAsyncOperation GetIntAsync(); Windows.Foundation.IAsyncOperationWithProgress GetStringAsync(); @@ -310,6 +332,15 @@ namespace TestComponentCSharp Windows.Foundation.Collections.IVectorView GetObjectVector(); Windows.Foundation.Collections.IVectorView GetInterfaceVector(); [noexcept] Windows.Foundation.Collections.IVectorView GetClassVector(); + Windows.Foundation.Collections.IVector GetIntVector2(); + Windows.Foundation.Collections.IVector GetBlittableStructVector2(); + Windows.Foundation.Collections.IVector GetNonBlittableStructVector2(); + + Windows.Foundation.Collections.IMap GetIntToIntDictionary(); + Windows.Foundation.Collections.IMap GetStringToBlittableDictionary(); + Windows.Foundation.Collections.IMap GetStringToNonBlittableDictionary(); + Windows.Foundation.Collections.IMap GetBlittableToObjectDictionary(); + Windows.Foundation.Collections.IMap > GetIntToListDictionary(); // Test IIDOptimizer Windows.Foundation.Collections.IVectorView GetEventArgsVector(); @@ -317,6 +348,11 @@ namespace TestComponentCSharp Windows.Foundation.Collections.IIterable GetIntIterable(); void SetIntIterable(Windows.Foundation.Collections.IIterable value); + void SetCharIterable(Windows.Foundation.Collections.IIterable value); + Windows.Foundation.Collections.IIterable GetEnumIterable(); + Windows.Foundation.Collections.IIterable GetClassIterable(); + + Windows.Foundation.Collections.IIterator GetIteratorForCollection(Windows.Foundation.Collections.IIterable iterable); // Bindable Microsoft.UI.Xaml.Interop.IBindableIterable BindableIterableProperty; @@ -330,10 +366,26 @@ namespace TestComponentCSharp event Windows.Foundation.EventHandler BindableVectorPropertyChanged; Microsoft.UI.Xaml.Interop.IBindableObservableVector BindableObservableVectorProperty; + Microsoft.UI.Xaml.Interop.IBindableObservableVector GetBindableObservableVector(Microsoft.UI.Xaml.Interop.IBindableObservableVector vector); + + Boolean ValidateBindableProperty( + Object bindableObject, + String property, + Windows.UI.Xaml.Interop.TypeName indexerType, + Boolean validateOnlyExists, + Boolean canRead, + Boolean canWrite, + Boolean isIndexer, + Windows.UI.Xaml.Interop.TypeName type, + Object indexerValue, + Object setValue, + out Object retrievedValue); void CopyProperties(IProperties1 src); void CopyPropertiesViaWeakReference(IProperties1 src); + Boolean CheckForBindableObjectInterface(Microsoft.UI.Xaml.Interop.IBindableIterable iterable); + // Async void CompleteAsync(); // Completes the in-flight async operation successfully void CompleteAsync(Int32 hr); // Completes the in-flight async operation with a failed HRESULT @@ -366,6 +418,7 @@ namespace TestComponentCSharp Windows.Foundation.Numerics.Vector2 Vector2Property; Windows.Foundation.Numerics.Vector3 Vector3Property; Windows.Foundation.Numerics.Vector4 Vector4Property; + Windows.Foundation.IReference Vector3NullableProperty; // Structs mapped to equivalent ones in the System namespace Windows.Foundation.TimeSpan TimeSpanProperty; @@ -375,22 +428,44 @@ namespace TestComponentCSharp // HResult->Exception type mapping Windows.Foundation.HResult HResultProperty; + Double Calculate(Windows.Foundation.Collections.IVector > values); + Windows.Foundation.Collections.IVector > GetNullableIntList(); + // Boxing static Int32 UnboxInt32(Object obj); static Boolean UnboxBoolean(Object obj); static String UnboxString(Object obj); static ProvideInt UnboxDelegate(Object obj); static EnumValue UnboxEnum(Object obj); + static Windows.UI.Xaml.Interop.TypeName UnboxType(Object obj); static Int32[] UnboxInt32Array(Object obj); static Boolean[] UnboxBooleanArray(Object obj); static String[] UnboxStringArray(Object obj); static Object BoxedDelegate{ get; }; static Object BoxedEnum{ get; }; + static Object BoxedEventHandler{ get; }; + static void UnboxAndCallProgressHandler(Object httpProgressHandler); + static Object BoxedStringArray{ get; }; + static Object BoxedInt32Array{ get; }; + static Object BoxedTimeSpanArray{ get; }; + Object BoxedObjectArray{ get; }; + + static Int32 UnboxInt32UsingPropertyValue(Object obj); + static String UnboxStringUsingPropertyValue(Object obj); + static Windows.Foundation.Rect UnboxRectUsingPropertyValue(Object obj); + static Int32[] UnboxInt32ArrayUsingPropertyValue(Object obj); + static Boolean[] UnboxBooleanArrayUsingPropertyValue(Object obj); + static Windows.Foundation.Point[] UnboxPointArrayUsingPropertyValue(Object obj); + + static Int32 GetPropertyType(Object obj); + static String GetName(Object obj); // WUX.Interop.TypeName -> System.Type mapping static Windows.UI.Xaml.Interop.TypeName Int32Type { get; }; static Windows.UI.Xaml.Interop.TypeName ThisClassType { get; }; static Windows.UI.Xaml.Interop.TypeName ReferenceInt32Type { get; }; + static Object BoxedType{ get; }; + static Windows.Foundation.Collections.IVector ListOfTypes{ get; }; static Boolean VerifyTypeIsInt32Type(Windows.UI.Xaml.Interop.TypeName type); static Boolean VerifyTypeIsThisClassType(Windows.UI.Xaml.Interop.TypeName type); @@ -404,10 +479,14 @@ namespace TestComponentCSharp // Keyword escapes String Catch(String params, out String lock); + String ThrowExceptionWithMessage(String message, Boolean throwNonMappedError); + String OriginateAndThrowExceptionWithMessage(String message); + // Interface projections static IProperties1 NativeProperties1{ get; }; static Object ServiceProvider{ get; }; static Object ComInterop{ get; }; + static Windows.Foundation.Collections.IPropertySet PropertySet{ get; }; // INotifyDataErrorInfo void RaiseDataErrorChanged(); @@ -428,7 +507,7 @@ namespace TestComponentCSharp } [threading(sta), marshaling_behavior(standard)] - runtimeclass NonAgileClass + runtimeclass NonAgileClass : Microsoft.UI.Xaml.Input.ICommand { NonAgileClass(); void Observe(Microsoft.UI.Xaml.Interop.IBindableObservableVector vector); @@ -452,6 +531,28 @@ namespace TestComponentCSharp CustomBindableVectorTest(); } + [default_interface] + runtimeclass CustomBindableObservableVectorTest : Microsoft.UI.Xaml.Interop.IBindableObservableVector + { + CustomBindableObservableVectorTest(); + } + + [default_interface] + runtimeclass CustomIteratorTest : Windows.Foundation.Collections.IIterator + { + CustomIteratorTest(); + CustomIteratorTest(Windows.Foundation.Collections.IIterator iterator); + } + + [default_interface] + runtimeclass CustomIterableTest : Windows.Foundation.Collections.IIterable + { + CustomIterableTest(); + CustomIterableTest(Windows.Foundation.Collections.IIterable iterable); + + static CustomIterableTest CreateWithCustomIterator(); + } + // SupportedOSPlatform warning tests [contract(Windows.Foundation.UniversalApiContract, 10)] [attributeusage(target_all)] @@ -480,11 +581,13 @@ namespace TestComponentCSharp static void Method(); static Int32 Property; static event Windows.Foundation.EventHandler Event; + static Int32 ReadWriteProperty{ get; }; [contract(Windows.Foundation.UniversalApiContract, 10)] { static void WarningMethod(); static Int32 WarningProperty; static event Windows.Foundation.EventHandler WarningEvent; + static Int32 ReadWriteProperty{ set; }; } } @@ -599,6 +702,14 @@ And this is another one" void f(); } + [default_interface] + runtimeclass ClassWithExplicitIUnknown + { + ClassWithExplicitIUnknown(); + ClassWithExplicitIUnknown(Int32 defaultValue); + Int32 Value{ get; set; }; + } + // Compile time test for sub windows namespace namespace Windows { @@ -625,6 +736,18 @@ And this is another one" }; } + // Compile time test for sub WinRT namespace + namespace WinRT + { + runtimeclass Class + { + Class(); + static void StaticMethod(); + void Method(); + void Method2(Windows.Foundation.IStringable stringable); + } + } + namespace AnotherAssembly { [default_interface] @@ -634,4 +757,19 @@ And this is another one" Int32 ReadWriteProperty{ set; }; } } + + namespace TestPublicExclusiveTo + { + interface IRegularInterface + { + Int32 Type { get; }; + } + + // This is a test for runtimeclass which can be have its interfaces implemented by other consumers + unsealed runtimeclass NonUniqueClass : IRegularInterface + { + String Path{ get; }; + static Int32 StaticProperty { get; }; + } + } } \ No newline at end of file diff --git a/src/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj b/src/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj index cbbd048ac..e57f313fc 100644 --- a/src/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj +++ b/src/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj @@ -1,7 +1,12 @@ - - + + + + + + + high true @@ -80,26 +85,32 @@ + + TestComponentCSharp.idl + + + + Create @@ -107,12 +118,14 @@ TestComponentCSharp.idl + + @@ -124,16 +137,33 @@ - - + + + + + + + + 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/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj.filters b/src/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj.filters index f87e12554..a2df1ce81 100644 --- a/src/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj.filters +++ b/src/Tests/TestComponentCSharp/TestComponentCSharp.vcxproj.filters @@ -21,6 +21,10 @@ + + + + @@ -35,6 +39,10 @@ + + + + diff --git a/src/Tests/TestComponentCSharp/WarningStatic.cpp b/src/Tests/TestComponentCSharp/WarningStatic.cpp index 275f592a8..79907d16e 100644 --- a/src/Tests/TestComponentCSharp/WarningStatic.cpp +++ b/src/Tests/TestComponentCSharp/WarningStatic.cpp @@ -38,4 +38,11 @@ namespace winrt::TestComponentCSharp::implementation void WarningStatic::WarningEvent(winrt::event_token const& token) noexcept { } + int32_t WarningStatic::ReadWriteProperty() + { + return 0; + } + void WarningStatic::ReadWriteProperty(int32_t value) + { + } } diff --git a/src/Tests/TestComponentCSharp/WarningStatic.h b/src/Tests/TestComponentCSharp/WarningStatic.h index b2697e8b2..1ff73f052 100644 --- a/src/Tests/TestComponentCSharp/WarningStatic.h +++ b/src/Tests/TestComponentCSharp/WarningStatic.h @@ -17,6 +17,8 @@ namespace winrt::TestComponentCSharp::implementation static void WarningProperty(int32_t value); static winrt::event_token WarningEvent(Windows::Foundation::EventHandler const& handler); static void WarningEvent(winrt::event_token const& token) noexcept; + static int32_t ReadWriteProperty(); + static void ReadWriteProperty(int32_t value); }; } namespace winrt::TestComponentCSharp::factory_implementation diff --git a/src/Tests/TestComponentCSharp/WinRT.Class.cpp b/src/Tests/TestComponentCSharp/WinRT.Class.cpp new file mode 100644 index 000000000..309766d4a --- /dev/null +++ b/src/Tests/TestComponentCSharp/WinRT.Class.cpp @@ -0,0 +1,18 @@ +#include "pch.h" +#include "WinRT.Class.h" +#include "WinRT.Class.g.cpp" + +namespace winrt::TestComponentCSharp::WinRT::implementation +{ + void Class::StaticMethod() + { + } + + void Class::Method() + { + } + + void Class::Method2(winrt::Windows::Foundation::IStringable const& stringable) + { + } +} diff --git a/src/Tests/TestComponentCSharp/WinRT.Class.h b/src/Tests/TestComponentCSharp/WinRT.Class.h new file mode 100644 index 000000000..809f6153b --- /dev/null +++ b/src/Tests/TestComponentCSharp/WinRT.Class.h @@ -0,0 +1,20 @@ +#pragma once +#include "WinRT.Class.g.h" + +namespace winrt::TestComponentCSharp::WinRT::implementation +{ + struct Class : ClassT + { + Class() = default; + + static void StaticMethod(); + void Method(); + void Method2(winrt::Windows::Foundation::IStringable const& stringable); + }; +} +namespace winrt::TestComponentCSharp::WinRT::factory_implementation +{ + struct Class : ClassT + { + }; +} diff --git a/src/Tests/TestComponentCSharp/packages.config b/src/Tests/TestComponentCSharp/packages.config index 56121e4ac..3123fcca5 100644 --- a/src/Tests/TestComponentCSharp/packages.config +++ b/src/Tests/TestComponentCSharp/packages.config @@ -1,5 +1,11 @@  - - + + + + + + + + \ No newline at end of file diff --git a/src/Tests/TestComponentCSharp/pch.h b/src/Tests/TestComponentCSharp/pch.h index 3ac7f6ff5..b745c18df 100644 --- a/src/Tests/TestComponentCSharp/pch.h +++ b/src/Tests/TestComponentCSharp/pch.h @@ -10,6 +10,7 @@ #include #include #include +#include // TODO: Replace with latest Cpp/WinRT namespace winrt diff --git a/src/Tests/UnitTest/ComGenerationTests.cs b/src/Tests/UnitTest/ComGenerationTests.cs new file mode 100644 index 000000000..3f5d0bb3a --- /dev/null +++ b/src/Tests/UnitTest/ComGenerationTests.cs @@ -0,0 +1,56 @@ +#if NET8_0_OR_GREATER +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; +using Windows.ApplicationModel.DataTransfer.DragDrop.Core; +using Windows.Graphics.Display; +using Windows.Graphics.Printing; +using Windows.Media; +using Windows.Media.PlayTo; +using Windows.Security.Authentication.Web.Core; +using Windows.Security.Credentials; +using Windows.Security.Credentials.UI; +using Windows.UI.ApplicationSettings; +using Windows.UI.Input; +using Windows.UI.Input.Core; +using Windows.UI.Input.Spatial; +using Windows.UI.ViewManagement; +using Xunit; +using WinRT; +using TestComponentCSharp; + +namespace UnitTest +{ + [GeneratedComInterface] + [Guid("15651B9F-6C6B-4CC0-944C-C7D7B0F36F81")] + internal partial interface IComInteropGenerated + { + Int64 ReturnWindowHandle(IntPtr hwnd, Guid iid); + } + + public class ComGenerationTests + { + private static readonly Guid IID_IComInterop = new Guid("15651B9F-6C6B-4CC0-944C-C7D7B0F36F81"); + + [Fact] + public void TestHWND() + { + var comInterop = (IComInteropGenerated)(object)Class.ComInterop; + if (System.Environment.Is64BitProcess) + { + var hwnd = new IntPtr(0x0123456789ABCDEF); + var value = comInterop.ReturnWindowHandle(hwnd, IID_IComInterop); + var hwndValue = hwnd.ToInt64(); + Assert.Equal(hwndValue, value); + } + else + { + var hwnd = new IntPtr(0x01234567); + var value = comInterop.ReturnWindowHandle(hwnd, IID_IComInterop); + var hwndValue = hwnd.ToInt32(); + Assert.Equal(hwndValue, value); + } + } + } +} +#endif \ No newline at end of file diff --git a/src/Tests/UnitTest/ComInteropTests.cs b/src/Tests/UnitTest/ComInteropTests.cs index e2f6c80a0..a7b6c50cc 100644 --- a/src/Tests/UnitTest/ComInteropTests.cs +++ b/src/Tests/UnitTest/ComInteropTests.cs @@ -1,5 +1,6 @@ using System; using System.Runtime.InteropServices; +using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer.DragDrop.Core; using Windows.Graphics.Display; using Windows.Graphics.Printing; @@ -161,5 +162,12 @@ public void TestDisplayInformation() Assert.Throws(() => DisplayInformationInterop.GetForWindow(new IntPtr(0))); Assert.Throws(() => DisplayInformationInterop.GetForMonitor(new IntPtr(0))); } + + [Fact] + public void TestDataTransferManager() + { + Assert.Throws(() => DataTransferManagerInterop.GetForWindow(new IntPtr(0))); + Assert.Throws(() => DataTransferManagerInterop.ShowShareUIForWindow(new IntPtr(0))); + } } } diff --git a/src/Tests/UnitTest/ExceptionTests.cs b/src/Tests/UnitTest/ExceptionTests.cs new file mode 100644 index 000000000..43376ddfb --- /dev/null +++ b/src/Tests/UnitTest/ExceptionTests.cs @@ -0,0 +1,24 @@ +using System; +using System.Globalization; +using WinRT; +using Xunit; + +namespace UnitTest +{ + public class ExceptionTests + { + [Fact] + public void TestGetExceptionForHR_WithValidHResult_ReturnsSystemFormattedException() + { + const int RPC_E_WRONG_THREAD = unchecked((int)0x8001010E); + + Exception exception = ExceptionHelpers.GetExceptionForHR(RPC_E_WRONG_THREAD); + Assert.NotNull(exception); + Assert.False(string.IsNullOrWhiteSpace(exception.Message)); + if (CultureInfo.CurrentUICulture.Name == "en-US") + { + Assert.Equal("The application called an interface that was marshalled for a different thread. (0x8001010E)", exception.Message); + } + } + } +} diff --git a/src/Tests/UnitTest/GuidTests.cs b/src/Tests/UnitTest/GuidTests.cs index 8e9c8cf87..855a2a75d 100644 --- a/src/Tests/UnitTest/GuidTests.cs +++ b/src/Tests/UnitTest/GuidTests.cs @@ -110,6 +110,12 @@ public void TestGenerics() AssertGuid>("0d82bd8d-fe62-5d67-a7b9-7886dd75bc4e"); AssertGuid>("5dafe591-86dc-59aa-bfda-07f5d59fc708"); AssertGuid>("c8477314-b257-511b-a3a1-9e4eb6385152"); + AssertGuid>("1bfca4f6-2c4e-5174-9869-b39d35848fcc"); + AssertGuid>("2f5fb6d3-231f-57a1-9f2a-daa7e43bf075"); + AssertGuid>("1d9ba3f5-b997-5a7d-82c4-7857ecbf3a42"); + AssertGuid>("a3c9b753-57ad-537f-9626-4ae5785473d4"); + AssertGuid>("94390dc5-e442-5870-88b6-007e232f902c"); + AssertGuid>("c0d513a9-ec4a-5a5d-b6d5-b707defdb9f7"); } } } diff --git a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs index 6badd330a..8912bb541 100644 --- a/src/Tests/UnitTest/TestComponentCSharp_Tests.cs +++ b/src/Tests/UnitTest/TestComponentCSharp_Tests.cs @@ -1,2507 +1,3162 @@ -using System; -using System.IO; -using System.Linq; -using System.Numerics; -using System.Threading; -using System.Threading.Tasks; -using Xunit; -using WinRT; - -using Windows.Foundation; -using Windows.UI; -using Windows.Security.Credentials.UI; -using Windows.Storage; -using Windows.Storage.Streams; -using Microsoft.UI.Xaml; -using Microsoft.UI.Xaml.Controls.Primitives; -using Microsoft.UI.Xaml.Interop; -using Microsoft.UI.Xaml.Media; -using Microsoft.UI.Xaml.Media.Animation; -using Microsoft.UI.Xaml.Media.Media3D; - -using TestComponentCSharp; -using System.Collections.Generic; -using System.Collections; -using WinRT.Interop; -using System.Runtime.InteropServices; -using System.Runtime.InteropServices.WindowsRuntime; -using Windows.Security.Cryptography; -using Windows.Security.Cryptography.Core; -using System.Reflection; +using System; +using System.IO; +using System.Linq; +using System.Numerics; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Xunit; +using WinRT; + +using Windows.Foundation; +using Windows.UI; +using Windows.Storage; +using Windows.Storage.Streams; +using Microsoft.UI.Xaml; +using Microsoft.UI.Xaml.Controls.Primitives; +using Microsoft.UI.Xaml.Interop; +using Microsoft.UI.Xaml.Media; +using Microsoft.UI.Xaml.Media.Animation; +using Microsoft.UI.Xaml.Media.Media3D; + +using TestComponentCSharp; +using System.Collections.Generic; +using System.Collections; +using WinRT.Interop; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.WindowsRuntime; +using Windows.Security.Cryptography; +using Windows.Security.Cryptography.Core; +using System.Reflection; using Windows.Devices.Enumeration.Pnp; using System.Diagnostics; +using Windows.Devices.Enumeration; +using Windows.UI.Notifications; + +#if NET +using WeakRefNS = System; +#else +using WeakRefNS = WinRT; +#endif + +#if NET +// Test SupportedOSPlatform warnings for APIs targeting 10.0.19041.0: +[assembly: global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.18362.0")] +#endif + +namespace UnitTest +{ + public class TestCSharp + { + public Class TestObject { get; private set; } + + public TestCSharp() + { + TestObject = new Class(); + } + + public enum E { A, B, C } + + public struct Estruct + { + E value; + } + + + // Test a fix for a bug in Mono.Cecil that was affecting the IIDOptimizer when it encountered long class names + [Fact] + public void TestLongClassNameEventSource() + { + bool flag = false; + var long_class_name = new ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz(); + long_class_name.EventForAVeryLongClassName += + (ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz sender, ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz args) + => flag = true; + long_class_name.InvokeEvent(); + Assert.True(flag); + } + + [Fact] + public void TestEventArgsVector() + { + var eventArgsVector = TestObject.GetEventArgsVector(); + Assert.Equal(1, eventArgsVector.Count); + foreach (var dataErrorChangedEventArgs in eventArgsVector) + { + var propName = dataErrorChangedEventArgs.PropertyName; + Assert.Equal("name", propName); + } + } + + [Fact] + public void TestNonGenericDelegateVector() + { + var provideUriVector = TestObject.GetNonGenericDelegateVector(); + + Assert.Equal(1, provideUriVector.Count); + + foreach (var provideUri in provideUriVector) + { + Uri delegateTarget = provideUri.Invoke(); + Assert.Equal("http://microsoft.com", delegateTarget.OriginalString); + } + } + + [Fact] + public void TestEnums() + { + // Enums + var expectedEnum = EnumValue.Two; + TestObject.EnumProperty = expectedEnum; + Assert.Equal(expectedEnum, TestObject.EnumProperty); + expectedEnum = EnumValue.One; + TestObject.CallForEnum(() => expectedEnum); + TestObject.EnumPropertyChanged += + (object sender, EnumValue value) => Assert.Equal(expectedEnum, value); + TestObject.RaiseEnumChanged(); + + var expectedEnumStruct = new EnumStruct() { value = EnumValue.Two }; + TestObject.EnumStructProperty = expectedEnumStruct; + Assert.Equal(expectedEnumStruct, TestObject.EnumStructProperty); + expectedEnumStruct = new EnumStruct() { value = EnumValue.One }; + TestObject.CallForEnumStruct(() => expectedEnumStruct); + TestObject.EnumStructPropertyChanged += + (object sender, EnumStruct value) => Assert.Equal(expectedEnumStruct, value); + TestObject.RaiseEnumStructChanged(); + + var expectedEnums = new EnumValue[] { EnumValue.One, EnumValue.Two }; + TestObject.EnumsProperty = expectedEnums; + Assert.Equal(expectedEnums, TestObject.EnumsProperty); + TestObject.CallForEnums(() => expectedEnums); + Assert.Equal(expectedEnums, TestObject.EnumsProperty); + + TestObject.EnumsProperty = null; + Assert.Equal(null, TestObject.EnumsProperty); + + var expectedEnumStructs = new EnumStruct[] { new EnumStruct(EnumValue.One), new EnumStruct(EnumValue.Two) }; + TestObject.EnumStructsProperty = expectedEnumStructs; + Assert.Equal(expectedEnumStructs, TestObject.EnumStructsProperty); + TestObject.CallForEnumStructs(() => expectedEnumStructs); + Assert.Equal(expectedEnumStructs, TestObject.EnumStructsProperty); + + TestObject.EnumStructsProperty = null; + Assert.Equal(null, TestObject.EnumStructsProperty); + + // Flags + var expectedFlag = FlagValue.All; + TestObject.FlagProperty = expectedFlag; + Assert.Equal(expectedFlag, TestObject.FlagProperty); + expectedFlag = FlagValue.One; + TestObject.CallForFlag(() => expectedFlag); + TestObject.FlagPropertyChanged += + (object sender, FlagValue value) => Assert.Equal(expectedFlag, value); + TestObject.RaiseFlagChanged(); + + var expectedFlagStruct = new FlagStruct() { value = FlagValue.All }; + TestObject.FlagStructProperty = expectedFlagStruct; + Assert.Equal(expectedFlagStruct, TestObject.FlagStructProperty); + expectedFlagStruct = new FlagStruct() { value = FlagValue.One }; + TestObject.CallForFlagStruct(() => expectedFlagStruct); + TestObject.FlagStructPropertyChanged += + (object sender, FlagStruct value) => Assert.Equal(expectedFlagStruct, value); + TestObject.RaiseFlagStructChanged(); + + var expectedFlags = new FlagValue[] { FlagValue.One, FlagValue.All }; + TestObject.FlagsProperty = expectedFlags; + Assert.Equal(expectedFlags, TestObject.FlagsProperty); + TestObject.CallForFlags(() => expectedFlags); + Assert.Equal(expectedFlags, TestObject.FlagsProperty); + + var expectedFlagStructs = new FlagStruct[] { new FlagStruct(FlagValue.One), new FlagStruct(FlagValue.All) }; + TestObject.FlagStructsProperty = expectedFlagStructs; + Assert.Equal(expectedFlagStructs, TestObject.FlagStructsProperty); + TestObject.CallForFlagStructs(() => expectedFlagStructs); + Assert.Equal(expectedFlagStructs, TestObject.FlagStructsProperty); + } + + [Fact] + public void TestGetByte() + { + var array = new byte[] { 0x01 }; + var buff = array.AsBuffer(); + Assert.True(buff.Length == 1); + byte b = buff.GetByte(0); + Assert.True(b == 0x01); + } + + [Fact] + public void TestManyBufferExtensionMethods() + { + var arrayLen3 = new byte[] { 0x01, 0x02, 0x03 }; + var buffLen3 = arrayLen3.AsBuffer(); + + var arrayLen4 = new byte[] { 0x11, 0x12, 0x13, 0x14 }; + var buffLen4 = arrayLen4.AsBuffer(); + + var arrayLen4Again = new byte[4]; + + arrayLen3.CopyTo(1, buffLen4, 0, 1); // copy just the second element of the array to the beginning of the buffer + Assert.True(buffLen4.Length == 4); + Assert.Throws(() => buffLen4.GetByte(5)); // shouldn't have a 5th element + Assert.True(buffLen4.GetByte(0) == 0x02); // make sure we got the 2nd element of the array + + arrayLen3.CopyTo(buffLen4); // Array to Buffer copying + Assert.True(buffLen4.Length == 4); + Assert.True(buffLen4.GetByte(0) == 0x01); // make sure we updated the first few + Assert.True(buffLen4.GetByte(1) == 0x02); + Assert.True(buffLen4.GetByte(2) == 0x03); + Assert.True(buffLen4.GetByte(3) == 0x14); // and kept the last one + + var buffLen3Again = buffLen3.ToArray().AsBuffer(); + Assert.True(buffLen3Again.GetByte(0) == 0x01); + Assert.True(buffLen3Again.GetByte(1) == 0x02); + Assert.True(buffLen3Again.GetByte(2) == 0x03); + + Assert.False(buffLen3.IsSameData(buffLen3Again)); // different memory regions + + buffLen4.CopyTo(arrayLen4Again); // Buffer to Array copying + var array4 = buffLen4.ToArray(); + Assert.True(arrayLen4Again.Length == array4.Length); + for (int i = 0; i < arrayLen4Again.Length; ++i) + { + Assert.True(arrayLen4Again[i] == array4[i]); // make sure we have equal array + } + } + + [Fact] + public void TestIsSameDataDifferentArrays() + { + var arr = new byte[] { 0x01, 0x02 }; + var buf1 = arr.AsBuffer(); + var arr2 = new byte[] { 0x01, 0x02 }; + var buf2 = arr2.AsBuffer(); + Assert.False(buf1.IsSameData(buf2)); + } + + [Fact] + public void TestIsSameDataUsingCopyTo() + { + var arr = new byte[] { 0x01, 0x02 }; + var buf1 = arr.AsBuffer(); + var buf2 = new Windows.Storage.Streams.Buffer(2); + buf1.CopyTo(buf2); + Assert.False(buf1.IsSameData(buf2)); + } + + [Fact] + public void TestIsSameDataUsingAsBufferTwice() + { + var arr = new byte[] { 0x01, 0x02 }; + var buf1 = arr.AsBuffer(); + var buf2 = arr.AsBuffer(); + Assert.True(buf1.IsSameData(buf2)); + } + + [Fact] + public void TestIsSameDataUsingToArray() + { + var arr = new byte[] { 0x01, 0x02 }; + var buf1 = arr.AsBuffer(); + var buf2 = buf1.ToArray().AsBuffer(); + Assert.False(buf1.IsSameData(buf2)); + } + + [Fact] + public void TestBufferAsStreamUsingAsBuffer() + { + var arr = new byte[] { 0x01, 0x02 }; + Stream stream = arr.AsBuffer().AsStream(); + Assert.True(stream != null); + Assert.True(stream.Length == 2); + } + +#if !NET47 + [Fact] + public void TestBufferAsStreamUsingAsBufferWithOffset() + { + var arr = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + var buffer = arr.AsBuffer(1, 2); + Stream stream = buffer.AsStream(); + Assert.True(stream != null); + Assert.True(stream.Length == 2); + + stream.Write(new byte[] { 0x05, 0x06 }); + Assert.True(stream.Length == 2); + Assert.True(buffer.Length == 2); + + Assert.Equal((byte)0x05, arr[1]); + Assert.Equal((byte)0x06, arr[2]); + } + + [Fact] + public void TestBufferAsStreamUsingAsBufferWithOffsetAndCapacity() + { + var arr = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + var buffer = arr.AsBuffer(1, 2, 3); + Stream stream = buffer.AsStream(); + Assert.True(stream != null); + Assert.True(stream.Length == 2); + + stream.Write(new byte[] { 0x05, 0x06, 0x07 }); + Assert.True(stream.Length == 3); + Assert.True(buffer.Length == 3); + + Assert.Equal((byte)0x05, arr[1]); + Assert.Equal((byte)0x06, arr[2]); + Assert.Equal((byte)0x07, arr[3]); + } +#endif + + [Fact] + public void TestBufferAsStreamWithEmptyBuffer() + { + var buffer = new Windows.Storage.Streams.Buffer(0); + Stream stream = buffer.AsStream(); + Assert.True(stream != null); + Assert.True(stream.Length == 0); + } + + [Fact] + public void TestBufferAsStreamRead() + { + var arr = new byte[] { 0x01, 0x02 }; + Stream stream = arr.AsBuffer().AsStream(); + Assert.True(stream != null); + Assert.True(stream.Length == 2); + int byte1 = stream.ReadByte(); + Assert.Equal(0x01, byte1); + } + + [Fact] + public void TestBufferAsStreamWrite() + { + var buffer = new Windows.Storage.Streams.Buffer(2); + Stream stream = buffer.AsStream(); + Assert.True(stream != null); + Assert.True(stream.Length == 0); + stream.WriteByte(0x01); + Assert.True(stream.Length == 1); + Assert.True(buffer.Length == 1); + } + + [Fact] + public void TestBufferImproperReadCopyToOutOfBounds() + { + var array = new byte[] { 0x01, 0x02, 0x03 }; + var buffer = array.AsBuffer(); + var biggerBuffer = new Windows.Storage.Streams.Buffer(5); + buffer.CopyTo(biggerBuffer); + Assert.Throws(() => biggerBuffer.ToArray(4, 2)); + } + + [Fact] + public void TestBufferImproperReadCopyToStraddleBounds() + { + var array = new byte[] { 0x01, 0x02, 0x03 }; + var buffer = array.AsBuffer(); + var biggerBuffer = new Windows.Storage.Streams.Buffer(5); + buffer.CopyTo(biggerBuffer); + Assert.Throws(() => biggerBuffer.ToArray(2, 2)); + } + + [Fact] + public void TestBufferImproperReadGetByte() + { + var array = new byte[] { 0x01, 0x02, 0x03 }; + var buffer = array.AsBuffer(); + Assert.Throws(() => buffer.GetByte(4)); + } + + [Fact] + public void TestEmptyBufferToArray() + { + var buffer = new Windows.Storage.Streams.Buffer(0); + var array = buffer.ToArray(); + Assert.True(array.Length == 0); + } + + [Fact] + public void TestArrayCopyToBufferEndToBeginning() + { + IBuffer buf = new Windows.Storage.Streams.Buffer(3); + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + arr.CopyTo(3, buf, 0, 0); + } + + [Fact] + public void TestArrayCopyToBufferEndToEnd2() + { + IBuffer buf = new Windows.Storage.Streams.Buffer(3); + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + arr.CopyTo(0, buf, 0, 3); + } + + [Fact] + public void TestArrayCopyToBufferEndToEnd() + { + IBuffer buf = new Windows.Storage.Streams.Buffer(3); + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + arr.CopyTo(3, buf, 3, 0); + } + + [Fact] + public void TestArrayCopyToBufferMidToMid() + { + IBuffer buf = new Windows.Storage.Streams.Buffer(3); + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + arr.CopyTo(1, buf, 1, 0); + } + + [Fact] + public void TestArrayCopyToBufferMidToEnd() + { + IBuffer buf = new Windows.Storage.Streams.Buffer(3); + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + arr.CopyTo(1, buf, 3, 0); + } + + [Fact] + public void TestBufferCopyToArrayEndToEnd() + { + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + var buf = arr.AsBuffer(); + var target = new byte[4]; + buf.CopyTo(3, target, 4, 0); + } + + [Fact] + public void BufferToArrayWithZeroCountAtEnd2() + { + byte[] array = { 0xA1, 0xA2, 0xA3 }; + var result = array.AsBuffer().ToArray(3, 0); + Assert.True(result != null); + Assert.True(0 == result.Length); + } + + [Fact] + public void BufferToArrayWithZeroCountAtEnd_WorksWithSpans() + { + byte[] array = { 0xA1, 0xA2, 0xA3 }; + var result = array.AsSpan().Slice(3, 0).ToArray(); + Assert.True(result != null); + Assert.True(0 == result.Length); + } + + [Fact] + public void TestWinRTBufferWithZeroLength() + { + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + MemoryStream stream = new MemoryStream(arr, 0, 3, false, true); + IBuffer buff = stream.GetWindowsRuntimeBuffer(3, 0); + Assert.True(buff != null); + Assert.True(buff.Length == 0); + } + + [Fact] + public void TestEmptyBufferCopyTo() + { + var buffer = new Windows.Storage.Streams.Buffer(0); + byte[] array = { }; + buffer.CopyTo(array); + Assert.True(array.Length == 0); + } + + [Fact] + public void TestBufferToArrayCapacityLargerThanLength() + { + var buffer = new Windows.Storage.Streams.Buffer(100); + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + arr.CopyTo(0, buffer, 0, 3); + + byte[] newArr = buffer.ToArray(); + Assert.True(newArr.Length == 3); + } + + [Fact] + public void TestBufferCopyToBufferCapacityLargerThanLength() + { + var buffer = new Windows.Storage.Streams.Buffer(100); + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + arr.CopyTo(0, buffer, 0, 3); + + var buffer2 = new Windows.Storage.Streams.Buffer(50); + byte[] arr2 = new byte[] { 0x01, 0x02 }; + arr2.CopyTo(0, buffer2, 0, 2); + + Assert.True(buffer2.Length == 2); + + buffer.CopyTo(buffer2); + Assert.True(buffer2.Length == 3); + } + +#if NET + [Fact] + public void TestTryGetDataUnsafe() + { + IBuffer buf = new Windows.Storage.Streams.Buffer(3); + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + arr.CopyTo(0, buf, 0, 3); + + Assert.True(WindowsRuntimeMarshal.TryGetDataUnsafe(buf, out IntPtr dataPtr)); + Assert.True(dataPtr != IntPtr.Zero); + + unsafe + { + Span buffSpan = new Span((byte*)dataPtr, (int)buf.Length); + + byte[] arr2 = buffSpan.ToArray(); + Assert.True(arr.SequenceEqual(arr2)); + } + + // Ensure buf doesn't get collected while we use the data pointer + GC.KeepAlive(buf); + } + + [Fact] + public void TestTryGetDataUnsafe_MemoryBufferReference() + { + var buffer = new Windows.Foundation.MemoryBuffer(256); + var reference = buffer.CreateReference(); + + Assert.True(WindowsRuntimeMarshal.TryGetDataUnsafe(reference, out IntPtr dataPtr1, out uint capacity1)); + Assert.True(dataPtr1 != IntPtr.Zero); + Assert.True(capacity1 == 256); + + Assert.True(WindowsRuntimeMarshal.TryGetDataUnsafe(reference, out IntPtr dataPtr2, out uint capacity2)); + Assert.True(dataPtr2 != IntPtr.Zero); + Assert.True(capacity2 == 256); + + Assert.True(dataPtr1 == dataPtr2); + + // Ensure the reference doesn't get collected while we use the data pointer + GC.KeepAlive(reference); + } + + [Fact] + public void TestBufferTryGetArray() + { + byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; + var buffer = arr.AsBuffer(); + + Assert.True(WindowsRuntimeMarshal.TryGetArray(buffer, out ArraySegment array)); + Assert.Equal(arr, array.Array); + } + + [Fact] + public void TestBufferTryGetArraySubset() + { + var arr = new byte[] { 0x01, 0x02, 0x03, 0x04 }; + var buffer = arr.AsBuffer(1, 2); + + Assert.True(WindowsRuntimeMarshal.TryGetArray(buffer, out ArraySegment array)); + Assert.Equal(arr, array.Array); + Assert.Equal(1, array.Offset); + Assert.Equal(2, array.Count); + } +#endif + + [Fact] + public void TestTypePropertyWithSystemType() + { + TestObject.TypeProperty = typeof(System.Type); + Assert.Equal("Windows.UI.Xaml.Interop.TypeName", TestObject.GetTypePropertyAbiName()); + Assert.Equal("Metadata", TestObject.GetTypePropertyKind()); + } + + class CustomDictionary : Dictionary { } + + [Fact] + public void TestTypePropertyWithCustomType() + { + TestObject.TypeProperty = typeof(CustomDictionary); + var name = TestObject.GetTypePropertyAbiName(); + Assert.Equal("UnitTest.TestCSharp+CustomDictionary, UnitTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", name); + } + + [Fact] + public void TestVectorCastConversion() + { + var vector = TestObject.GetUriVectorAsIInspectableVector(); + var uriVector = vector.Cast(); + var first = uriVector.First(); + Assert.Equal(vector, uriVector); + } + + async Task LookupPorts() + { + var ports = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync( + Windows.Devices.SerialCommunication.SerialDevice.GetDeviceSelector(), + new string[] { "System.ItemNameDisplay" }); + foreach (var port in ports) + { + object o = port.Properties["System.ItemNameDisplay"]; + Assert.NotNull(o); + } + } + + [Fact] + public void TestReadOnlyDictionaryLookup() + { + Assert.True(LookupPorts().Wait(5000)); + } + +#if NET + async Task InvokeStreamWriteZeroBytes() + { + var random = new Random(42); + byte[] data = new byte[256]; + random.NextBytes(data); + + using var stream = new InMemoryRandomAccessStream().AsStream(); + await stream.WriteAsync(data, 0, 0); + await stream.WriteAsync(data, data.Length, 0); + } + + [Fact] + public void TestStreamWriteZeroByte() + { + Assert.True(InvokeStreamWriteZeroBytes().Wait(1000)); + } + + async Task InvokeStreamWriteAsync() + { + using var fileStream = File.OpenWrite("TestFile.txt"); + using var winRTStream = fileStream.AsOutputStream(); + + var winRTBuffer = new Windows.Storage.Streams.Buffer(capacity: 0); + + await winRTStream.WriteAsync(winRTBuffer); + Assert.True(true); + } + + [Fact] + public void TestStreamWriteAsync() + { + Assert.True(InvokeStreamWriteAsync().Wait(1000)); + } + + [Fact] + public void TestAsStream() + { + using InMemoryRandomAccessStream winrtStream = new InMemoryRandomAccessStream(); + using Stream normalStream = winrtStream.AsStream(); + using var memoryStream = new MemoryStream(); + normalStream.CopyTo(memoryStream); + } + + async Task InvokeStreamWriteAndReadAsync() + { + var random = new Random(42); + byte[] data = new byte[256]; + random.NextBytes(data); + + using var stream = new InMemoryRandomAccessStream().AsStream(); + await stream.WriteAsync(data, 0, data.Length); + stream.Seek(0, SeekOrigin.Begin); + + byte[] read = new byte[256]; + await stream.ReadAsync(read, 0, read.Length); + Assert.Equal(read, data); + } + + [Fact] + public void TestStreamWriteAndRead() + { + Assert.True(InvokeStreamWriteAndReadAsync().Wait(1000)); + } + + [Fact] + public void TestDynamicInterfaceCastingOnValidInterface() + { + var agileObject = (IAgileObject)(IWinRTObject)TestObject; + Assert.NotNull(agileObject); + } + + [Fact] + public void TestDynamicInterfaceCastingOnInvalidInterface() + { + Assert.ThrowsAny(() => (IStringableInterop)(IWinRTObject)TestObject); + } + + [Fact] + public void TestBuffer() + { + var arr1 = new byte[] { 0x01, 0x02 }; + var buff = arr1.AsBuffer(); + var arr2 = buff.ToArray(0, 2); + Assert.True(arr1[0] == arr2[0]); + Assert.True(arr1[1] == arr2[1]); + } + +#endif + + async Task TestStorageFileAsync() + { + var folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + StorageFile file = await StorageFile.GetFileFromPathAsync(folderPath + "\\UnitTest.dll"); + var handle = WindowsRuntimeStorageExtensions.CreateSafeFileHandle(file, FileAccess.Read); + Assert.NotNull(handle); + } + + [Fact] + public void TestStorageFile() + { + Assert.True(TestStorageFileAsync().Wait(5000)); + } + + async Task TestStorageFolderAsync() + { + var folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + StorageFolder folder = await StorageFolder.GetFolderFromPathAsync(folderPath); + var handle = WindowsRuntimeStorageExtensions.CreateSafeFileHandle(folder, "UnitTest.dll", FileMode.Open, FileAccess.Read); + Assert.NotNull(handle); + } + + [Fact] + public void TestStorageFolder() + { + Assert.True(TestStorageFolderAsync().Wait(5000)); + } + + async Task InvokeWriteBufferAsync() + { + var random = new Random(42); + byte[] data = new byte[256]; + random.NextBytes(data); + + using var stream = new InMemoryRandomAccessStream(); + IBuffer buffer = data.AsBuffer(); + await stream.WriteAsync(buffer); + } + + [Fact] + public void TestWriteBuffer() + { + Assert.True(InvokeWriteBufferAsync().Wait(1000)); + } + + [Fact] + public void TestUri() + { + var base_uri = "https://github.com"; + var relative_uri = "microsoft/CsWinRT"; + var full_uri = base_uri + "/" + relative_uri; + var managedUri = new Uri(full_uri); + + var uri1 = ABI.System.Uri.FromAbi(ABI.System.Uri.FromManaged(managedUri)); + var str1 = uri1.ToString(); + Assert.Equal(full_uri, str1); + + var expected = new Uri("http://expected"); + TestObject.UriProperty = expected; + Assert.Equal(expected, TestObject.UriProperty); + + TestObject.CallForUri(() => managedUri); + TestObject.UriPropertyChanged += + (object sender, Uri value) => Assert.Equal(managedUri, value); + TestObject.RaiseUriChanged(); + + var uri2 = MarshalInspectable.FromAbi(ABI.System.Uri.FromManaged(managedUri)); + var str2 = uri2.ToString(); + Assert.Equal(full_uri, str2); + + var uri3 = MarshalInspectable.FromAbi(ABI.System.Uri.FromManaged(managedUri)); + var str3 = uri3.ToString(); + Assert.Equal(full_uri, str3); + } + + [Fact] + public void TestNulls() + { + TestObject.StringProperty = null; + Assert.Equal("", TestObject.StringProperty); + TestObject.CallForString(() => null); + TestObject.StringPropertyChanged += + (Class sender, string value) => Assert.Equal("", value); + TestObject.RaiseStringChanged(); + + TestObject.UriProperty = null; + Assert.Null(TestObject.UriProperty); + TestObject.CallForUri(() => null); + TestObject.UriPropertyChanged += + (object sender, Uri value) => Assert.Null(value); + TestObject.RaiseUriChanged(); + + TestObject.ObjectProperty = null; + Assert.Null(TestObject.ObjectProperty); + TestObject.CallForObject(() => null); + TestObject.ObjectPropertyChanged += + (object sender, Object value) => Assert.Null(value); + TestObject.RaiseObjectChanged(); + + // todo: arrays, delegates, event args, mapped types... + } + + [Fact] + public void TestEvents() + { + int events_expected = 0; + int events_received = 0; + + TestObject.Event0 += () => events_received++; + TestObject.InvokeEvent0(); + events_expected++; + + TestObject.Event1 += (Class sender) => + { + events_received++; + Assert.IsAssignableFrom(sender); + }; + TestObject.InvokeEvent1(TestObject); + events_expected++; + + int int0 = 42; + TestObject.Event2 += (Class sender, int arg0) => + { + events_received++; + Assert.Equal(arg0, int0); + }; + TestObject.InvokeEvent2(TestObject, int0); + events_expected++; + + string string1 = "foo"; + TestObject.Event3 += (Class sender, int arg0, string arg1) => + { + events_received++; + Assert.Equal(arg1, string1); + }; + TestObject.InvokeEvent3(TestObject, int0, string1); + events_expected++; + + int[] ints = { 1, 2, 3 }; + TestObject.NestedEvent += (object sender, IList arg0) => + { + events_received++; + Assert.True(arg0.SequenceEqual(ints)); + }; + TestObject.InvokeNestedEvent(TestObject, ints); + events_expected++; + + TestObject.ReturnEvent += (int arg0) => + { + events_received++; + return arg0; + }; + Assert.Equal(42, TestObject.InvokeReturnEvent(42)); + events_expected++; + + var collection0 = new int[] { 42, 1729 }; + var collection1 = new Dictionary { [1] = "foo", [2] = "bar" }; + TestObject.CollectionEvent += (Class sender, IList arg0, IDictionary arg1) => + { + events_received++; + Assert.True(arg0.SequenceEqual(collection0)); + Assert.True(arg1.SequenceEqual(collection1)); + }; + TestObject.InvokeCollectionEvent(TestObject, collection0, collection1); + events_expected++; + + Assert.Equal(events_received, events_expected); + } + +#if NET + [WinRTExposedType(typeof(ManagedUriHandlerWinRTTypeDetails))] +#endif + class ManagedUriHandler : IUriHandler + { + public Class TestObject { get; private set; } + + public ManagedUriHandler(Class testObject) + { + TestObject = testObject; + } + + public void AddUriHandler(ProvideUri provideUri) + { + TestObject.CallForUri(provideUri); + Assert.Equal(new Uri("http://github.com"), TestObject.UriProperty); + } + } + +#if NET + internal sealed class ManagedUriHandlerWinRTTypeDetails : IWinRTExposedTypeDetails + { + public ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + { + return new ComWrappers.ComInterfaceEntry[] + { + new ComWrappers.ComInterfaceEntry + { + IID = typeof(IUriHandler).GUID, + Vtable = ABI.TestComponentCSharp.IUriHandlerMethods.AbiToProjectionVftablePtr + } + }; + } + } +#endif + + [Fact] + public void TestDelegateUnwrapping() + { + var obj = TestObject.GetUriDelegate(); + TestObject.CallForUri(obj); + Assert.Equal(new Uri("http://microsoft.com"), TestObject.UriProperty); + + TestObject.AddUriHandler(new ManagedUriHandler(TestObject)); + } + + // TODO: when the public WinUI nuget supports IXamlServiceProvider, just use the projection + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("68B3A2DF-8173-539F-B524-C8A2348F5AFB")] + internal unsafe interface IServiceProviderInterop + { + // Note: Invoking methods on ComInterfaceType.InterfaceIsIInspectable interfaces + // no longer appears supported in the runtime (probably with removal of WinRT support), + // so simulate with IUnknown. + void GetIids(out int iidCount, out IntPtr iids); + void GetRuntimeClassName(out IntPtr className); + void GetTrustLevel(out TrustLevel trustLevel); + + void GetService(IntPtr type, out IntPtr service); + } + + [Fact] + public void TestCustomProjections() + { + // INotifyDataErrorsInfo + string propertyName = ""; + TestObject.ErrorsChanged += (object sender, System.ComponentModel.DataErrorsChangedEventArgs e) => + { + propertyName = e.PropertyName; + }; + TestObject.RaiseDataErrorChanged(); + Assert.Equal("name", propertyName); + + bool eventCalled = false; + TestObject.CanExecuteChanged += (object sender, EventArgs e) => + { + eventCalled = true; + }; + + TestObject.RaiseCanExecuteChanged(); + Assert.True(eventCalled); + + // IXamlServiceProvider <-> IServiceProvider + var serviceProvider = Class.ServiceProvider.As(); + IntPtr service; + serviceProvider.GetService(IntPtr.Zero, out service); + Assert.Equal(new IntPtr(42), service); + + // Ensure robustness with bad runtime class names (parsing errors, type not found, etc) + var badRuntimeClassName = Class.BadRuntimeClassName; + Assert.NotNull(badRuntimeClassName); + } + + [Fact] + public void TestKeyValuePair() + { + var expected = new KeyValuePair("key", "value"); + TestObject.StringPairProperty = expected; + Assert.Equal(expected, TestObject.StringPairProperty); + + expected = new KeyValuePair("foo", "bar"); + TestObject.CallForStringPair(() => expected); + TestObject.StringPairPropertyChanged += + (object sender, KeyValuePair value) => Assert.Equal(expected, value); + TestObject.RaiseStringPairChanged(); + + var expected2 = new KeyValuePair(EnumValue.Two, new EnumStruct() { value = EnumValue.One }); + TestObject.EnumPairProperty = expected2; + Assert.Equal(expected2, TestObject.EnumPairProperty); + } + + [Fact] + public void TestObjectCasting() + { + object expected_uri = new Uri("http://aka.ms/cswinrt"); + TestObject.ObjectProperty = expected_uri; + Assert.Equal(expected_uri, TestObject.UriProperty); + Assert.Equal(expected_uri, TestObject.ObjectProperty); + + var expected = new KeyValuePair("key", "value"); + TestObject.ObjectProperty = expected; + var out_pair = (KeyValuePair)TestObject.ObjectProperty; + Assert.Equal(expected, out_pair); + + var nested = new KeyValuePair, KeyValuePair>( + new KeyValuePair(42, 1729), + new KeyValuePair("key", "value") + ); + TestObject.ObjectProperty = nested; + var out_nested = (KeyValuePair, KeyValuePair>)TestObject.ObjectProperty; + Assert.Equal(nested, out_nested); + + // Test projected types in generic of KeyValuePair + TestObject.ObjectProperty = new KeyValuePair("one", new Class()); + TestObject.ObjectProperty = new KeyValuePair("two", new Class()); + TestObject.ObjectProperty = new KeyValuePair("two", new CustomDisposableTest()); + + // Test non-projected types in generic of KeyValuePair + TestObject.ObjectProperty = new KeyValuePair("two", PlatformID.Win32NT); + TestObject.ObjectProperty = new KeyValuePair("two", new Random()); + + var strings_in = new[] { "hello", "world" }; + TestObject.StringsProperty = strings_in; + var strings_out = TestObject.StringsProperty; + Assert.True(strings_out.SequenceEqual(strings_in)); + + TestObject.ObjectProperty = strings_in; + strings_out = (string[])TestObject.ObjectProperty; + Assert.True(strings_out.SequenceEqual(strings_in)); + + var objects = new List() { new ManagedType(), new ManagedType() }; + var query = from item in objects select item; + TestObject.ObjectIterableProperty = query; + + TestObject.ObjectProperty = "test"; + Assert.Equal("test", TestObject.ObjectProperty); + + var objectArray = new ManagedType[] { new ManagedType(), new ManagedType() }; + TestObject.ObjectIterableProperty = objectArray; + Assert.True(TestObject.ObjectIterableProperty.SequenceEqual(objectArray)); + + var strArray = new string[] { "str1", "str2", "str3" }; + TestObject.ObjectIterableProperty = strArray; + Assert.True(TestObject.ObjectIterableProperty.SequenceEqual(strArray)); + + var uriArray = new Uri[] { new Uri("http://aka.ms/cswinrt"), new Uri("http://github.com") }; + TestObject.ObjectIterableProperty = uriArray; + Assert.True(TestObject.ObjectIterableProperty.SequenceEqual(uriArray)); + + var objectUriArray = new object[] { new Uri("http://github.com") }; + TestObject.ObjectIterableProperty = objectUriArray; + Assert.True(TestObject.ObjectIterableProperty.SequenceEqual(objectUriArray)); + } + + [Fact] + public void TestStringMap() + { + var map = new Dictionary { ["foo"] = "bar", ["hello"] = "world" }; + var stringMap = new Windows.Foundation.Collections.StringMap(); + foreach (var item in map) + { + stringMap[item.Key] = item.Value; + } + Assert.Equal(map.Count, stringMap.Count); + foreach (var item in map) + { + Assert.Equal(stringMap[item.Key], item.Value); + } + KeyValuePair[] pairs = new KeyValuePair[2]; + stringMap.CopyTo(pairs, 0); + Assert.Equal(2, pairs.Length); + } + + [Fact] + public void TestPropertySet() + { + var map = new Dictionary { ["foo"] = "bar", ["hello"] = "world" }; + var propertySet = new Windows.Foundation.Collections.PropertySet(); + foreach (var item in map) + { + propertySet[item.Key] = item.Value; + } + Assert.Equal(map.Count, propertySet.Count); + foreach (var item in map) + { + Assert.Equal(propertySet[item.Key], item.Value); + } + } + + [Fact] + public void TestValueSet() + { + var map = new Dictionary { ["foo"] = "bar", ["hello"] = "world" }; + var valueSet = new Windows.Foundation.Collections.ValueSet(); + foreach (var item in map) + { + valueSet[item.Key] = item.Value; + } + Assert.Equal(map.Count, valueSet.Count); + foreach (var item in map) + { + Assert.Equal(valueSet[item.Key], item.Value); + } + } + + [Fact] + public void TestValueSetArrays() + { + var map = new Dictionary + { + ["foo"] = new long[] { 1, 2, 3 }, + ["hello"] = new long[0], + ["world"] = new long[] { 1, 2, 3 }, + ["bar"] = new long[0] + }; + var valueSet = new Windows.Foundation.Collections.ValueSet(); + foreach (var item in map) + { + valueSet[item.Key] = item.Value; + } + Assert.Equal(map.Count, valueSet.Count); + foreach (var item in map) + { + Assert.Equal(valueSet[item.Key], item.Value); + } + } + + [Fact] + public void TestFactories() + { + var cls1 = new Class(); + + var cls2 = new Class(42); + Assert.Equal(42, cls2.IntProperty); + + var cls3 = new Class(42, "foo"); + Assert.Equal(42, cls3.IntProperty); + Assert.Equal("foo", cls3.StringProperty); + } + + [Fact] + public void TestFactoriesWithExplicitlyImplementedIUnknown() + { + var cls1 = new ClassWithExplicitIUnknown(); + Assert.Equal(0, cls1.Value); + cls1.Value = 42; + Assert.Equal(42, cls1.Value); + + var cls2 = new ClassWithExplicitIUnknown(42); + Assert.Equal(42, cls2.Value); + cls2.Value = 22; + Assert.Equal(22, cls2.Value); + } + + [Fact] + public void TestStaticMembers() + { + Class.StaticIntProperty = 42; + Assert.Equal(42, Class.StaticIntProperty); + + Class.StaticStringProperty = "foo"; + Assert.Equal("foo", Class.StaticStringProperty); + } + + [Fact] + public void TestInterfaces() + { + var expected = "hello"; + TestObject.StringProperty = expected; + + // projected wrapper + Assert.Equal(expected, TestObject.ToString()); + + // implicit cast + var str = (IStringable)TestObject; + Assert.Equal(expected, str.ToString()); + + var str2 = TestObject as IStringable; + Assert.Equal(expected, str2.ToString()); + + Assert.IsAssignableFrom(TestObject); + } + + [Fact] + public void TestAsync() + { + TestObject.IntProperty = 42; + var async_get_int = TestObject.GetIntAsync(); + int async_int = 0; + async_get_int.Completed = (info, status) => async_int = info.GetResults(); + async_get_int.GetResults(); + Assert.Equal(42, async_int); + + TestObject.StringProperty = "foo"; + var async_get_string = TestObject.GetStringAsync(); + string async_string = ""; + async_get_string.Completed = (info, status) => async_string = info.GetResults(); + int async_progress; + async_get_string.Progress = (info, progress) => async_progress = progress; + async_get_string.GetResults(); + Assert.Equal("foo", async_string); + } + + [Fact] + public void TestPrimitives() + { + var test_int = 21; + TestObject.IntPropertyChanged += (object sender, Int32 value) => + { + Assert.IsAssignableFrom(sender); + var c = (Class)sender; + Assert.Equal(value, test_int); + }; + TestObject.IntProperty = test_int; + + var expectedVal = true; + var hits = 0; + TestObject.BoolPropertyChanged += (object sender, bool value) => + { + Assert.Equal(expectedVal, value); + ++hits; + }; + + TestObject.BoolProperty = true; + Assert.Equal(1, hits); + + expectedVal = false; + TestObject.CallForBool(() => false); + Assert.Equal(2, hits); + + TestObject.RaiseBoolChanged(); + Assert.Equal(3, hits); + } + + [Fact] + public void TestStrings() + { + string test_string = "x"; + string test_string2 = "y"; + + // In hstring from managed->native implicitly creates hstring reference + TestObject.StringProperty = test_string; + + // Out hstring from native->managed only creates System.String on demand + var sp = TestObject.StringProperty; + Assert.Equal(sp, test_string); + + // Out hstring from managed->native always creates HString from System.String + TestObject.CallForString(() => test_string2); + Assert.Equal(TestObject.StringProperty, test_string2); + + // In hstring from native->managed only creates System.String on demand + TestObject.StringPropertyChanged += (Class sender, string value) => sender.StringProperty2 = value; + TestObject.RaiseStringChanged(); + Assert.Equal(TestObject.StringProperty2, test_string2); + } + + [Fact] + public void TestBlittableStruct() + { + // Property setter/getter + var val = new BlittableStruct() { i32 = 42 }; + TestObject.BlittableStructProperty = val; + Assert.Equal(42, TestObject.BlittableStructProperty.i32); + + // Manual getter + Assert.Equal(42, TestObject.GetBlittableStruct().i32); + + // Manual setter + val.i32 = 8; + TestObject.SetBlittableStruct(val); + Assert.Equal(8, TestObject.BlittableStructProperty.i32); + + // Output argument + val = default; + TestObject.OutBlittableStruct(out val); + Assert.Equal(8, val.i32); + } + + [Fact] + public void TestComposedBlittableStruct() + { + // Property setter/getter + var val = new ComposedBlittableStruct() { blittable = new BlittableStruct() { i32 = 42 } }; + TestObject.ComposedBlittableStructProperty = val; + Assert.Equal(42, TestObject.ComposedBlittableStructProperty.blittable.i32); + + // Manual getter + Assert.Equal(42, TestObject.GetComposedBlittableStruct().blittable.i32); + + // Manual setter + val.blittable.i32 = 8; + TestObject.SetComposedBlittableStruct(val); + Assert.Equal(8, TestObject.ComposedBlittableStructProperty.blittable.i32); + + // Output argument + val = default; + TestObject.OutComposedBlittableStruct(out val); + Assert.Equal(8, val.blittable.i32); + } + + [Fact] + public void TestNonBlittableStringStruct() + { + // Property getter/setter + var val = new NonBlittableStringStruct() { str = "I like tacos" }; + TestObject.NonBlittableStringStructProperty = val; + Assert.Equal("I like tacos", TestObject.NonBlittableStringStructProperty.str.ToString()); + + // Manual getter + Assert.Equal("I like tacos", TestObject.GetNonBlittableStringStruct().str.ToString()); + + // Manual setter + val.str = "Hello, world"; + TestObject.SetNonBlittableStringStruct(val); + Assert.Equal("Hello, world", TestObject.NonBlittableStringStructProperty.str.ToString()); + + // Output argument + val = default; + TestObject.OutNonBlittableStringStruct(out val); + Assert.Equal("Hello, world", val.str.ToString()); + } + + [Fact] + public void TestNonBlittableBoolStruct() + { + // Property getter/setter + var val = new NonBlittableBoolStruct() { w = true, x = false, y = true, z = false }; + TestObject.NonBlittableBoolStructProperty = val; + Assert.True(TestObject.NonBlittableBoolStructProperty.w); + Assert.False(TestObject.NonBlittableBoolStructProperty.x); + Assert.True(TestObject.NonBlittableBoolStructProperty.y); + Assert.False(TestObject.NonBlittableBoolStructProperty.z); + + // Manual getter + Assert.True(TestObject.GetNonBlittableBoolStruct().w); + Assert.False(TestObject.GetNonBlittableBoolStruct().x); + Assert.True(TestObject.GetNonBlittableBoolStruct().y); + Assert.False(TestObject.GetNonBlittableBoolStruct().z); + + // Manual setter + val.w = false; + val.x = true; + val.y = false; + val.z = true; + TestObject.SetNonBlittableBoolStruct(val); + Assert.False(TestObject.NonBlittableBoolStructProperty.w); + Assert.True(TestObject.NonBlittableBoolStructProperty.x); + Assert.False(TestObject.NonBlittableBoolStructProperty.y); + Assert.True(TestObject.NonBlittableBoolStructProperty.z); + + // Output argument + val = default; + TestObject.OutNonBlittableBoolStruct(out val); + Assert.False(val.w); + Assert.True(val.x); + Assert.False(val.y); + Assert.True(val.z); + } + + [Fact] + public void TestNonBlittableRefStruct() + { + // Property getter/setter + // TODO: Need to either support interface inheritance or project IReference/INullable for setter + Assert.Equal(42, TestObject.NonBlittableRefStructProperty.ref32.Value); + + // Manual getter + Assert.Equal(42, TestObject.GetNonBlittableRefStruct().ref32.Value); + + // TODO: Manual setter + + // Output argument + NonBlittableRefStruct val; + TestObject.OutNonBlittableRefStruct(out val); + Assert.Equal(42, val.ref32.Value); + } + + [Fact] + public void TestComposedNonBlittableStruct() + { + // Property getter/setter + var val = new ComposedNonBlittableStruct() + { + blittable = new BlittableStruct() { i32 = 42 }, + strings = new NonBlittableStringStruct() { str = "I like tacos" }, + bools = new NonBlittableBoolStruct() { w = true, x = false, y = true, z = false }, + refs = TestObject.NonBlittableRefStructProperty // TODO: Need to either support interface inheritance or project IReference/INullable for setter + }; + TestObject.ComposedNonBlittableStructProperty = val; + Assert.Equal(42, TestObject.ComposedNonBlittableStructProperty.blittable.i32); + Assert.Equal("I like tacos", TestObject.ComposedNonBlittableStructProperty.strings.str); + Assert.True(TestObject.ComposedNonBlittableStructProperty.bools.w); + Assert.False(TestObject.ComposedNonBlittableStructProperty.bools.x); + Assert.True(TestObject.ComposedNonBlittableStructProperty.bools.y); + Assert.False(TestObject.ComposedNonBlittableStructProperty.bools.z); + + // Manual getter + Assert.Equal(42, TestObject.GetComposedNonBlittableStruct().blittable.i32); + Assert.Equal("I like tacos", TestObject.GetComposedNonBlittableStruct().strings.str); + Assert.True(TestObject.GetComposedNonBlittableStruct().bools.w); + Assert.False(TestObject.GetComposedNonBlittableStruct().bools.x); + Assert.True(TestObject.GetComposedNonBlittableStruct().bools.y); + Assert.False(TestObject.GetComposedNonBlittableStruct().bools.z); + + // Manual setter + val.blittable.i32 = 8; + val.strings.str = "Hello, world"; + val.bools.w = false; + val.bools.x = true; + val.bools.y = false; + val.bools.z = true; + TestObject.SetComposedNonBlittableStruct(val); + Assert.Equal(8, TestObject.ComposedNonBlittableStructProperty.blittable.i32); + Assert.Equal("Hello, world", TestObject.ComposedNonBlittableStructProperty.strings.str); + Assert.False(TestObject.ComposedNonBlittableStructProperty.bools.w); + Assert.True(TestObject.ComposedNonBlittableStructProperty.bools.x); + Assert.False(TestObject.ComposedNonBlittableStructProperty.bools.y); + Assert.True(TestObject.ComposedNonBlittableStructProperty.bools.z); + + // Output argument + val = default; + TestObject.OutComposedNonBlittableStruct(out val); + Assert.Equal(8, val.blittable.i32); + Assert.Equal("Hello, world", val.strings.str); + Assert.False(val.bools.w); + Assert.True(val.bools.x); + Assert.False(val.bools.y); + Assert.True(val.bools.z); + } + + [Fact] + public void TestBlittableArrays() + { + int[] arr = new[] { 2, 4, 6, 8 }; + TestObject.SetInts(arr); + Assert.True(TestObject.GetInts().SequenceEqual(arr)); + + TestObject.SetInts(null); + Assert.Null(TestObject.GetInts()); + } + +#if !NET + [Fact] + public void TestGenericCast() + { + var ints = TestObject.GetIntVector(); + var abiView = (ABI.System.Collections.Generic.IReadOnlyList)ints; + Assert.Equal(abiView.ThisPtr, abiView.As().As.Vftbl>().ThisPtr); + } +#endif + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("96369F54-8EB6-48F0-ABCE-C1B211E627C3")] + internal unsafe interface IStringableInterop + { + // Note: Invoking methods on ComInterfaceType.InterfaceIsIInspectable interfaces + // no longer appears supported in the runtime (probably with removal of WinRT support), + // so simulate with IUnknown. + void GetIids(out int iidCount, out IntPtr iids); + void GetRuntimeClassName(out IntPtr className); + void GetTrustLevel(out TrustLevel trustLevel); + + void ToString(out IntPtr hstr); + } + + [Fact] + public unsafe void TestFactoryCast() + { + IntPtr hstr; + + // Access nonstatic class factory + var instanceFactory = Class.As(); + instanceFactory.ToString(out hstr); + Assert.Equal("Class", MarshalString.FromAbi(hstr)); + + // Access static class factory + var staticFactory = ComImports.As(); + staticFactory.ToString(out hstr); + Assert.Equal("ComImports", MarshalString.FromAbi(hstr)); + } + +#if !NET47 + [Fact] + public unsafe void TestMarshalString_FromAbiUnsafe() + { + // The span must be empty and point to a null-terminated buffer (HSTRING-s are null-terminated too) + var span = MarshalString.FromAbiUnsafe(IntPtr.Zero); + Assert.Equal(0, span.Length); + Assert.True(MemoryMarshal.GetReference(span) == '\0'); + + // Same thing but with round-tripping from a null string + var hstr = MarshalString.FromManaged(null); + span = MarshalString.FromAbiUnsafe(hstr); + Assert.Equal(0, span.Length); + Assert.True(MemoryMarshal.GetReference(span) == '\0'); + MarshalString.DisposeAbi(hstr); + + // Same thing but with an empty string (equivalent to null) + hstr = MarshalString.FromManaged(""); + span = MarshalString.FromAbiUnsafe(hstr); + Assert.Equal(0, span.Length); + Assert.True(MemoryMarshal.GetReference(span) == '\0'); + MarshalString.DisposeAbi(hstr); + + // Marshal from some non-null, non-empty string. We want to check that both the span has the expected content, + // but also that it's correctly null-terminated (outside of its bounds). This is always safe to access, like + // before, because the memory should point to the HSTRING buffer, which is always null-terminated as well. + hstr = MarshalString.FromManaged(nameof(TestMarshalString_FromAbiUnsafe)); + span = MarshalString.FromAbiUnsafe(hstr); + Assert.True(span.SequenceEqual(nameof(TestMarshalString_FromAbiUnsafe))); + Assert.True(Unsafe.Add(ref MemoryMarshal.GetReference(span), span.Length) == '\0'); + MarshalString.DisposeAbi(hstr); + } +#endif + + [Fact] + public void TestFundamentalGeneric() + { + var ints = TestObject.GetIntVector(); + Assert.Equal(10, ints.Count); + for (int i = 0; i < 10; ++i) + { + Assert.Equal(i, ints[i]); + } + + var bools = TestObject.GetBoolVector(); + Assert.Equal(4, bools.Count); + for (int i = 0; i < 4; ++i) + { + Assert.Equal(i % 2 == 0, bools[i]); + } + } + + [Fact] + public void TestStringGeneric() + { + var strings = TestObject.GetStringVector(); + Assert.Equal(5, strings.Count); + for (int i = 0; i < 5; ++i) + { + Assert.Equal("String" + i, strings[i]); + } + } + + [Fact] + public void TestStructGeneric() + { + var blittable = TestObject.GetBlittableStructVector(); + Assert.Equal(5, blittable.Count); + for (int i = 0; i < 5; ++i) + { + Assert.Equal(i, blittable[i].blittable.i32); + } + + var nonblittable = TestObject.GetNonBlittableStructVector(); + Assert.Equal(3, nonblittable.Count); + for (int i = 0; i < 3; ++i) + { + var val = nonblittable[i]; + Assert.Equal(i, val.blittable.i32); + Assert.Equal("String" + i, val.strings.str); + Assert.Equal(i % 2 == 0, val.bools.w); + Assert.Equal(i % 2 == 1, val.bools.x); + Assert.Equal(i % 2 == 0, val.bools.y); + Assert.Equal(i % 2 == 1, val.bools.z); + Assert.Equal(i, val.refs.ref32.Value); + } + } + + [Fact] + public void TestValueUnboxing() + { + var objs = TestObject.GetObjectVector(); + Assert.Equal(3, objs.Count); + for (int i = 0; i < 3; ++i) + { + Assert.Equal(i, (int)objs[i]); + } + } + + [Fact] + void TestInterfaceGeneric() + { + var objs = TestObject.GetInterfaceVector(); + Assert.Equal(3, objs.Count); + TestObject.ReadWriteProperty = 42; + for (int i = 0; i < 3; ++i) + { + var obj = objs[i]; + Assert.Same(obj, TestObject); + Assert.Equal(42, obj.ReadWriteProperty); + } + } + + [Fact] + public void TestIterable() + { + var ints_in = new int[] { 0, 1, 2 }; + TestObject.SetIntIterable(ints_in); + var ints_out = TestObject.GetIntIterable(); + Assert.True(ints_in.SequenceEqual(ints_out)); + } + + class ManagedBindableObservable : IBindableObservableVector + { + private IList _list; + + public class TObservation : IProperties2 + { + private int _value = 0; + + public int ReadWriteProperty { get => _value; set => _value = value; } + + int IProperties1.ReadWriteProperty => ReadWriteProperty; + } + TObservation _observation; + + public int Observation { get => _observation.ReadWriteProperty; } + + public ManagedBindableObservable(IList list) => _list = new ArrayList(list); + + private void OnChanged() + { + VectorChanged?.Invoke(this, _observation = new TObservation()); + } + + public event BindableVectorChangedEventHandler VectorChanged; + + public object this[int index] + { + get => _list[index]; + set { _list[index] = value; OnChanged(); } + } + + public bool IsFixedSize => false; + + public bool IsReadOnly => false; + + public int Count => _list.Count; + + public bool IsSynchronized => _list.IsSynchronized; + + public object SyncRoot => _list; + + public int Add(object value) + { + var result = _list.Add(value); + OnChanged(); + return result; + } + + public void Clear() + { + _list.Clear(); + OnChanged(); + } + + public bool Contains(object value) => _list.Contains(value); + + public void CopyTo(Array array, int index) => _list.CopyTo(array, index); + + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + + public int IndexOf(object value) => _list.IndexOf(value); + + public void Insert(int index, object value) + { + _list.Insert(index, value); + OnChanged(); + } + + public void Remove(object value) + { + _list.Remove(value); + OnChanged(); + } + + public void RemoveAt(int index) + { + _list.RemoveAt(index); + OnChanged(); + } + } + + [Fact] + public void TestBindable() + { + var expected = new int[] { 0, 1, 2 }; + + TestObject.BindableIterableProperty = expected; + Assert.Equal(expected, TestObject.BindableIterableProperty); + TestObject.CallForBindableIterable(() => expected); + TestObject.BindableIterablePropertyChanged += + (object sender, IEnumerable value) => Assert.Equal(expected, value); + TestObject.RaiseBindableIterableChanged(); + + TestObject.BindableVectorProperty = expected; + Assert.Equal(expected, TestObject.BindableVectorProperty); + TestObject.CallForBindableVector(() => expected); + TestObject.BindableVectorPropertyChanged += + (object sender, IList value) => Assert.Equal(expected, value); + TestObject.RaiseBindableVectorChanged(); + + var observable = new ManagedBindableObservable(expected); + TestObject.BindableObservableVectorProperty = observable; + observable.Add(3); + Assert.Equal(6, observable.Observation); + } + + [Fact] + public void TestClassGeneric() + { + var objs = TestObject.GetClassVector(); + Assert.Equal(3, objs.Count); + for (int i = 0; i < 3; ++i) + { + var obj = objs[i]; + Assert.Same(obj, TestObject); + Assert.Equal(TestObject, objs[i]); + } + } + + [Fact] + public void TestSimpleCCWs() + { + var managedProperties = new ManagedProperties(42); + TestObject.CopyProperties(managedProperties); + Assert.Equal(managedProperties.ReadWriteProperty, TestObject.ReadWriteProperty); + } + + [Fact] + public void TestCCWMarshaler() + { + Guid IID_IMarshal = new Guid("00000003-0000-0000-c000-000000000046"); + var managedProperties = new ManagedProperties(42); + IObjectReference ccw = MarshalInterface.CreateMarshaler(managedProperties); + ccw.TryAs(IID_IMarshal, out var marshalCCW); + Assert.NotNull(marshalCCW); + + var array = new byte[] { 0x01 }; + var buff = array.AsBuffer(); + IObjectReference ccw2 = MarshalInterface.CreateMarshaler(buff); + ccw2.TryAs(IID_IMarshal, out var marshalCCW2); + Assert.NotNull(marshalCCW2); + } + +#if NET + [Fact] + public void TestDelegateCCWMarshaler() + { + CreateAndValidateStreamedFile().Wait(); + } + + private async Task CreateAndValidateStreamedFile() + { + var storageFile = await StorageFile.CreateStreamedFileAsync("CreateAndValidateStreamedFile.txt", StreamedFileWriter, null); + using var inputStream = await storageFile.OpenSequentialReadAsync(); + using var stream = inputStream.AsStreamForRead(); + byte[] buff = new byte[50]; + var numRead = stream.Read(buff, 0, 50); + Assert.True(numRead > 0); + var result = System.Text.Encoding.Default.GetString(buff, 0, numRead).TrimEnd(null); + Assert.Equal("Success!", result); + } + + private static async void StreamedFileWriter(StreamedFileDataRequest request) + { + try + { + using (var stream = request.AsStreamForWrite()) + using (var streamWriter = new StreamWriter(stream)) + { + await streamWriter.WriteLineAsync("Success!"); + } + request.Dispose(); + } + catch (Exception) + { + request.FailAndClose(StreamedFileFailureMode.Incomplete); + } + } +#endif + + [Fact] + public void TestWeakReference() + { + var managedProperties = new ManagedProperties(42); + TestObject.CopyPropertiesViaWeakReference(managedProperties); + Assert.Equal(managedProperties.ReadWriteProperty, TestObject.ReadWriteProperty); + } + + [Fact] + public void TestCCWIdentity() + { + var managedProperties = new ManagedProperties(42); + IObjectReference ccw1 = MarshalInterface.CreateMarshaler(managedProperties); + IObjectReference ccw2 = MarshalInterface.CreateMarshaler(managedProperties); + Assert.Equal(ccw1.ThisPtr, ccw2.ThisPtr); + } + + [Fact] + public void TestInterfaceCCWLifetime() + { + static (WeakReference, IObjectReference) CreateCCW() + { + var managedProperties = new ManagedProperties(42); + IObjectReference ccw1 = MarshalInterface.CreateMarshaler(managedProperties); + return (new WeakReference(managedProperties), ccw1); + } + + static (WeakReference obj, WeakReference ccw) GetWeakReferenceToObjectAndCCW() + { + var (reference, ccw) = CreateCCW(); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + + Assert.True(reference.IsAlive); + return (reference, new WeakReference(ccw)); + } + + var (obj, ccw) = GetWeakReferenceToObjectAndCCW(); + + while (ccw.IsAlive) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + + // Now that the CCW is dead, we should have no references to the managed object. + // Run GC one more time to collect the managed object. + GC.Collect(); + GC.WaitForPendingFinalizers(); + Assert.False(obj.IsAlive); + } + + [Fact] + public void TestDelegateCCWLifetime() + { + static (WeakReference, IObjectReference) CreateCCW(Action action) + { + TypedEventHandler eventHandler = (o, i) => action(o, i); + IObjectReference ccw1 = ABI.Windows.Foundation.TypedEventHandler.CreateMarshaler(eventHandler); + return (new WeakReference(eventHandler), ccw1); + } + + static (WeakReference obj, WeakReference ccw) GetWeakReferenceToObjectAndCCW(Action action) + { + var (reference, ccw) = CreateCCW(action); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + + Assert.True(reference.IsAlive); + return (reference, new WeakReference(ccw)); + } + + var (obj, ccw) = GetWeakReferenceToObjectAndCCW((o, i) => { }); + + while (ccw.IsAlive) + { + GC.Collect(); + GC.WaitForPendingFinalizers(); + } + + // Now that the CCW is dead, we should have no references to the managed object. + // Run GC one more time to collect the managed object. + GC.Collect(); + GC.WaitForPendingFinalizers(); + Assert.False(obj.IsAlive); + } + + [Fact] + public void TestCCWIdentityThroughRefCountZero() + { + static (WeakReference, IntPtr) CreateCCWReference(IProperties1 properties) + { + IObjectReference ccw = MarshalInterface.CreateMarshaler(properties); + return (new WeakReference(ccw), ccw.ThisPtr); + } + + var obj = new ManagedProperties(42); + + var (ccwWeakReference, ptr) = CreateCCWReference(obj); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + + Assert.False(ccwWeakReference.IsAlive); + + var (_, ptr2) = CreateCCWReference(obj); + + Assert.Equal(ptr, ptr2); + } + + [Fact()] + public void TestExceptionPropagation_Managed() + { + var exceptionToThrow = new ArgumentNullException("foo"); + var properties = new ThrowingManagedProperties(exceptionToThrow); + Assert.Throws("foo", () => TestObject.CopyProperties(properties)); + + var properties2 = new ThrowingManagedProperties2(TestObject); + Assert.Throws("foo", () => TestObject.CopyProperties(properties2)); + } + + [Fact] + public void TestExceptionPropagation() + { + VerifyException(() => TestObject.ThrowExceptionWithMessage("Parameter1", false), "Parameter1"); + VerifyException(() => TestObject.ThrowExceptionWithMessage("Error message", true), "Error message"); + + void callProperties() + { + var properties = new ThrowingManagedProperties(TestObject); + TestObject.CopyProperties(properties); + } + VerifyException(callProperties, "Property threw"); + + VerifyException(() => TestObject.OriginateAndThrowExceptionWithMessage("Parameter3"), "Parameter3"); + + void callProperties2() + { + var properties = new ThrowingManagedProperties(TestObject, true); + TestObject.CopyProperties(properties); + } + VerifyException(callProperties2, "Property threw with language exception"); + + static void VerifyException(Action action, string expectedMessage) where T : Exception + { + try + { + action(); + Assert.True(false); + } + catch (T ex) + { + Assert.Contains(expectedMessage, ex.Message); + } + catch (Exception) + { + Assert.True(false); + } + } + } + + class ManagedProperties : IProperties1 + { + private readonly int _value; + + public ManagedProperties(int value) + { + _value = value; + } + public int ReadWriteProperty => _value; + } + + class ThrowingManagedProperties : IProperties1 + { + public ThrowingManagedProperties(Exception exceptionToThrow) + { + ExceptionToThrow = exceptionToThrow; + } + + public ThrowingManagedProperties(Class instance, bool includeLanguageException = false) + { + Instance = instance; + IncludeLanguageException = includeLanguageException; + } + + public Exception ExceptionToThrow { get; } + + public Class Instance { get; } + + public bool IncludeLanguageException { get; } + + public int ReadWriteProperty + { + get + { + if (Instance is not null) + { + if (IncludeLanguageException) + { + return Instance.OriginateAndThrowExceptionWithMessage("Property threw with language exception").Length; + } + else + { + return Instance.ThrowExceptionWithMessage("Property threw", false).Length; + } + } + else + { + throw ExceptionToThrow; + } + } + } + } + + class ThrowingManagedProperties2 : IProperties1 + { + public ThrowingManagedProperties2(Class instance) + { + Instance = instance; + } + + public Class Instance { get; } + + public int ReadWriteProperty + { + get + { + var exceptionToThrow = new ArgumentNullException("foo"); + var properties = new ThrowingManagedProperties(exceptionToThrow); + Instance.CopyProperties(properties); + return 1; + } + } + } + + + readonly int E_FAIL = -2147467259; + + async Task InvokeDoitAsync() + { + await TestObject.DoitAsync(); + } + + [Fact] + public void TestAsyncAction() + { + var task = InvokeDoitAsync(); + Assert.False(task.Wait(25)); + TestObject.CompleteAsync(); + Assert.True(task.Wait(5000)); + Assert.Equal(TaskStatus.RanToCompletion, task.Status); + + task = InvokeDoitAsync(); + Assert.False(task.Wait(25)); + TestObject.CompleteAsync(E_FAIL); + var e = Assert.Throws(() => task.Wait(5000)); + Assert.Equal(E_FAIL, e.InnerException.HResult); + Assert.Equal(TaskStatus.Faulted, task.Status); + + var src = new CancellationTokenSource(); + task = TestObject.DoitAsync().AsTask(src.Token); + Assert.False(task.Wait(25)); + src.Cancel(); + e = Assert.Throws(() => task.Wait(5000)); + Assert.True(e.InnerException is TaskCanceledException); + Assert.Equal(TaskStatus.Canceled, task.Status); + } + + [Fact] + public void TestAsyncActionWait() + { + var asyncAction = TestObject.DoitAsync(); + TestObject.CompleteAsync(); + asyncAction.Wait(); + Assert.Equal(AsyncStatus.Completed, asyncAction.Status); + + asyncAction = TestObject.DoitAsync(); + TestObject.CompleteAsync(E_FAIL); + var e = Assert.Throws(() => asyncAction.Wait()); + Assert.Equal(E_FAIL, e.InnerException.HResult); + Assert.Equal(AsyncStatus.Error, asyncAction.Status); + + asyncAction = TestObject.DoitAsync(); + asyncAction.Cancel(); + e = Assert.Throws(() => asyncAction.Wait()); + Assert.True(e.InnerException is TaskCanceledException); + Assert.Equal(AsyncStatus.Canceled, asyncAction.Status); + } + + [Fact] + public void TestAsyncActionRoundTrip() + { + var task = InvokeDoitAsync().AsAsyncAction().AsTask(); + Assert.False(task.Wait(25)); + TestObject.CompleteAsync(); + Assert.True(task.Wait(5000)); + Assert.Equal(TaskStatus.RanToCompletion, task.Status); + + task = InvokeDoitAsync().AsAsyncAction().AsTask(); + Assert.False(task.Wait(25)); + TestObject.CompleteAsync(E_FAIL); + var e = Assert.Throws(() => task.Wait(5000)); + Assert.Equal(E_FAIL, e.InnerException.HResult); + Assert.Equal(TaskStatus.Faulted, task.Status); + + var src = new CancellationTokenSource(); + task = InvokeDoitAsync().AsAsyncAction().AsTask(src.Token); + Assert.False(task.Wait(25)); + src.Cancel(); + e = Assert.Throws(() => task.Wait(5000)); + Assert.True(e.InnerException is TaskCanceledException); + Assert.Equal(TaskStatus.Canceled, task.Status); + } + + async Task InvokeDoitAsyncWithProgress() + { + await TestObject.DoitAsyncWithProgress(); + } + + [Fact] + public void TestAsyncActionWithProgress() + { + int progress = 0; + var evt = new AutoResetEvent(false); + var task = TestObject.DoitAsyncWithProgress().AsTask(new Progress((v) => + { + progress = v; + evt.Set(); + })); + + for (int i = 1; i <= 10; ++i) + { + TestObject.AdvanceAsync(10); + Assert.True(evt.WaitOne(5000)); + Assert.Equal(10 * i, progress); + } + + TestObject.CompleteAsync(); + Assert.True(task.Wait(5000)); + Assert.Equal(TaskStatus.RanToCompletion, task.Status); + + task = InvokeDoitAsyncWithProgress(); + TestObject.CompleteAsync(E_FAIL); + var e = Assert.Throws(() => task.Wait(5000)); + Assert.Equal(E_FAIL, e.InnerException.HResult); + Assert.Equal(TaskStatus.Faulted, task.Status); + + var src = new CancellationTokenSource(); + task = TestObject.DoitAsyncWithProgress().AsTask(src.Token); + Assert.False(task.Wait(25)); + src.Cancel(); + e = Assert.Throws(() => task.Wait(5000)); + Assert.True(e.InnerException is TaskCanceledException); + Assert.Equal(TaskStatus.Canceled, task.Status); + } + + [Fact] + public void TestAsyncActionWithProgressWait() + { + var asyncAction = TestObject.DoitAsyncWithProgress(); + TestObject.CompleteAsync(); + asyncAction.Wait(); + Assert.Equal(AsyncStatus.Completed, asyncAction.Status); + + asyncAction = TestObject.DoitAsyncWithProgress(); + TestObject.CompleteAsync(E_FAIL); + var e = Assert.Throws(() => asyncAction.Wait()); + Assert.Equal(E_FAIL, e.InnerException.HResult); + Assert.Equal(AsyncStatus.Error, asyncAction.Status); + + asyncAction = TestObject.DoitAsyncWithProgress(); + asyncAction.Cancel(); + e = Assert.Throws(() => asyncAction.Wait()); + Assert.True(e.InnerException is TaskCanceledException); + Assert.Equal(AsyncStatus.Canceled, asyncAction.Status); + } + + async Task InvokeAddAsync(int lhs, int rhs) + { + return await TestObject.AddAsync(lhs, rhs); + } + + [Fact] + public void TestAsyncOperation() + { + var task = InvokeAddAsync(42, 8); + Assert.False(task.Wait(25)); + TestObject.CompleteAsync(); + Assert.True(task.Wait(10000)); + Assert.Equal(TaskStatus.RanToCompletion, task.Status); + Assert.Equal(50, task.Result); + + task = InvokeAddAsync(0, 0); + Assert.False(task.Wait(25)); + TestObject.CompleteAsync(E_FAIL); + var e = Assert.Throws(() => task.Wait(5000)); + Assert.Equal(E_FAIL, e.InnerException.HResult); + Assert.Equal(TaskStatus.Faulted, task.Status); + + var src = new CancellationTokenSource(); + task = TestObject.AddAsync(0, 0).AsTask(src.Token); + Assert.False(task.Wait(25)); + src.Cancel(); + e = Assert.Throws(() => task.Wait(5000)); + Assert.True(e.InnerException is TaskCanceledException); + Assert.Equal(TaskStatus.Canceled, task.Status); + } + + [Fact] + public void TestAsyncOperationWait() + { + var asyncOperation = TestObject.AddAsync(42, 8); + TestObject.CompleteAsync(); + asyncOperation.Wait(); + Assert.Equal(AsyncStatus.Completed, asyncOperation.Status); + + asyncOperation = TestObject.AddAsync(42, 8); + TestObject.CompleteAsync(E_FAIL); + var e = Assert.Throws(() => asyncOperation.Wait()); + Assert.Equal(E_FAIL, e.InnerException.HResult); + Assert.Equal(AsyncStatus.Error, asyncOperation.Status); + + asyncOperation = TestObject.AddAsync(42, 8); + asyncOperation.Cancel(); + e = Assert.Throws(() => asyncOperation.Wait()); + Assert.True(e.InnerException is TaskCanceledException); + Assert.Equal(AsyncStatus.Canceled, asyncOperation.Status); + } + + + [Fact] + public void TestAsyncOperationRoundTrip() + { + var task = InvokeAddAsync(42, 8).AsAsyncOperation().AsTask(); + Assert.False(task.Wait(25)); + TestObject.CompleteAsync(); + Assert.True(task.Wait(5000)); + Assert.Equal(TaskStatus.RanToCompletion, task.Status); + Assert.Equal(50, task.Result); + + task = InvokeAddAsync(0, 0).AsAsyncOperation().AsTask(); + Assert.False(task.Wait(25)); + TestObject.CompleteAsync(E_FAIL); + var e = Assert.Throws(() => task.Wait(5000)); + Assert.Equal(E_FAIL, e.InnerException.HResult); + Assert.Equal(TaskStatus.Faulted, task.Status); + + var src = new CancellationTokenSource(); + task = InvokeAddAsync(0, 0).AsAsyncOperation().AsTask(src.Token); + Assert.False(task.Wait(25)); + src.Cancel(); + e = Assert.Throws(() => task.Wait(5000)); + Assert.True(e.InnerException is TaskCanceledException); + Assert.Equal(TaskStatus.Canceled, task.Status); + } + + async Task InvokeAddAsyncWithProgress(int lhs, int rhs) + { + return await TestObject.AddAsyncWithProgress(lhs, rhs); + } + + [Fact] + public void TestAsyncOperationWithProgress() + { + int progress = 0; + var evt = new AutoResetEvent(false); + var task = TestObject.AddAsyncWithProgress(42, 8).AsTask(new Progress((v) => + { + progress = v; + evt.Set(); + })); + + for (int i = 1; i <= 10; ++i) + { + TestObject.AdvanceAsync(10); + Assert.True(evt.WaitOne(5000)); + Assert.Equal(10 * i, progress); + } + + TestObject.CompleteAsync(); + Assert.True(task.Wait(5000)); + Assert.Equal(TaskStatus.RanToCompletion, task.Status); + Assert.Equal(50, task.Result); + + task = InvokeAddAsyncWithProgress(0, 0); + TestObject.CompleteAsync(E_FAIL); + var e = Assert.Throws(() => task.Wait(5000)); + Assert.Equal(E_FAIL, e.InnerException.HResult); + Assert.Equal(TaskStatus.Faulted, task.Status); + + var src = new CancellationTokenSource(); + task = TestObject.AddAsyncWithProgress(0, 0).AsTask(src.Token); + Assert.False(task.Wait(25)); + src.Cancel(); + e = Assert.Throws(() => task.Wait(5000)); + Assert.True(e.InnerException is TaskCanceledException); + Assert.Equal(TaskStatus.Canceled, task.Status); + } + + [Fact] + public void TestAsyncOperationWithProgressWait() + { + var asyncOperation = TestObject.AddAsyncWithProgress(42, 8); + TestObject.CompleteAsync(); + asyncOperation.Wait(); + Assert.Equal(AsyncStatus.Completed, asyncOperation.Status); + + asyncOperation = TestObject.AddAsyncWithProgress(42, 8); + TestObject.CompleteAsync(E_FAIL); + var e = Assert.Throws(() => asyncOperation.Wait()); + Assert.Equal(E_FAIL, e.InnerException.HResult); + Assert.Equal(AsyncStatus.Error, asyncOperation.Status); + + asyncOperation = TestObject.AddAsyncWithProgress(42, 8); + asyncOperation.Cancel(); + e = Assert.Throws(() => asyncOperation.Wait()); + Assert.True(e.InnerException is TaskCanceledException); + Assert.Equal(AsyncStatus.Canceled, asyncOperation.Status); + } + + [Fact] + public void TestPointTypeMapping() + { + var pt = new Point { X = 3.14, Y = 42 }; + TestObject.PointProperty = pt; + Assert.Equal(pt.X, TestObject.PointProperty.X); + Assert.Equal(pt.Y, TestObject.PointProperty.Y); + Assert.True(TestObject.PointProperty == pt); + Assert.Equal(pt, TestObject.GetPointReference().Value); + + var vector2 = TestObject.PointProperty.ToVector2(); + Assert.Equal(pt.X, vector2.X); + Assert.Equal(pt.Y, vector2.Y); + + TestObject.PointProperty = vector2.ToPoint(); + Assert.Equal(pt.X, TestObject.PointProperty.X); + Assert.Equal(pt.Y, TestObject.PointProperty.Y); + } + + [Fact] + public void TestRectTypeMapping() + { + var rect = new Rect { X = 3.14, Y = 42, Height = 3.14, Width = 42 }; + TestObject.RectProperty = rect; + Assert.Equal(rect.X, TestObject.RectProperty.X); + Assert.Equal(rect.Y, TestObject.RectProperty.Y); + Assert.Equal(rect.Height, TestObject.RectProperty.Height); + Assert.Equal(rect.Width, TestObject.RectProperty.Width); + Assert.True(TestObject.RectProperty == rect); + } + + [Fact] + public void TestSizeTypeMapping() + { + var size = new Size { Height = 3.14, Width = 42 }; + TestObject.SizeProperty = size; + Assert.Equal(size.Height, TestObject.SizeProperty.Height); + Assert.Equal(size.Width, TestObject.SizeProperty.Width); + Assert.True(TestObject.SizeProperty == size); + + var vector2 = TestObject.SizeProperty.ToVector2(); + Assert.Equal(size.Width, vector2.X); + Assert.Equal(size.Height, vector2.Y); + + TestObject.SizeProperty = vector2.ToSize(); + Assert.Equal(size.Width, TestObject.SizeProperty.Width); + Assert.Equal(size.Height, TestObject.SizeProperty.Height); + } + + [Fact] + public void TestColorTypeMapping() + { + var color = new Color { A = 0x20, R = 0x40, G = 0x60, B = 0x80 }; + TestObject.ColorProperty = color; + Assert.Equal(color.A, TestObject.ColorProperty.A); + Assert.Equal(color.R, TestObject.ColorProperty.R); + Assert.Equal(color.G, TestObject.ColorProperty.G); + Assert.Equal(color.B, TestObject.ColorProperty.B); + Assert.True(TestObject.ColorProperty == color); + } + + [Fact] + public void TestCornerRadiusTypeMapping() + { + var cornerRadius = new CornerRadius { TopLeft = 1, TopRight = 2, BottomRight = 3, BottomLeft = 4 }; + TestObject.CornerRadiusProperty = cornerRadius; + Assert.Equal(cornerRadius.TopLeft, TestObject.CornerRadiusProperty.TopLeft); + Assert.Equal(cornerRadius.TopRight, TestObject.CornerRadiusProperty.TopRight); + Assert.Equal(cornerRadius.BottomRight, TestObject.CornerRadiusProperty.BottomRight); + Assert.Equal(cornerRadius.BottomLeft, TestObject.CornerRadiusProperty.BottomLeft); + Assert.True(TestObject.CornerRadiusProperty == cornerRadius); + } + + [Fact] + public void TestDurationTypeMapping() + { + var duration = new Duration(TimeSpan.FromTicks(42)); + TestObject.DurationProperty = duration; + Assert.Equal(duration.TimeSpan, TestObject.DurationProperty.TimeSpan); + Assert.True(TestObject.DurationProperty == duration); + } + + [Fact] + public void TestGridLengthTypeMapping() + { + var gridLength = new GridLength(42, GridUnitType.Pixel); + TestObject.GridLengthProperty = gridLength; + Assert.Equal(gridLength.GridUnitType, TestObject.GridLengthProperty.GridUnitType); + Assert.Equal(gridLength.Value, TestObject.GridLengthProperty.Value); + Assert.True(TestObject.GridLengthProperty == gridLength); + } + + [Fact] + public void TestThicknessTypeMapping() + { + var thickness = new Thickness { Left = 1, Top = 2, Right = 3, Bottom = 4 }; + TestObject.ThicknessProperty = thickness; + Assert.Equal(thickness.Left, TestObject.ThicknessProperty.Left); + Assert.Equal(thickness.Top, TestObject.ThicknessProperty.Top); + Assert.Equal(thickness.Right, TestObject.ThicknessProperty.Right); + Assert.Equal(thickness.Bottom, TestObject.ThicknessProperty.Bottom); + Assert.True(TestObject.ThicknessProperty == thickness); + } + + [Fact] + public void TestGeneratorPositionTypeMapping() + { + var generatorPosition = new GeneratorPosition { Index = 1, Offset = 2 }; + TestObject.GeneratorPositionProperty = generatorPosition; + Assert.Equal(generatorPosition.Index, TestObject.GeneratorPositionProperty.Index); + Assert.Equal(generatorPosition.Offset, TestObject.GeneratorPositionProperty.Offset); + Assert.True(TestObject.GeneratorPositionProperty == generatorPosition); + } + + [Fact] + public void TestMatrixTypeMapping() + { + var matrix = new Matrix { M11 = 11, M12 = 12, M21 = 21, M22 = 22, OffsetX = 3, OffsetY = 4 }; + TestObject.MatrixProperty = matrix; + Assert.Equal(matrix.M11, TestObject.MatrixProperty.M11); + Assert.Equal(matrix.M12, TestObject.MatrixProperty.M12); + Assert.Equal(matrix.M21, TestObject.MatrixProperty.M21); + Assert.Equal(matrix.M22, TestObject.MatrixProperty.M22); + Assert.Equal(matrix.OffsetX, TestObject.MatrixProperty.OffsetX); + Assert.Equal(matrix.OffsetY, TestObject.MatrixProperty.OffsetY); + Assert.True(TestObject.MatrixProperty == matrix); + } + + [Fact] + public void TestKeyTimeTypeMapping() + { + var keyTime = KeyTime.FromTimeSpan(TimeSpan.FromTicks(42)); + TestObject.KeyTimeProperty = keyTime; + Assert.Equal(keyTime.TimeSpan, TestObject.KeyTimeProperty.TimeSpan); + Assert.True(TestObject.KeyTimeProperty == keyTime); + } + + [Fact] + public void TestRepeatBehaviorTypeMapping() + { + var repeatBehavior = new RepeatBehavior + { + Count = 1, + Duration = TimeSpan.FromTicks(42), + Type = RepeatBehaviorType.Forever + }; + TestObject.RepeatBehaviorProperty = repeatBehavior; + Assert.Equal(repeatBehavior.Count, TestObject.RepeatBehaviorProperty.Count); + Assert.Equal(repeatBehavior.Duration, TestObject.RepeatBehaviorProperty.Duration); + Assert.Equal(repeatBehavior.Type, TestObject.RepeatBehaviorProperty.Type); + Assert.True(TestObject.RepeatBehaviorProperty == repeatBehavior); + } + + [Fact] + public void TestMatrix3DTypeMapping() + { + var matrix3D = new Matrix3D + { + M11 = 11, + M12 = 12, + M13 = 13, + M14 = 14, + M21 = 21, + M22 = 22, + M23 = 23, + M24 = 24, + M31 = 31, + M32 = 32, + M33 = 33, + M34 = 34, + OffsetX = 41, + OffsetY = 42, + OffsetZ = 43, + M44 = 44 + }; + + TestObject.Matrix3DProperty = matrix3D; + Assert.Equal(matrix3D.M11, TestObject.Matrix3DProperty.M11); + Assert.Equal(matrix3D.M12, TestObject.Matrix3DProperty.M12); + Assert.Equal(matrix3D.M13, TestObject.Matrix3DProperty.M13); + Assert.Equal(matrix3D.M14, TestObject.Matrix3DProperty.M14); + Assert.Equal(matrix3D.M21, TestObject.Matrix3DProperty.M21); + Assert.Equal(matrix3D.M22, TestObject.Matrix3DProperty.M22); + Assert.Equal(matrix3D.M23, TestObject.Matrix3DProperty.M23); + Assert.Equal(matrix3D.M24, TestObject.Matrix3DProperty.M24); + Assert.Equal(matrix3D.M31, TestObject.Matrix3DProperty.M31); + Assert.Equal(matrix3D.M32, TestObject.Matrix3DProperty.M32); + Assert.Equal(matrix3D.M33, TestObject.Matrix3DProperty.M33); + Assert.Equal(matrix3D.M34, TestObject.Matrix3DProperty.M34); + Assert.Equal(matrix3D.OffsetX, TestObject.Matrix3DProperty.OffsetX); + Assert.Equal(matrix3D.OffsetY, TestObject.Matrix3DProperty.OffsetY); + Assert.Equal(matrix3D.OffsetZ, TestObject.Matrix3DProperty.OffsetZ); + Assert.Equal(matrix3D.M44, TestObject.Matrix3DProperty.M44); + Assert.True(TestObject.Matrix3DProperty == matrix3D); + } + + [Fact] + public void TestMatrix3x2TypeMapping() + { + var matrix3x2 = new Matrix3x2 + { + M11 = 11, + M12 = 12, + M21 = 21, + M22 = 22, + M31 = 31, + M32 = 32, + }; + TestObject.Matrix3x2Property = matrix3x2; + Assert.Equal(matrix3x2.M11, TestObject.Matrix3x2Property.M11); + Assert.Equal(matrix3x2.M12, TestObject.Matrix3x2Property.M12); + Assert.Equal(matrix3x2.M21, TestObject.Matrix3x2Property.M21); + Assert.Equal(matrix3x2.M22, TestObject.Matrix3x2Property.M22); + Assert.Equal(matrix3x2.M31, TestObject.Matrix3x2Property.M31); + Assert.Equal(matrix3x2.M32, TestObject.Matrix3x2Property.M32); + Assert.True(TestObject.Matrix3x2Property == matrix3x2); + } + + [Fact] + public void TestMatrix4x4TypeMapping() + { + var matrix4x4 = new Matrix4x4 + { + M11 = 11, + M12 = 12, + M13 = 13, + M14 = 14, + M21 = 21, + M22 = 22, + M23 = 23, + M24 = 24, + M31 = 31, + M32 = 32, + M33 = 33, + M34 = 34, + M41 = 41, + M42 = 42, + M43 = 43, + M44 = 44 + }; + TestObject.Matrix4x4Property = matrix4x4; + Assert.Equal(matrix4x4.M11, TestObject.Matrix4x4Property.M11); + Assert.Equal(matrix4x4.M12, TestObject.Matrix4x4Property.M12); + Assert.Equal(matrix4x4.M13, TestObject.Matrix4x4Property.M13); + Assert.Equal(matrix4x4.M14, TestObject.Matrix4x4Property.M14); + Assert.Equal(matrix4x4.M21, TestObject.Matrix4x4Property.M21); + Assert.Equal(matrix4x4.M22, TestObject.Matrix4x4Property.M22); + Assert.Equal(matrix4x4.M23, TestObject.Matrix4x4Property.M23); + Assert.Equal(matrix4x4.M24, TestObject.Matrix4x4Property.M24); + Assert.Equal(matrix4x4.M31, TestObject.Matrix4x4Property.M31); + Assert.Equal(matrix4x4.M32, TestObject.Matrix4x4Property.M32); + Assert.Equal(matrix4x4.M33, TestObject.Matrix4x4Property.M33); + Assert.Equal(matrix4x4.M34, TestObject.Matrix4x4Property.M34); + Assert.Equal(matrix4x4.M41, TestObject.Matrix4x4Property.M41); + Assert.Equal(matrix4x4.M42, TestObject.Matrix4x4Property.M42); + Assert.Equal(matrix4x4.M43, TestObject.Matrix4x4Property.M43); + Assert.Equal(matrix4x4.M44, TestObject.Matrix4x4Property.M44); + Assert.True(TestObject.Matrix4x4Property == matrix4x4); + } + + [Fact] + public void TestPlaneTypeMapping() + { + var plane = new Plane { D = 3.14F, Normal = new Vector3(1, 2, 3) }; + TestObject.PlaneProperty = plane; + Assert.Equal(plane.D, TestObject.PlaneProperty.D); + Assert.Equal(plane.Normal, TestObject.PlaneProperty.Normal); + Assert.True(TestObject.PlaneProperty == plane); + } + + [Fact] + public void TestQuaternionTypeMapping() + { + var quaternion = new Quaternion { W = 3.14F, X = 1, Y = 42, Z = 1729 }; + TestObject.QuaternionProperty = quaternion; + Assert.Equal(quaternion.W, TestObject.QuaternionProperty.W); + Assert.Equal(quaternion.X, TestObject.QuaternionProperty.X); + Assert.Equal(quaternion.Y, TestObject.QuaternionProperty.Y); + Assert.Equal(quaternion.Z, TestObject.QuaternionProperty.Z); + Assert.True(TestObject.QuaternionProperty == quaternion); + } + + [Fact] + public void TestVector2TypeMapping() + { + var vector2 = new Vector2 { X = 1, Y = 42 }; + TestObject.Vector2Property = vector2; + Assert.Equal(vector2.X, TestObject.Vector2Property.X); + Assert.Equal(vector2.Y, TestObject.Vector2Property.Y); + Assert.True(TestObject.Vector2Property == vector2); + } + + [Fact] + public void TestVector3TypeMapping() + { + var vector3 = new Vector3 { X = 1, Y = 42, Z = 1729 }; + TestObject.Vector3Property = vector3; + Assert.Equal(vector3.X, TestObject.Vector3Property.X); + Assert.Equal(vector3.Y, TestObject.Vector3Property.Y); + Assert.Equal(vector3.Z, TestObject.Vector3Property.Z); + Assert.True(TestObject.Vector3Property == vector3); + + TestObject.Vector3NullableProperty = Vector3.Zero; + Assert.Equal(0, TestObject.Vector3Property.X); + Assert.Equal(0, TestObject.Vector3Property.Y); + Assert.Equal(0, TestObject.Vector3Property.Z); + Assert.Equal(Vector3.Zero, TestObject.Vector3NullableProperty); + } + + [Fact] + public void TestVector4TypeMapping() + { + var vector4 = new Vector4 { W = 3.14F, X = 1, Y = 42, Z = 1729 }; + TestObject.Vector4Property = vector4; + Assert.Equal(vector4.W, TestObject.Vector4Property.W); + Assert.Equal(vector4.X, TestObject.Vector4Property.X); + Assert.Equal(vector4.Y, TestObject.Vector4Property.Y); + Assert.Equal(vector4.Z, TestObject.Vector4Property.Z); + Assert.True(TestObject.Vector4Property == vector4); + } + + [Fact] + public void TestTimeSpanMapping() + { + var ts = TimeSpan.FromSeconds(42); + TestObject.TimeSpanProperty = ts; + Assert.Equal(ts, TestObject.TimeSpanProperty); + Assert.Equal(ts, TestObject.GetTimeSpanReference().Value); + Assert.Equal(ts, Class.FromSeconds(42)); + } + + [Fact] + public void TestDateTimeMapping() + { + var now = DateTimeOffset.Now; + Assert.InRange((Class.Now() - now).Ticks, -TimeSpan.TicksPerSecond, TimeSpan.TicksPerSecond); // Unlikely to be the same, but should be within a second + TestObject.DateTimeProperty = now; + Assert.Equal(now, TestObject.DateTimeProperty); + Assert.Equal(now, TestObject.GetDateTimeProperty().Value); + } + + [Fact] + public void TestDateTimeMappingNegative() + { + var time = new DateTimeOffset(1501, 1, 1, 0, 0, 0, TimeSpan.Zero); + TestObject.DateTimeProperty = time; + Assert.Equal(time, TestObject.DateTimeProperty); + Assert.Equal(time, TestObject.GetDateTimeProperty().Value); + } + + [Fact] + public void TestExceptionMapping() + { + var ex = new ArgumentOutOfRangeException(); + + TestObject.HResultProperty = ex; + + Assert.IsType(TestObject.HResultProperty); + + TestObject.HResultProperty = null; + + Assert.Null(TestObject.HResultProperty); + } + + [Fact] + public void TestGeneratedRuntimeClassName() + { + IInspectable inspectable = new IInspectable(ComWrappersSupport.CreateCCWForObject(new ManagedProperties(2))); + Assert.Equal(typeof(IProperties1).FullName, inspectable.GetRuntimeClassName()); + } + + [Fact] + public void TestGetPropertyType() + { + Array arr = new[] { E.A, E.B, E.C }; + Array arr2 = new[] { new Estruct(), new Estruct() }; + Array arr3 = new int[] { 1, 2, 3 }; + IList arr4 = new List() { E.A, E.B, E.C }; + Array arr5 = new PropertyType[] { PropertyType.UInt8, PropertyType.Int16, PropertyType.UInt16 }; + + Assert.Equal(-1, Class.GetPropertyType(arr)); + Assert.Equal(-1, Class.GetPropertyType(arr2)); + Assert.Equal((int)PropertyType.Int32Array, Class.GetPropertyType(arr3)); + Assert.Equal(-1, Class.GetPropertyType(arr4)); + Assert.Equal((int)PropertyType.OtherTypeArray, Class.GetPropertyType(arr5)); + Assert.Equal(-1, Class.GetPropertyType(arr.GetValue(0))); + Assert.Equal(-1, Class.GetPropertyType(arr2.GetValue(0))); + Assert.Equal((int)PropertyType.Int32, Class.GetPropertyType(arr3.GetValue(0))); + Assert.Equal(-1, Class.GetPropertyType(arr4[0])); + Assert.Equal((int)PropertyType.OtherType, Class.GetPropertyType(arr5.GetValue(0))); + } + + [Fact] + public void TestGetRuntimeClassName() + { + Array arr = new[] { E.A, E.B, E.C }; + Array arr2 = new[] { new Estruct(), new Estruct() }; + Array arr3 = new int[] { 1, 2, 3 }; + IList arr4 = new List() { E.A, E.B, E.C }; + Array arr5 = new PropertyType[] { PropertyType.UInt8, PropertyType.Int16, PropertyType.UInt16 }; + + Assert.Equal(string.Empty, Class.GetName(arr)); + Assert.Equal(string.Empty, Class.GetName(arr2)); + Assert.Equal("Windows.Foundation.IReferenceArray`1", Class.GetName(arr3)); + Assert.Equal("Microsoft.UI.Xaml.Interop.IBindableVector", Class.GetName(arr4)); + Assert.Equal("Windows.Foundation.IReferenceArray`1", Class.GetName(arr5)); + Assert.Equal(string.Empty, Class.GetName(arr.GetValue(0))); + Assert.Equal(string.Empty, Class.GetName(arr2.GetValue(0))); + Assert.Equal("Windows.Foundation.IReference`1", Class.GetName(arr3.GetValue(0))); + Assert.Equal(string.Empty, Class.GetName(arr4[0])); + Assert.Equal("Windows.Foundation.IReference`1", Class.GetName(arr5.GetValue(0))); + + Assert.Equal("Windows.Foundation.IReference`1", Class.GetName(typeof(IProperties1))); + Assert.Equal("Windows.Foundation.IReference`1", Class.GetName(typeof(Type))); + } + + [Fact] + public void TestGeneratedRuntimeClassName_Primitive() + { + IInspectable inspectable = new IInspectable(ComWrappersSupport.CreateCCWForObject(2)); + Assert.Equal("Windows.Foundation.IReference`1", inspectable.GetRuntimeClassName()); + } + + [Fact] + public void TestGeneratedRuntimeClassName_Array() + { + IInspectable inspectable = new IInspectable(ComWrappersSupport.CreateCCWForObject(new int[0])); + Assert.Equal("Windows.Foundation.IReferenceArray`1", inspectable.GetRuntimeClassName()); + } + + [Fact] + public void TestValueBoxing() + { + int i = 42; + Assert.Equal(i, Class.UnboxInt32(i)); + + bool b = true; + Assert.Equal(b, Class.UnboxBoolean(b)); + + string s = "Hello World!"; + Assert.Equal(s, Class.UnboxString(s)); + + ProvideInt intHandler = () => 42; + Assert.Equal(intHandler, Class.UnboxDelegate(intHandler)); + + EnumValue enumValue = EnumValue.Two; + Assert.Equal(enumValue, Class.UnboxEnum(enumValue)); + + var type = typeof(EnumValue); + Assert.Equal(type, Class.UnboxType(type)); + + Assert.Equal(typeof(Class), Class.BoxedType); + } + + [Fact] + public void TestArrayBoxing() + { + int[] i = new[] { 42, 1, 4, 50, 0, -23 }; + Assert.Equal((IEnumerable)i, Class.UnboxInt32Array(i)); + + bool[] b = new[] { true, false, true, true, false }; + Assert.Equal((IEnumerable)b, Class.UnboxBooleanArray(b)); + + string[] s = new[] { "Hello World!", "WinRT", "C#", "Boxing" }; + Assert.Equal((IEnumerable)s, Class.UnboxStringArray(s)); + } + + [Fact] + public void TestArrayUnboxing() + { + int[] i = new[] { 42, 1, 4, 50, 0, -23 }; + + var obj = PropertyValue.CreateInt32Array(i); + Assert.IsType(obj); + Assert.Equal(i, (IEnumerable)obj); + } + + [Fact] + public void TestUnboxingUsingPropertyValue() + { + int i = 24; + Assert.Equal(i, Class.UnboxInt32UsingPropertyValue(i)); + + uint j = 42; + Assert.Equal((int)j, Class.UnboxInt32UsingPropertyValue(j)); + + System.Nullable k = new System.Nullable(34); + Assert.Equal(k, Class.UnboxInt32UsingPropertyValue(k)); + + string s = "Hello!"; + Assert.Equal(s, Class.UnboxStringUsingPropertyValue(s)); + + Guid guid = new("36AA48DD-ACBB-4570-B12A-86BF71D09A12"); + Assert.Equal("36AA48DD-ACBB-4570-B12A-86BF71D09A12", Class.UnboxStringUsingPropertyValue(guid), true); + + Assert.Throws(() => Class.UnboxInt32UsingPropertyValue(s)); + + Rect rect = new Rect(1, 2, 3, 4); + Assert.Equal(rect, Class.UnboxRectUsingPropertyValue(rect)); + + int[] iArr = new[] { 42, 0, -23 }; + Assert.Equal(iArr, (IEnumerable)Class.UnboxInt32ArrayUsingPropertyValue(iArr)); + + bool[] bArr = new[] { true, false, false }; + Assert.Equal((IEnumerable)bArr, Class.UnboxBooleanArrayUsingPropertyValue(bArr)); + + Point[] pArr = new[] { new Point(1, 3), new Point(2, 4) }; + Assert.Equal((IEnumerable)pArr, Class.UnboxPointArrayUsingPropertyValue(pArr)); + } + + [Fact] + public void TestListOfTypes() + { + var types = Class.ListOfTypes; + Assert.Equal(2, types.Count); + Assert.Equal(typeof(Class), types[0]); + Assert.Equal(typeof(int?), types[1]); + } + + [Fact] + public void PrimitiveTypeInfo() + { + Assert.Equal(typeof(int), Class.Int32Type); + Assert.True(Class.VerifyTypeIsInt32Type(typeof(int))); + } + + [Fact] + public void WinRTTypeInfo() + { + Assert.Equal(typeof(Class), Class.ThisClassType); + Assert.True(Class.VerifyTypeIsThisClassType(typeof(Class))); + } + + [Fact] + public void ProjectedTypeInfo() + { + Assert.Equal(typeof(int?), Class.ReferenceInt32Type); + Assert.True(Class.VerifyTypeIsReferenceInt32Type(typeof(int?))); + } + + [Fact] + public void TypeInfoGenerics() + { + var typeName = Class.GetTypeNameForType(typeof(IList)); + Assert.Equal("Windows.Foundation.Collections.IVector`1", typeName); + } + + [Fact] + public void TypeInfoType() + { + var typeName = Class.GetTypeNameForType(typeof(Type)); + Assert.Equal("Windows.UI.Xaml.Interop.TypeName", typeName); + } + + [Fact] + public void TestGenericTypeMarshalling() + { + Assert.Equal(typeof(ABI.System.Type), Marshaler.AbiType); + } + + [Fact] + public void TestStringUnboxing() + { + var str1 = Class.EmptyString; + var str2 = Class.EmptyString; + Assert.IsType(str1); + Assert.IsType(str2); + Assert.Equal(string.Empty, (string)str1); + Assert.Equal(string.Empty, (string)str2); + } + + [Fact] + public void TestDelegateUnboxing() + { + var del = Class.BoxedDelegate; + Assert.IsType(del); + var provideUriDel = (ProvideUri)del; + Assert.Equal(new Uri("http://microsoft.com"), provideUriDel()); + } + + [Fact] + public void TestEnumUnboxing() + { + var enumVal = Class.BoxedEnum; + Assert.IsType(enumVal); + Assert.Equal(EnumValue.Two, enumVal); + } + + internal class ManagedType { } + + [Fact] + public void CCWOfListOfManagedType() + { + using var ccw = ComWrappersSupport.CreateCCWForObject(new List()); + using var qiResult = ccw.As(GuidGenerator.GetIID(typeof(global::System.Collections.Generic.IEnumerable).GetHelperType())); + } + + [Fact] + public void TestForIterableObject() + { + // Make sure for collections of value types that they don't project IEnumerable + // as it isn't a covariant interface. + Assert.False(TestObject.CheckForBindableObjectInterface(new List())); + Assert.False(TestObject.CheckForBindableObjectInterface(new List())); + Assert.False(TestObject.CheckForBindableObjectInterface(new List())); + Assert.False(TestObject.CheckForBindableObjectInterface(new Dictionary())); + + // Make sure for collections of object types that they do project IEnumerable + // as it is an covariant interface. + Assert.True(TestObject.CheckForBindableObjectInterface(new List())); + Assert.True(TestObject.CheckForBindableObjectInterface(new List())); + Assert.True(TestObject.CheckForBindableObjectInterface(new List())); + Assert.True(TestObject.CheckForBindableObjectInterface(new List())); + } + + internal class ManagedType2 : List { } + + internal class ManagedType3 : List, IDisposable + { + public void Dispose() + { + } + } + + [Fact] + public void CCWOfListOfManagedType2() + { + using var ccw = ComWrappersSupport.CreateCCWForObject(new ManagedType2()); + var qiResult = ccw.As(GuidGenerator.GetIID(typeof(global::System.Collections.Generic.IEnumerable).GetHelperType())); + } + + [Fact] + public void CCWOfListOfManagedType3() + { + using var ccw = ComWrappersSupport.CreateCCWForObject(new ManagedType3()); + var qiResult = ccw.As(GuidGenerator.GetIID(typeof(global::System.Collections.Generic.IEnumerable).GetHelperType())); + var qiResult2 = ccw.As(GuidGenerator.GetIID(typeof(global::System.Collections.Generic.IEnumerable).GetHelperType())); + } + + [Fact] + public void WeakReferenceOfManagedObject() + { + var properties = new ManagedProperties(42); + WeakRefNS.WeakReference weakReference = new WeakRefNS.WeakReference(properties); + Assert.True(weakReference.TryGetTarget(out var propertiesStrong)); + Assert.Same(properties, propertiesStrong); + } + + [Fact] + public void WeakReferenceOfNativeObject() + { + var weakReference = new WeakRefNS.WeakReference(TestObject); + Assert.True(weakReference.TryGetTarget(out var classStrong)); + Assert.Same(TestObject, classStrong); + } + + [Fact] + public void WeakReferenceOfNativeObjectRehydratedAfterWrapperIsCollected() + { + static (WeakRefNS.WeakReference winrt, WeakReference net, IObjectReference objRef) GetWeakReferences() + { + var obj = new Class(); + ComWrappersSupport.TryUnwrapObject(obj, out var objRef); + return (new WeakRefNS.WeakReference(obj), new WeakReference(obj), objRef); + } + + var (winrt, net, objRef) = GetWeakReferences(); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + Assert.False(net.IsAlive); + Assert.True(winrt.TryGetTarget(out _)); + GC.KeepAlive(objRef); + } + + [Fact] + public void TestUnwrapInspectable() + { + using var objRef = MarshalInspectable.CreateMarshaler(TestObject); + var inspectable = IInspectable.FromAbi(objRef.ThisPtr); + Assert.True(ComWrappersSupport.TryUnwrapObject(inspectable, out _)); + + using var objRef2 = MarshalInspectable.CreateMarshaler(TestObject); + var inspectable2 = IInspectable.FromAbi(objRef2.ThisPtr); + Assert.True(ComWrappersSupport.TryUnwrapObject(inspectable2, out _)); + } + + [Fact] + public void TestManagedAgileObject() + { + using var testObjectAgileRef = TestObject.AsAgile(); + var agileTestObject = testObjectAgileRef.Get(); + Assert.Equal(TestObject, agileTestObject); + + IProperties1 properties = new ManagedProperties(42); + using var propertiesAgileRef = properties.AsAgile(); + var agileProperties = propertiesAgileRef.Get(); + Assert.Equal(properties.ReadWriteProperty, agileProperties.ReadWriteProperty); + + var agileObject = TestObject.As(); + Assert.NotNull(agileObject); + + IProperties1 properties2 = null; + using var properties2AgileRef = properties2.AsAgile(); + var agileProperties2 = properties2AgileRef.Get(); + Assert.Null(agileProperties2); + } + + class NonAgileClassCaller + { + public void AcquireObject() + { + Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState()); + nonAgileClass = new NonAgileClass(); + nonAgileClass.CanExecuteChanged += NonAgileClass_Event; -#if NET -using WeakRefNS = System; -#else -using WeakRefNS = WinRT; -#endif - -#if NET -// Test SupportedOSPlatform warnings for APIs targeting 10.0.19041.0: -[assembly: global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.18362.0")] -#endif - -namespace UnitTest -{ - public class TestCSharp - { - public Class TestObject { get; private set; } - - public TestCSharp() - { - TestObject = new Class(); - } - + nonAgileObject = new Windows.UI.Popups.PopupMenu(); + nonAgileObject.Commands.Add(new Windows.UI.Popups.UICommand("test")); + nonAgileObject.Commands.Add(new Windows.UI.Popups.UICommand("test2")); + Assert.ThrowsAny(() => nonAgileObject.As()); - // Test a fix for a bug in Mono.Cecil that was affecting the IIDOptimizer when it encountered long class names - [Fact] - public void TestLongClassNameEventSource() + agileReference = nonAgileObject.AsAgile(); + objectAcquired.Set(); + valueAcquired.WaitOne(); + + // Object gets proxied to the apartment. + Assert.Equal(2, proxyObject.Commands.Count); + + agileReference.Dispose(); + } + + public void CheckValue() + { + objectAcquired.WaitOne(); + Assert.Equal(ApartmentState.MTA, Thread.CurrentThread.GetApartmentState()); + proxyObject = agileReference.Get(); + Assert.Equal(2, proxyObject.Commands.Count); + + // Remove the event handler from a different context from what it was initially added in + // to make sure we can unsubscribe from the event via the proxy in non agile scenarios. + nonAgileClass.CanExecuteChanged -= NonAgileClass_Event; + + valueAcquired.Set(); + } + + public void CallProxyObject() + { + // Call to a proxy object which we internally use an agile reference + // to resolve after the apartment is gone should throw. + Assert.ThrowsAny(() => proxyObject.Commands); + } + + public void NonAgileClass_Event(object sender, object e) + { + } + + private Windows.UI.Popups.PopupMenu nonAgileObject; + private Windows.UI.Popups.PopupMenu proxyObject; + private AgileReference agileReference; + private readonly AutoResetEvent objectAcquired = new AutoResetEvent(false); + private readonly AutoResetEvent valueAcquired = new AutoResetEvent(false); + private NonAgileClass nonAgileClass; + } + + + [Fact] + public void TestNonAgileObjectCall() { - bool flag = false; - var long_class_name = new ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz(); - long_class_name.EventForAVeryLongClassName += - (ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz sender, ABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVQXYZabcdefghijklmnopqrstuvqxyzABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz args) - => flag = true; - long_class_name.InvokeEvent(); - Assert.True(flag); - } - - [Fact] - public void TestEventArgsVector() - { - var eventArgsVector = TestObject.GetEventArgsVector(); - Assert.Equal(1, eventArgsVector.Count); - foreach (var dataErrorChangedEventArgs in eventArgsVector) - { - var propName = dataErrorChangedEventArgs.PropertyName; - Assert.Equal("name", propName); - } - } - - [Fact] - public void TestNonGenericDelegateVector() + NonAgileClassCaller caller = new NonAgileClassCaller(); + Thread staThread = new Thread(new ThreadStart(caller.AcquireObject)); + staThread.SetApartmentState(ApartmentState.STA); + staThread.Start(); + + Thread mtaThread = new Thread(new ThreadStart(caller.CheckValue)); + mtaThread.SetApartmentState(ApartmentState.MTA); + mtaThread.Start(); + mtaThread.Join(); + staThread.Join(); + + // Spin another STA thread after the other 2 threads are done and try to + // access one of the proxied objects. They should fail as there is no context + // to switch to in order to marshal it to the current apartment. + Thread anotherStaThread = new Thread(new ThreadStart(caller.CallProxyObject)); + anotherStaThread.SetApartmentState(ApartmentState.STA); + anotherStaThread.Start(); + anotherStaThread.Join(); + } + + [Fact] + public void TestNonAgileDelegateCall() { - var provideUriVector = TestObject.GetNonGenericDelegateVector(); + var expected = new int[] { 0, 1, 2 }; + var observable = new ManagedBindableObservable(expected); + var nonAgileClass = new NonAgileClass(); + nonAgileClass.Observe(observable); + observable.Add(3); + Assert.Equal(6, observable.Observation); + } - Assert.Equal(1, provideUriVector.Count); - - foreach (var provideUri in provideUriVector) + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("EECDBF0E-BAE9-4CB6-A68E-9598E1CB57BB")] + internal interface IWindowNative + { + IntPtr WindowHandle { get; } + } + + [ComImport] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [Guid("3E68D4BD-7135-4D10-8018-9FB6D9F33FA1")] + internal interface IInitializeWithWindow + { + void Initialize(IntPtr hwnd); + } + + [Fact] + unsafe public void TestComImports() + { + static Object MakeObject() { - Uri delegateTarget = provideUri.Invoke(); - Assert.Equal("http://microsoft.com", delegateTarget.OriginalString); + Assert.Equal(0, ComImports.NumObjects); + var obj = ComImports.MakeObject(); + Assert.Equal(1, ComImports.NumObjects); + return obj; } - } - - [Fact] - public void TestEnums() - { - // Enums - var expectedEnum = EnumValue.Two; - TestObject.EnumProperty = expectedEnum; - Assert.Equal(expectedEnum, TestObject.EnumProperty); - expectedEnum = EnumValue.One; - TestObject.CallForEnum(() => expectedEnum); - TestObject.EnumPropertyChanged += - (object sender, EnumValue value) => Assert.Equal(expectedEnum, value); - TestObject.RaiseEnumChanged(); - - var expectedEnumStruct = new EnumStruct() { value = EnumValue.Two }; - TestObject.EnumStructProperty = expectedEnumStruct; - Assert.Equal(expectedEnumStruct, TestObject.EnumStructProperty); - expectedEnumStruct = new EnumStruct() { value = EnumValue.One }; - TestObject.CallForEnumStruct(() => expectedEnumStruct); - TestObject.EnumStructPropertyChanged += - (object sender, EnumStruct value) => Assert.Equal(expectedEnumStruct, value); - TestObject.RaiseEnumStructChanged(); - - var expectedEnums = new EnumValue[] { EnumValue.One, EnumValue.Two }; - TestObject.EnumsProperty = expectedEnums; - Assert.Equal(expectedEnums, TestObject.EnumsProperty); - TestObject.CallForEnums(() => expectedEnums); - Assert.Equal(expectedEnums, TestObject.EnumsProperty); - - TestObject.EnumsProperty = null; - Assert.Equal(null, TestObject.EnumsProperty); - - var expectedEnumStructs = new EnumStruct[] { new EnumStruct(EnumValue.One), new EnumStruct(EnumValue.Two) }; - TestObject.EnumStructsProperty = expectedEnumStructs; - Assert.Equal(expectedEnumStructs, TestObject.EnumStructsProperty); - TestObject.CallForEnumStructs(() => expectedEnumStructs); - Assert.Equal(expectedEnumStructs, TestObject.EnumStructsProperty); - - TestObject.EnumStructsProperty = null; - Assert.Equal(null, TestObject.EnumStructsProperty); - - // Flags - var expectedFlag = FlagValue.All; - TestObject.FlagProperty = expectedFlag; - Assert.Equal(expectedFlag, TestObject.FlagProperty); - expectedFlag = FlagValue.One; - TestObject.CallForFlag(() => expectedFlag); - TestObject.FlagPropertyChanged += - (object sender, FlagValue value) => Assert.Equal(expectedFlag, value); - TestObject.RaiseFlagChanged(); - - var expectedFlagStruct = new FlagStruct() { value = FlagValue.All }; - TestObject.FlagStructProperty = expectedFlagStruct; - Assert.Equal(expectedFlagStruct, TestObject.FlagStructProperty); - expectedFlagStruct = new FlagStruct() { value = FlagValue.One }; - TestObject.CallForFlagStruct(() => expectedFlagStruct); - TestObject.FlagStructPropertyChanged += - (object sender, FlagStruct value) => Assert.Equal(expectedFlagStruct, value); - TestObject.RaiseFlagStructChanged(); - - var expectedFlags = new FlagValue[] { FlagValue.One, FlagValue.All }; - TestObject.FlagsProperty = expectedFlags; - Assert.Equal(expectedFlags, TestObject.FlagsProperty); - TestObject.CallForFlags(() => expectedFlags); - Assert.Equal(expectedFlags, TestObject.FlagsProperty); - - var expectedFlagStructs = new FlagStruct[] { new FlagStruct(FlagValue.One), new FlagStruct(FlagValue.All) }; - TestObject.FlagStructsProperty = expectedFlagStructs; - Assert.Equal(expectedFlagStructs, TestObject.FlagStructsProperty); - TestObject.CallForFlagStructs(() => expectedFlagStructs); - Assert.Equal(expectedFlagStructs, TestObject.FlagStructsProperty); - } - - [Fact] - public void TestGetByte() - { - var array = new byte[] { 0x01 }; - var buff = array.AsBuffer(); - Assert.True(buff.Length == 1); - byte b = buff.GetByte(0); - Assert.True(b == 0x01); - } - - [Fact] - public void TestManyBufferExtensionMethods() - { - var arrayLen3 = new byte[] { 0x01, 0x02, 0x03 }; - var buffLen3 = arrayLen3.AsBuffer(); - - var arrayLen4 = new byte[] { 0x11, 0x12, 0x13, 0x14 }; - var buffLen4 = arrayLen4.AsBuffer(); - - var arrayLen4Again = new byte[4]; - - arrayLen3.CopyTo(1, buffLen4, 0, 1); // copy just the second element of the array to the beginning of the buffer - Assert.True(buffLen4.Length == 4); - Assert.Throws(() => buffLen4.GetByte(5)); // shouldn't have a 5th element - Assert.True(buffLen4.GetByte(0) == 0x02); // make sure we got the 2nd element of the array - - arrayLen3.CopyTo(buffLen4); // Array to Buffer copying - Assert.True(buffLen4.Length == 4); - Assert.True(buffLen4.GetByte(0) == 0x01); // make sure we updated the first few - Assert.True(buffLen4.GetByte(1) == 0x02); - Assert.True(buffLen4.GetByte(2) == 0x03); - Assert.True(buffLen4.GetByte(3) == 0x14); // and kept the last one - - var buffLen3Again = buffLen3.ToArray().AsBuffer(); - Assert.True(buffLen3Again.GetByte(0) == 0x01); - Assert.True(buffLen3Again.GetByte(1) == 0x02); - Assert.True(buffLen3Again.GetByte(2) == 0x03); - - Assert.False(buffLen3.IsSameData(buffLen3Again)); // different memory regions - - buffLen4.CopyTo(arrayLen4Again); // Buffer to Array copying - var array4 = buffLen4.ToArray(); - Assert.True(arrayLen4Again.Length == array4.Length); - for (int i = 0; i < arrayLen4Again.Length; ++i) - { - Assert.True(arrayLen4Again[i] == array4[i]); // make sure we have equal array - } - } - - [Fact] - public void TestIsSameDataDifferentArrays() - { - var arr = new byte[] { 0x01, 0x02 }; - var buf1 = arr.AsBuffer(); - var arr2 = new byte[] { 0x01, 0x02 }; - var buf2 = arr2.AsBuffer(); - Assert.False(buf1.IsSameData(buf2)); - } - - [Fact] - public void TestIsSameDataUsingCopyTo() - { - var arr = new byte[] { 0x01, 0x02 }; - var buf1 = arr.AsBuffer(); - var buf2 = new Windows.Storage.Streams.Buffer(2); - buf1.CopyTo(buf2); - Assert.False(buf1.IsSameData(buf2)); - } - - [Fact] - public void TestIsSameDataUsingAsBufferTwice() - { - var arr = new byte[] { 0x01, 0x02 }; - var buf1 = arr.AsBuffer(); - var buf2 = arr.AsBuffer(); - Assert.True(buf1.IsSameData(buf2)); - } - - [Fact] - public void TestIsSameDataUsingToArray() - { - var arr = new byte[] { 0x01, 0x02 }; - var buf1 = arr.AsBuffer(); - var buf2 = buf1.ToArray().AsBuffer(); - Assert.False(buf1.IsSameData(buf2)); - } - - [Fact] - public void TestBufferAsStreamUsingAsBuffer() - { - var arr = new byte[] { 0x01, 0x02 }; - Stream stream = arr.AsBuffer().AsStream(); - Assert.True(stream != null); - Assert.True(stream.Length == 2); - } - - [Fact] - public void TestBufferAsStreamWithEmptyBuffer1() - { - var buffer = new Windows.Storage.Streams.Buffer(0); - Stream stream = buffer.AsStream(); - Assert.True(stream != null); - Assert.True(stream.Length == 0); - } - - [Fact] - public void TestBufferImproperReadCopyToOutOfBounds() - { - var array = new byte[] { 0x01, 0x02, 0x03 }; - var buffer = array.AsBuffer(); - var biggerBuffer = new Windows.Storage.Streams.Buffer(5); - buffer.CopyTo(biggerBuffer); - Assert.Throws(() => biggerBuffer.ToArray(4, 2)); - } - - [Fact] - public void TestBufferImproperReadCopyToStraddleBounds() - { - var array = new byte[] { 0x01, 0x02, 0x03 }; - var buffer = array.AsBuffer(); - var biggerBuffer = new Windows.Storage.Streams.Buffer(5); - buffer.CopyTo(biggerBuffer); - Assert.Throws(() => biggerBuffer.ToArray(2, 2)); - } - - [Fact] - public void TestBufferImproperReadGetByte() - { - var array = new byte[] { 0x01, 0x02, 0x03 }; - var buffer = array.AsBuffer(); - Assert.Throws(() => buffer.GetByte(4)); - } - - [Fact] - public void TestEmptyBufferToArray() - { - var buffer = new Windows.Storage.Streams.Buffer(0); - var array = buffer.ToArray(); - Assert.True(array.Length == 0); - } - - [Fact] - public void TestArrayCopyToBufferEndToBeginning() - { - IBuffer buf = new Windows.Storage.Streams.Buffer(3); - byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; - arr.CopyTo(3, buf, 0, 0); - } - - [Fact] - public void TestArrayCopyToBufferEndToEnd2() - { - IBuffer buf = new Windows.Storage.Streams.Buffer(3); - byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; - arr.CopyTo(0, buf, 0, 3); - } - - [Fact] - public void TestArrayCopyToBufferEndToEnd() - { - IBuffer buf = new Windows.Storage.Streams.Buffer(3); - byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; - arr.CopyTo(3, buf, 3, 0); - } - - [Fact] - public void TestArrayCopyToBufferMidToMid() - { - IBuffer buf = new Windows.Storage.Streams.Buffer(3); - byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; - arr.CopyTo(1, buf, 1, 0); - } - - [Fact] - public void TestArrayCopyToBufferMidToEnd() - { - IBuffer buf = new Windows.Storage.Streams.Buffer(3); - byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; - arr.CopyTo(1, buf, 3, 0); - } - - [Fact] - public void TestBufferCopyToArrayEndToEnd() - { - byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; - var buf = arr.AsBuffer(); - var target = new byte[4]; - buf.CopyTo(3, target, 4, 0); - } - - [Fact] - public void BufferToArrayWithZeroCountAtEnd2() - { - byte[] array = { 0xA1, 0xA2, 0xA3 }; - var result = array.AsBuffer().ToArray(3, 0); - Assert.True(result != null); - Assert.True(0 == result.Length); - } - - [Fact] - public void BufferToArrayWithZeroCountAtEnd_WorksWithSpans() - { - byte[] array = { 0xA1, 0xA2, 0xA3 }; - var result = array.AsSpan().Slice(3, 0).ToArray(); - Assert.True(result != null); - Assert.True(0 == result.Length); - } - - [Fact] - public void TestWinRTBufferWithZeroLength() - { - byte[] arr = new byte[] { 0x01, 0x02, 0x03 }; - MemoryStream stream = new MemoryStream(arr, 0, 3, false, true); - IBuffer buff = stream.GetWindowsRuntimeBuffer(3, 0); - Assert.True(buff != null); - Assert.True(buff.Length == 0); - } - - [Fact] - public void TestEmptyBufferCopyTo() - { - var buffer = new Windows.Storage.Streams.Buffer(0); - byte[] array = { }; - buffer.CopyTo(array); - Assert.True(array.Length == 0); - } - - [Fact] - public void TestTypePropertyWithSystemType() - { - TestObject.TypeProperty = typeof(System.Type); - Assert.Equal("Windows.UI.Xaml.Interop.TypeName", TestObject.GetTypePropertyAbiName()); - Assert.Equal("Metadata", TestObject.GetTypePropertyKind()); - } - - class CustomDictionary : Dictionary { } - - [Fact] - public void TestTypePropertyWithCustomType() - { - TestObject.TypeProperty = typeof(CustomDictionary); - var name = TestObject.GetTypePropertyAbiName(); - Assert.Equal("UnitTest.TestCSharp+CustomDictionary, UnitTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null", name); - } - - [Fact] - public void TestVectorCastConversion() - { - var vector = TestObject.GetUriVectorAsIInspectableVector(); - var uriVector = vector.Cast(); - var first = uriVector.First(); - Assert.Equal(vector, uriVector); - } - - async Task LookupPorts() - { - var ports = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync( - Windows.Devices.SerialCommunication.SerialDevice.GetDeviceSelector(), - new string[] { "System.ItemNameDisplay" }); - foreach (var port in ports) - { - object o = port.Properties["System.ItemNameDisplay"]; - Assert.NotNull(o); - } - } - - [Fact] - public void TestReadOnlyDictionaryLookup() - { - Assert.True(LookupPorts().Wait(5000)); - } - -#if NET - async Task InvokeStreamWriteZeroBytes() - { - var random = new Random(42); - byte[] data = new byte[256]; - random.NextBytes(data); - - using var stream = new InMemoryRandomAccessStream().AsStream(); - await stream.WriteAsync(data, 0, 0); - await stream.WriteAsync(data, data.Length, 0); - } - - [Fact] - public void TestStreamWriteZeroByte() - { - Assert.True(InvokeStreamWriteZeroBytes().Wait(1000)); - } - - async Task InvokeStreamWriteAsync() - { - using var fileStream = File.OpenWrite("TestFile.txt"); - using var winRTStream = fileStream.AsOutputStream(); - - var winRTBuffer = new Windows.Storage.Streams.Buffer(capacity: 0); - - await winRTStream.WriteAsync(winRTBuffer); - Assert.True(true); - } - - [Fact] - public void TestStreamWriteAsync() - { - Assert.True(InvokeStreamWriteAsync().Wait(1000)); - } - - [Fact] - public void TestAsStream() - { - using InMemoryRandomAccessStream winrtStream = new InMemoryRandomAccessStream(); - using Stream normalStream = winrtStream.AsStream(); - using var memoryStream = new MemoryStream(); - normalStream.CopyTo(memoryStream); - } - - async Task InvokeStreamWriteAndReadAsync() - { - var random = new Random(42); - byte[] data = new byte[256]; - random.NextBytes(data); - - using var stream = new InMemoryRandomAccessStream().AsStream(); - await stream.WriteAsync(data, 0, data.Length); - stream.Seek(0, SeekOrigin.Begin); - - byte[] read = new byte[256]; - await stream.ReadAsync(read, 0, read.Length); - Assert.Equal(read, data); - } - - [Fact] - public void TestStreamWriteAndRead() - { - Assert.True(InvokeStreamWriteAndReadAsync().Wait(1000)); - } - - [Fact] - public void TestDynamicInterfaceCastingOnValidInterface() - { - var agileObject = (IAgileObject)(IWinRTObject)TestObject; - Assert.NotNull(agileObject); - } - - [Fact] - public void TestDynamicInterfaceCastingOnInvalidInterface() - { - Assert.ThrowsAny(() => (IStringableInterop)(IWinRTObject)TestObject); - } - - [Fact] - public void TestBuffer() - { - var arr1 = new byte[] { 0x01, 0x02 }; - var buff = arr1.AsBuffer(); - var arr2 = buff.ToArray(0,2); - Assert.True(arr1[0] == arr2[0]); - Assert.True(arr1[1] == arr2[1]); - } - -#endif - - async Task TestStorageFileAsync() - { - var folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - StorageFile file = await StorageFile.GetFileFromPathAsync(folderPath + "\\UnitTest.dll"); - var handle = WindowsRuntimeStorageExtensions.CreateSafeFileHandle(file, FileAccess.Read); - Assert.NotNull(handle); - } - - [Fact] - public void TestStorageFile() - { - Assert.True(TestStorageFileAsync().Wait(1000)); - } - - async Task TestStorageFolderAsync() - { - var folderPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); - StorageFolder folder = await StorageFolder.GetFolderFromPathAsync(folderPath); - var handle = WindowsRuntimeStorageExtensions.CreateSafeFileHandle(folder, "UnitTest.dll", FileMode.Open, FileAccess.Read); - Assert.NotNull(handle); - } - - [Fact] - public void TestStorageFolder() - { - Assert.True(TestStorageFolderAsync().Wait(1000)); - } - - async Task InvokeWriteBufferAsync() - { - var random = new Random(42); - byte[] data = new byte[256]; - random.NextBytes(data); - - using var stream = new InMemoryRandomAccessStream(); - IBuffer buffer = data.AsBuffer(); - await stream.WriteAsync(buffer); - } - - [Fact] - public void TestWriteBuffer() - { - Assert.True(InvokeWriteBufferAsync().Wait(1000)); - } - - [Fact] - public void TestUri() - { - var base_uri = "https://github.com"; - var relative_uri = "microsoft/CsWinRT"; - var full_uri = base_uri + "/" + relative_uri; - var managedUri = new Uri(full_uri); - - var uri1 = ABI.System.Uri.FromAbi(ABI.System.Uri.FromManaged(managedUri)); - var str1 = uri1.ToString(); - Assert.Equal(full_uri, str1); - - var expected = new Uri("http://expected"); - TestObject.UriProperty = expected; - Assert.Equal(expected, TestObject.UriProperty); - - TestObject.CallForUri(() => managedUri); - TestObject.UriPropertyChanged += - (object sender, Uri value) => Assert.Equal(managedUri, value); - TestObject.RaiseUriChanged(); - - var uri2 = MarshalInspectable.FromAbi(ABI.System.Uri.FromManaged(managedUri)); - var str2 = uri2.ToString(); - Assert.Equal(full_uri, str2); - - var uri3 = MarshalInspectable.FromAbi(ABI.System.Uri.FromManaged(managedUri)); - var str3 = uri3.ToString(); - Assert.Equal(full_uri, str3); - } - - [Fact] - public void TestNulls() - { - TestObject.StringProperty = null; - Assert.Equal("", TestObject.StringProperty); - TestObject.CallForString(() => null); - TestObject.StringPropertyChanged += - (Class sender, string value) => Assert.Equal("", value); - TestObject.RaiseStringChanged(); - - TestObject.UriProperty = null; - Assert.Null(TestObject.UriProperty); - TestObject.CallForUri(() => null); - TestObject.UriPropertyChanged += - (object sender, Uri value) => Assert.Null(value); - TestObject.RaiseUriChanged(); - - TestObject.ObjectProperty = null; - Assert.Null(TestObject.ObjectProperty); - TestObject.CallForObject(() => null); - TestObject.ObjectPropertyChanged += - (object sender, Object value) => Assert.Null(value); - TestObject.RaiseObjectChanged(); - - // todo: arrays, delegates, event args, mapped types... - } - - [Fact] - public void TestEvents() - { - int events_expected = 0; - int events_received = 0; - - TestObject.Event0 += () => events_received++; - TestObject.InvokeEvent0(); - events_expected++; - - TestObject.Event1 += (Class sender) => - { - events_received++; - Assert.IsAssignableFrom(sender); - }; - TestObject.InvokeEvent1(TestObject); - events_expected++; - - int int0 = 42; - TestObject.Event2 += (Class sender, int arg0) => - { - events_received++; - Assert.Equal(arg0, int0); - }; - TestObject.InvokeEvent2(TestObject, int0); - events_expected++; - - string string1 = "foo"; - TestObject.Event3 += (Class sender, int arg0, string arg1) => - { - events_received++; - Assert.Equal(arg1, string1); - }; - TestObject.InvokeEvent3(TestObject, int0, string1); - events_expected++; - - int[] ints = { 1, 2, 3 }; - TestObject.NestedEvent += (object sender, IList arg0) => - { - events_received++; - Assert.True(arg0.SequenceEqual(ints)); - }; - TestObject.InvokeNestedEvent(TestObject, ints); - events_expected++; - - TestObject.ReturnEvent += (int arg0) => - { - events_received++; - return arg0; - }; - Assert.Equal(42, TestObject.InvokeReturnEvent(42)); - events_expected++; - - var collection0 = new int[] { 42, 1729 }; - var collection1 = new Dictionary { [1] = "foo", [2] = "bar" }; - TestObject.CollectionEvent += (Class sender, IList arg0, IDictionary arg1) => - { - events_received++; - Assert.True(arg0.SequenceEqual(collection0)); - Assert.True(arg1.SequenceEqual(collection1)); - }; - TestObject.InvokeCollectionEvent(TestObject, collection0, collection1); - events_expected++; - - Assert.Equal(events_received, events_expected); - } - - class ManagedUriHandler : IUriHandler - { - public Class TestObject { get; private set; } - - public ManagedUriHandler(Class testObject) - { - TestObject = testObject; - } - - public void AddUriHandler(ProvideUri provideUri) - { - TestObject.CallForUri(provideUri); - Assert.Equal(new Uri("http://github.com"), TestObject.UriProperty); - } - } - - [Fact] - public void TestDelegateUnwrapping() - { - var obj = TestObject.GetUriDelegate(); - TestObject.CallForUri(obj); - Assert.Equal(new Uri("http://microsoft.com"), TestObject.UriProperty); - - TestObject.AddUriHandler(new ManagedUriHandler(TestObject)); - } - - // TODO: when the public WinUI nuget supports IXamlServiceProvider, just use the projection - [ComImport] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [Guid("68B3A2DF-8173-539F-B524-C8A2348F5AFB")] - internal unsafe interface IServiceProviderInterop - { - // Note: Invoking methods on ComInterfaceType.InterfaceIsIInspectable interfaces - // no longer appears supported in the runtime (probably with removal of WinRT support), - // so simulate with IUnknown. - void GetIids(out int iidCount, out IntPtr iids); - void GetRuntimeClassName(out IntPtr className); - void GetTrustLevel(out TrustLevel trustLevel); - - void GetService(IntPtr type, out IntPtr service); - } - - [Fact] - public void TestCustomProjections() - { - // INotifyDataErrorsInfo - string propertyName = ""; - TestObject.ErrorsChanged += (object sender, System.ComponentModel.DataErrorsChangedEventArgs e) => - { - propertyName = e.PropertyName; - }; - TestObject.RaiseDataErrorChanged(); - Assert.Equal("name", propertyName); - - bool eventCalled = false; - TestObject.CanExecuteChanged += (object sender, EventArgs e) => + + static void TestObject() => MakeObject(); + + static (IInitializeWithWindow, IWindowNative) MakeImports() { - eventCalled = true; - }; - - TestObject.RaiseCanExecuteChanged(); - Assert.True(eventCalled); - - // IXamlServiceProvider <-> IServiceProvider - var serviceProvider = Class.ServiceProvider.As(); - IntPtr service; - serviceProvider.GetService(IntPtr.Zero, out service); - Assert.Equal(new IntPtr(42), service); - - // Ensure robustness with bad runtime class names (parsing errors, type not found, etc) - var badRuntimeClassName = Class.BadRuntimeClassName; - Assert.NotNull(badRuntimeClassName); - } - - [Fact] - public void TestKeyValuePair() - { - var expected = new KeyValuePair("key", "value"); - TestObject.StringPairProperty = expected; - Assert.Equal(expected, TestObject.StringPairProperty); - - expected = new KeyValuePair("foo", "bar"); - TestObject.CallForStringPair(() => expected); - TestObject.StringPairPropertyChanged += - (object sender, KeyValuePair value) => Assert.Equal(expected, value); - TestObject.RaiseStringPairChanged(); - } - - [Fact] - public void TestObjectCasting() - { - object expected_uri = new Uri("http://aka.ms/cswinrt"); - TestObject.ObjectProperty = expected_uri; - Assert.Equal(expected_uri, TestObject.UriProperty); - Assert.Equal(expected_uri, TestObject.ObjectProperty); - - var expected = new KeyValuePair("key", "value"); - TestObject.ObjectProperty = expected; - var out_pair = (KeyValuePair)TestObject.ObjectProperty; - Assert.Equal(expected, out_pair); - - var nested = new KeyValuePair, KeyValuePair>( - new KeyValuePair(42, 1729), - new KeyValuePair("key", "value") - ); - TestObject.ObjectProperty = nested; - var out_nested = (KeyValuePair, KeyValuePair>)TestObject.ObjectProperty; - Assert.Equal(nested, out_nested); - - var strings_in = new[] { "hello", "world" }; - TestObject.StringsProperty = strings_in; - var strings_out = TestObject.StringsProperty; - Assert.True(strings_out.SequenceEqual(strings_in)); - - TestObject.ObjectProperty = strings_in; - strings_out = (string[])TestObject.ObjectProperty; - Assert.True(strings_out.SequenceEqual(strings_in)); - - var objects = new List() { new ManagedType(), new ManagedType() }; - var query = from item in objects select item; - TestObject.ObjectIterableProperty = query; - - TestObject.ObjectProperty = "test"; - Assert.Equal("test", TestObject.ObjectProperty); - - var objectArray = new ManagedType[] { new ManagedType(), new ManagedType() }; - TestObject.ObjectIterableProperty = objectArray; - Assert.True(TestObject.ObjectIterableProperty.SequenceEqual(objectArray)); - - var strArray = new string[] { "str1", "str2", "str3" }; - TestObject.ObjectIterableProperty = strArray; - Assert.True(TestObject.ObjectIterableProperty.SequenceEqual(strArray)); - - var uriArray = new Uri[] { new Uri("http://aka.ms/cswinrt"), new Uri("http://github.com") }; - TestObject.ObjectIterableProperty = uriArray; - Assert.True(TestObject.ObjectIterableProperty.SequenceEqual(uriArray)); - - var objectUriArray = new object[] { new Uri("http://github.com") }; - TestObject.ObjectIterableProperty = objectUriArray; - Assert.True(TestObject.ObjectIterableProperty.SequenceEqual(objectUriArray)); - } - - [Fact] - public void TestStringMap() - { - var map = new Dictionary { ["foo"] = "bar", ["hello"] = "world" }; - var stringMap = new Windows.Foundation.Collections.StringMap(); - foreach (var item in map) - { - stringMap[item.Key] = item.Value; - } - Assert.Equal(map.Count, stringMap.Count); - foreach (var item in map) - { - Assert.Equal(stringMap[item.Key], item.Value); - } - KeyValuePair[] pairs = new KeyValuePair[2]; - stringMap.CopyTo(pairs, 0); - Assert.Equal(2, pairs.Length); - } - - [Fact] - public void TestPropertySet() - { - var map = new Dictionary { ["foo"] = "bar", ["hello"] = "world" }; - var propertySet = new Windows.Foundation.Collections.PropertySet(); - foreach (var item in map) - { - propertySet[item.Key] = item.Value; - } - Assert.Equal(map.Count, propertySet.Count); - foreach (var item in map) - { - Assert.Equal(propertySet[item.Key], item.Value); - } - } - - [Fact] - public void TestValueSet() - { - var map = new Dictionary { ["foo"] = "bar", ["hello"] = "world" }; - var valueSet = new Windows.Foundation.Collections.ValueSet(); - foreach (var item in map) - { - valueSet[item.Key] = item.Value; - } - Assert.Equal(map.Count, valueSet.Count); - foreach (var item in map) - { - Assert.Equal(valueSet[item.Key], item.Value); - } - } - - [Fact] - public void TestValueSetArrays() - { - var map = new Dictionary - { - ["foo"] = new long[] { 1, 2, 3 }, - ["hello"] = new long[0], - ["world"] = new long[] { 1, 2, 3 }, - ["bar"] = new long[0] - }; - var valueSet = new Windows.Foundation.Collections.ValueSet(); - foreach (var item in map) - { - valueSet[item.Key] = item.Value; - } - Assert.Equal(map.Count, valueSet.Count); - foreach (var item in map) - { - Assert.Equal(valueSet[item.Key], item.Value); - } - } - - [Fact] - public void TestFactories() - { - var cls1 = new Class(); - - var cls2 = new Class(42); - Assert.Equal(42, cls2.IntProperty); - - var cls3 = new Class(42, "foo"); - Assert.Equal(42, cls3.IntProperty); - Assert.Equal("foo", cls3.StringProperty); - } - - [Fact] - public void TestStaticMembers() - { - Class.StaticIntProperty = 42; - Assert.Equal(42, Class.StaticIntProperty); - - Class.StaticStringProperty = "foo"; - Assert.Equal("foo", Class.StaticStringProperty); - } - - [Fact] - public void TestInterfaces() - { - var expected = "hello"; - TestObject.StringProperty = expected; - - // projected wrapper - Assert.Equal(expected, TestObject.ToString()); - - // implicit cast - var str = (IStringable)TestObject; - Assert.Equal(expected, str.ToString()); - - var str2 = TestObject as IStringable; - Assert.Equal(expected, str2.ToString()); - - Assert.IsAssignableFrom(TestObject); - } - - [Fact] - public void TestAsync() - { - TestObject.IntProperty = 42; - var async_get_int = TestObject.GetIntAsync(); - int async_int = 0; - async_get_int.Completed = (info, status) => async_int = info.GetResults(); - async_get_int.GetResults(); - Assert.Equal(42, async_int); - - TestObject.StringProperty = "foo"; - var async_get_string = TestObject.GetStringAsync(); - string async_string = ""; - async_get_string.Completed = (info, status) => async_string = info.GetResults(); - int async_progress; - async_get_string.Progress = (info, progress) => async_progress = progress; - async_get_string.GetResults(); - Assert.Equal("foo", async_string); - } - - [Fact] - public void TestPrimitives() - { - var test_int = 21; - TestObject.IntPropertyChanged += (object sender, Int32 value) => - { - Assert.IsAssignableFrom(sender); - var c = (Class)sender; - Assert.Equal(value, test_int); - }; - TestObject.IntProperty = test_int; - - var expectedVal = true; - var hits = 0; - TestObject.BoolPropertyChanged += (object sender, bool value) => - { - Assert.Equal(expectedVal, value); - ++hits; - }; - - TestObject.BoolProperty = true; - Assert.Equal(1, hits); - - expectedVal = false; - TestObject.CallForBool(() => false); - Assert.Equal(2, hits); - - TestObject.RaiseBoolChanged(); - Assert.Equal(3, hits); - } - - [Fact] - public void TestStrings() - { - string test_string = "x"; - string test_string2 = "y"; - - // In hstring from managed->native implicitly creates hstring reference - TestObject.StringProperty = test_string; - - // Out hstring from native->managed only creates System.String on demand - var sp = TestObject.StringProperty; - Assert.Equal(sp, test_string); - - // Out hstring from managed->native always creates HString from System.String - TestObject.CallForString(() => test_string2); - Assert.Equal(TestObject.StringProperty, test_string2); - - // In hstring from native->managed only creates System.String on demand - TestObject.StringPropertyChanged += (Class sender, string value) => sender.StringProperty2 = value; - TestObject.RaiseStringChanged(); - Assert.Equal(TestObject.StringProperty2, test_string2); - } - - [Fact] - public void TestBlittableStruct() - { - // Property setter/getter - var val = new BlittableStruct() { i32 = 42 }; - TestObject.BlittableStructProperty = val; - Assert.Equal(42, TestObject.BlittableStructProperty.i32); - - // Manual getter - Assert.Equal(42, TestObject.GetBlittableStruct().i32); - - // Manual setter - val.i32 = 8; - TestObject.SetBlittableStruct(val); - Assert.Equal(8, TestObject.BlittableStructProperty.i32); - - // Output argument - val = default; - TestObject.OutBlittableStruct(out val); - Assert.Equal(8, val.i32); - } - - [Fact] - public void TestComposedBlittableStruct() - { - // Property setter/getter - var val = new ComposedBlittableStruct() { blittable = new BlittableStruct() { i32 = 42 } }; - TestObject.ComposedBlittableStructProperty = val; - Assert.Equal(42, TestObject.ComposedBlittableStructProperty.blittable.i32); - - // Manual getter - Assert.Equal(42, TestObject.GetComposedBlittableStruct().blittable.i32); - - // Manual setter - val.blittable.i32 = 8; - TestObject.SetComposedBlittableStruct(val); - Assert.Equal(8, TestObject.ComposedBlittableStructProperty.blittable.i32); - - // Output argument - val = default; - TestObject.OutComposedBlittableStruct(out val); - Assert.Equal(8, val.blittable.i32); - } - - [Fact] - public void TestNonBlittableStringStruct() - { - // Property getter/setter - var val = new NonBlittableStringStruct() { str = "I like tacos" }; - TestObject.NonBlittableStringStructProperty = val; - Assert.Equal("I like tacos", TestObject.NonBlittableStringStructProperty.str.ToString()); - - // Manual getter - Assert.Equal("I like tacos", TestObject.GetNonBlittableStringStruct().str.ToString()); - - // Manual setter - val.str = "Hello, world"; - TestObject.SetNonBlittableStringStruct(val); - Assert.Equal("Hello, world", TestObject.NonBlittableStringStructProperty.str.ToString()); - - // Output argument - val = default; - TestObject.OutNonBlittableStringStruct(out val); - Assert.Equal("Hello, world", val.str.ToString()); - } - - [Fact] - public void TestNonBlittableBoolStruct() - { - // Property getter/setter - var val = new NonBlittableBoolStruct() { w = true, x = false, y = true, z = false }; - TestObject.NonBlittableBoolStructProperty = val; - Assert.True(TestObject.NonBlittableBoolStructProperty.w); - Assert.False(TestObject.NonBlittableBoolStructProperty.x); - Assert.True(TestObject.NonBlittableBoolStructProperty.y); - Assert.False(TestObject.NonBlittableBoolStructProperty.z); - - // Manual getter - Assert.True(TestObject.GetNonBlittableBoolStruct().w); - Assert.False(TestObject.GetNonBlittableBoolStruct().x); - Assert.True(TestObject.GetNonBlittableBoolStruct().y); - Assert.False(TestObject.GetNonBlittableBoolStruct().z); - - // Manual setter - val.w = false; - val.x = true; - val.y = false; - val.z = true; - TestObject.SetNonBlittableBoolStruct(val); - Assert.False(TestObject.NonBlittableBoolStructProperty.w); - Assert.True(TestObject.NonBlittableBoolStructProperty.x); - Assert.False(TestObject.NonBlittableBoolStructProperty.y); - Assert.True(TestObject.NonBlittableBoolStructProperty.z); - - // Output argument - val = default; - TestObject.OutNonBlittableBoolStruct(out val); - Assert.False(val.w); - Assert.True(val.x); - Assert.False(val.y); - Assert.True(val.z); - } - - [Fact] - public void TestNonBlittableRefStruct() - { - // Property getter/setter - // TODO: Need to either support interface inheritance or project IReference/INullable for setter - Assert.Equal(42, TestObject.NonBlittableRefStructProperty.ref32.Value); - - // Manual getter - Assert.Equal(42, TestObject.GetNonBlittableRefStruct().ref32.Value); - - // TODO: Manual setter - - // Output argument - NonBlittableRefStruct val; - TestObject.OutNonBlittableRefStruct(out val); - Assert.Equal(42, val.ref32.Value); - } - - [Fact] - public void TestComposedNonBlittableStruct() - { - // Property getter/setter - var val = new ComposedNonBlittableStruct() - { - blittable = new BlittableStruct() { i32 = 42 }, - strings = new NonBlittableStringStruct() { str = "I like tacos" }, - bools = new NonBlittableBoolStruct() { w = true, x = false, y = true, z = false }, - refs = TestObject.NonBlittableRefStructProperty // TODO: Need to either support interface inheritance or project IReference/INullable for setter - }; - TestObject.ComposedNonBlittableStructProperty = val; - Assert.Equal(42, TestObject.ComposedNonBlittableStructProperty.blittable.i32); - Assert.Equal("I like tacos", TestObject.ComposedNonBlittableStructProperty.strings.str); - Assert.True(TestObject.ComposedNonBlittableStructProperty.bools.w); - Assert.False(TestObject.ComposedNonBlittableStructProperty.bools.x); - Assert.True(TestObject.ComposedNonBlittableStructProperty.bools.y); - Assert.False(TestObject.ComposedNonBlittableStructProperty.bools.z); - - // Manual getter - Assert.Equal(42, TestObject.GetComposedNonBlittableStruct().blittable.i32); - Assert.Equal("I like tacos", TestObject.GetComposedNonBlittableStruct().strings.str); - Assert.True(TestObject.GetComposedNonBlittableStruct().bools.w); - Assert.False(TestObject.GetComposedNonBlittableStruct().bools.x); - Assert.True(TestObject.GetComposedNonBlittableStruct().bools.y); - Assert.False(TestObject.GetComposedNonBlittableStruct().bools.z); - - // Manual setter - val.blittable.i32 = 8; - val.strings.str = "Hello, world"; - val.bools.w = false; - val.bools.x = true; - val.bools.y = false; - val.bools.z = true; - TestObject.SetComposedNonBlittableStruct(val); - Assert.Equal(8, TestObject.ComposedNonBlittableStructProperty.blittable.i32); - Assert.Equal("Hello, world", TestObject.ComposedNonBlittableStructProperty.strings.str); - Assert.False(TestObject.ComposedNonBlittableStructProperty.bools.w); - Assert.True(TestObject.ComposedNonBlittableStructProperty.bools.x); - Assert.False(TestObject.ComposedNonBlittableStructProperty.bools.y); - Assert.True(TestObject.ComposedNonBlittableStructProperty.bools.z); - - // Output argument - val = default; - TestObject.OutComposedNonBlittableStruct(out val); - Assert.Equal(8, val.blittable.i32); - Assert.Equal("Hello, world", val.strings.str); - Assert.False(val.bools.w); - Assert.True(val.bools.x); - Assert.False(val.bools.y); - Assert.True(val.bools.z); - } - - [Fact] - public void TestBlittableArrays() + var obj = MakeObject(); + var initializeWithWindow = obj.As(); + var windowNative = obj.As(); + return (initializeWithWindow, windowNative); + } + + static void TestImports() + { + var (initializeWithWindow, windowNative) = MakeImports(); + + GC.Collect(); + GC.WaitForPendingFinalizers(); + + var hwnd = new IntPtr(0x12345678); + initializeWithWindow.Initialize(hwnd); + Assert.Equal(windowNative.WindowHandle, hwnd); + } + + TestObject(); + GC.Collect(); + GC.WaitForPendingFinalizers(); + Assert.Equal(0, ComImports.NumObjects); + + TestImports(); + GC.Collect(); + GC.WaitForPendingFinalizers(); + Assert.Equal(0, ComImports.NumObjects); + } + + [Fact] + public void TestInterfaceObjectMarshalling() { - int[] arr = new[] { 2, 4, 6, 8 }; - TestObject.SetInts(arr); - Assert.True(TestObject.GetInts().SequenceEqual(arr)); + var nativeProperties = Class.NativeProperties1; - TestObject.SetInts(null); - Assert.Null(TestObject.GetInts()); - } - -#if NETCOREAPP2_0 - [Fact] - public void TestGenericCast() - { - var ints = TestObject.GetIntVector(); - var abiView = (ABI.System.Collections.Generic.IReadOnlyList)ints; - Assert.Equal(abiView.ThisPtr, abiView.As().As.Vftbl>().ThisPtr); - } -#endif - - [ComImport] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [Guid("96369F54-8EB6-48F0-ABCE-C1B211E627C3")] - internal unsafe interface IStringableInterop - { - // Note: Invoking methods on ComInterfaceType.InterfaceIsIInspectable interfaces - // no longer appears supported in the runtime (probably with removal of WinRT support), - // so simulate with IUnknown. - void GetIids(out int iidCount, out IntPtr iids); - void GetRuntimeClassName(out IntPtr className); - void GetTrustLevel(out TrustLevel trustLevel); - - void ToString(out IntPtr hstr); - } - - [Fact] - public unsafe void TestFactoryCast() - { - IntPtr hstr; - - // Access nonstatic class factory - var instanceFactory = Class.As(); - instanceFactory.ToString(out hstr); - Assert.Equal("Class", MarshalString.FromAbi(hstr)); - - // Access static class factory - var staticFactory = ComImports.As(); - staticFactory.ToString(out hstr); - Assert.Equal("ComImports", MarshalString.FromAbi(hstr)); - } - - [Fact] - public void TestFundamentalGeneric() - { - var ints = TestObject.GetIntVector(); - Assert.Equal(10, ints.Count); - for (int i = 0; i < 10; ++i) - { - Assert.Equal(i, ints[i]); - } - - var bools = TestObject.GetBoolVector(); - Assert.Equal(4, bools.Count); - for (int i = 0; i < 4; ++i) - { - Assert.Equal(i % 2 == 0, bools[i]); - } - } - - [Fact] - public void TestStringGeneric() - { - var strings = TestObject.GetStringVector(); - Assert.Equal(5, strings.Count); - for (int i = 0; i < 5; ++i) - { - Assert.Equal("String" + i, strings[i]); - } - } - - [Fact] - public void TestStructGeneric() - { - var blittable = TestObject.GetBlittableStructVector(); - Assert.Equal(5, blittable.Count); - for (int i = 0; i < 5; ++i) - { - Assert.Equal(i, blittable[i].blittable.i32); - } - - var nonblittable = TestObject.GetNonBlittableStructVector(); - Assert.Equal(3, nonblittable.Count); - for (int i = 0; i < 3; ++i) - { - var val = nonblittable[i]; - Assert.Equal(i, val.blittable.i32); - Assert.Equal("String" + i, val.strings.str); - Assert.Equal(i % 2 == 0, val.bools.w); - Assert.Equal(i % 2 == 1, val.bools.x); - Assert.Equal(i % 2 == 0, val.bools.y); - Assert.Equal(i % 2 == 1, val.bools.z); - Assert.Equal(i, val.refs.ref32.Value); - } - } - - [Fact] - public void TestValueUnboxing() - { - var objs = TestObject.GetObjectVector(); - Assert.Equal(3, objs.Count); - for (int i = 0; i < 3; ++i) - { - Assert.Equal(i, (int)objs[i]); - } - } - - [Fact] - void TestInterfaceGeneric() - { - var objs = TestObject.GetInterfaceVector(); - Assert.Equal(3, objs.Count); - TestObject.ReadWriteProperty = 42; - for (int i = 0; i < 3; ++i) - { - var obj = objs[i]; - Assert.Same(obj, TestObject); - Assert.Equal(42, obj.ReadWriteProperty); - } - } - - [Fact] - public void TestIterable() - { - var ints_in = new int[] { 0, 1, 2 }; - TestObject.SetIntIterable(ints_in); - var ints_out = TestObject.GetIntIterable(); - Assert.True(ints_in.SequenceEqual(ints_out)); - } - - class ManagedBindableObservable : IBindableObservableVector - { - private IList _list; - - public class TObservation : IProperties2 - { - private int _value = 0; - - public int ReadWriteProperty { get => _value; set => _value = value; } - - int IProperties1.ReadWriteProperty => ReadWriteProperty; - } - TObservation _observation; - - public int Observation { get => _observation.ReadWriteProperty; } - - public ManagedBindableObservable(IList list) => _list = new ArrayList(list); - - private void OnChanged() - { - VectorChanged.Invoke(this, _observation = new TObservation()); - } - - public event BindableVectorChangedEventHandler VectorChanged; - - public object this[int index] - { - get => _list[index]; - set { _list[index] = value; OnChanged(); } - } - - public bool IsFixedSize => false; - - public bool IsReadOnly => false; - - public int Count => _list.Count; - - public bool IsSynchronized => _list.IsSynchronized; - - public object SyncRoot => _list; - - public int Add(object value) - { - var result = _list.Add(value); - OnChanged(); - return result; - } - - public void Clear() - { - _list.Clear(); - OnChanged(); - } - - public bool Contains(object value) => _list.Contains(value); - - public void CopyTo(Array array, int index) => _list.CopyTo(array, index); - - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - - public int IndexOf(object value) => _list.IndexOf(value); - - public void Insert(int index, object value) - { - _list.Insert(index, value); - OnChanged(); - } - - public void Remove(object value) - { - _list.Remove(value); - OnChanged(); - } - - public void RemoveAt(int index) - { - _list.RemoveAt(index); - OnChanged(); - } - } - - [Fact] - public void TestBindable() - { - var expected = new int[] { 0, 1, 2 }; - - TestObject.BindableIterableProperty = expected; - Assert.Equal(expected, TestObject.BindableIterableProperty); - TestObject.CallForBindableIterable(() => expected); - TestObject.BindableIterablePropertyChanged += - (object sender, IEnumerable value) => Assert.Equal(expected, value); - TestObject.RaiseBindableIterableChanged(); - - TestObject.BindableVectorProperty = expected; - Assert.Equal(expected, TestObject.BindableVectorProperty); - TestObject.CallForBindableVector(() => expected); - TestObject.BindableVectorPropertyChanged += - (object sender, IList value) => Assert.Equal(expected, value); - TestObject.RaiseBindableVectorChanged(); - - var observable = new ManagedBindableObservable(expected); - TestObject.BindableObservableVectorProperty = observable; - observable.Add(3); - Assert.Equal(6, observable.Observation); - } - - [Fact] - public void TestClassGeneric() - { - var objs = TestObject.GetClassVector(); - Assert.Equal(3, objs.Count); - for (int i = 0; i < 3; ++i) - { - var obj = objs[i]; - Assert.Same(obj, TestObject); - Assert.Equal(TestObject, objs[i]); - } - } - - [Fact] - public void TestSimpleCCWs() - { - var managedProperties = new ManagedProperties(42); - TestObject.CopyProperties(managedProperties); - Assert.Equal(managedProperties.ReadWriteProperty, TestObject.ReadWriteProperty); - } - - [Fact] - public void TestCCWMarshaler() - { - Guid IID_IMarshal = new Guid("00000003-0000-0000-c000-000000000046"); - var managedProperties = new ManagedProperties(42); - IObjectReference ccw = MarshalInterface.CreateMarshaler(managedProperties); - ccw.TryAs(IID_IMarshal, out var marshalCCW); - Assert.NotNull(marshalCCW); - - var array = new byte[] { 0x01 }; - var buff = array.AsBuffer(); - IObjectReference ccw2 = MarshalInterface.CreateMarshaler(buff); - ccw2.TryAs(IID_IMarshal, out var marshalCCW2); - Assert.NotNull(marshalCCW2); - } - -#if NET - [Fact] - public void TestDelegateCCWMarshaler() - { - CreateAndValidateStreamedFile().Wait(); - } - - private async Task CreateAndValidateStreamedFile() - { - var storageFile = await StorageFile.CreateStreamedFileAsync("CreateAndValidateStreamedFile.txt", StreamedFileWriter, null); - using var inputStream = await storageFile.OpenSequentialReadAsync(); - using var stream = inputStream.AsStreamForRead(); - byte[] buff = new byte[50]; - var numRead = stream.Read(buff, 0, 50); - Assert.True(numRead > 0); - var result = System.Text.Encoding.Default.GetString(buff, 0, numRead).TrimEnd(null); - Assert.Equal("Success!", result); - } - - private static async void StreamedFileWriter(StreamedFileDataRequest request) - { - try - { - using (var stream = request.AsStreamForWrite()) - using (var streamWriter = new StreamWriter(stream)) - { - await streamWriter.WriteLineAsync("Success!"); - } - request.Dispose(); - } - catch (Exception) - { - request.FailAndClose(StreamedFileFailureMode.Incomplete); - } - } -#endif - - [Fact] - public void TestWeakReference() - { - var managedProperties = new ManagedProperties(42); - TestObject.CopyPropertiesViaWeakReference(managedProperties); - Assert.Equal(managedProperties.ReadWriteProperty, TestObject.ReadWriteProperty); - } - - [Fact] - public void TestCCWIdentity() - { - var managedProperties = new ManagedProperties(42); - IObjectReference ccw1 = MarshalInterface.CreateMarshaler(managedProperties); - IObjectReference ccw2 = MarshalInterface.CreateMarshaler(managedProperties); - Assert.Equal(ccw1.ThisPtr, ccw2.ThisPtr); - } - - [Fact] - public void TestInterfaceCCWLifetime() - { - static (WeakReference, IObjectReference) CreateCCW() - { - var managedProperties = new ManagedProperties(42); - IObjectReference ccw1 = MarshalInterface.CreateMarshaler(managedProperties); - return (new WeakReference(managedProperties), ccw1); - } - - static (WeakReference obj, WeakReference ccw) GetWeakReferenceToObjectAndCCW() - { - var (reference, ccw) = CreateCCW(); - - GC.Collect(); - GC.WaitForPendingFinalizers(); - - Assert.True(reference.IsAlive); - return (reference, new WeakReference(ccw)); - } - - var (obj, ccw) = GetWeakReferenceToObjectAndCCW(); - - while (ccw.IsAlive) - { - GC.Collect(); - GC.WaitForPendingFinalizers(); - } - - // Now that the CCW is dead, we should have no references to the managed object. - // Run GC one more time to collect the managed object. - GC.Collect(); - GC.WaitForPendingFinalizers(); - Assert.False(obj.IsAlive); - } - - [Fact] - public void TestDelegateCCWLifetime() - { - static (WeakReference, IObjectReference) CreateCCW(Action action) - { - TypedEventHandler eventHandler = (o, i) => action(o, i); - IObjectReference ccw1 = ABI.Windows.Foundation.TypedEventHandler.CreateMarshaler(eventHandler); - return (new WeakReference(eventHandler), ccw1); - } - - static (WeakReference obj, WeakReference ccw) GetWeakReferenceToObjectAndCCW(Action action) - { - var (reference, ccw) = CreateCCW(action); - - GC.Collect(); - GC.WaitForPendingFinalizers(); - - Assert.True(reference.IsAlive); - return (reference, new WeakReference(ccw)); - } - - var (obj, ccw) = GetWeakReferenceToObjectAndCCW((o, i) => { }); - - while (ccw.IsAlive) - { - GC.Collect(); - GC.WaitForPendingFinalizers(); - } - - // Now that the CCW is dead, we should have no references to the managed object. - // Run GC one more time to collect the managed object. - GC.Collect(); - GC.WaitForPendingFinalizers(); - Assert.False(obj.IsAlive); - } - - [Fact] - public void TestCCWIdentityThroughRefCountZero() - { - static (WeakReference, IntPtr) CreateCCWReference(IProperties1 properties) - { - IObjectReference ccw = MarshalInterface.CreateMarshaler(properties); - return (new WeakReference(ccw), ccw.ThisPtr); - } - - var obj = new ManagedProperties(42); - - var (ccwWeakReference, ptr) = CreateCCWReference(obj); - - GC.Collect(); - GC.WaitForPendingFinalizers(); - - Assert.False(ccwWeakReference.IsAlive); - - var (_, ptr2) = CreateCCWReference(obj); - - Assert.Equal(ptr, ptr2); - } - - [Fact()] - public void TestExceptionPropagation_Managed() - { - var exceptionToThrow = new ArgumentNullException("foo"); - var properties = new ThrowingManagedProperties(exceptionToThrow); - Assert.Throws("foo", () => TestObject.CopyProperties(properties)); - } - - class ManagedProperties : IProperties1 - { - private readonly int _value; - - public ManagedProperties(int value) - { - _value = value; - } - public int ReadWriteProperty => _value; - } - - class ThrowingManagedProperties : IProperties1 - { - public ThrowingManagedProperties(Exception exceptionToThrow) - { - ExceptionToThrow = exceptionToThrow; - } - - public Exception ExceptionToThrow { get; } - - public int ReadWriteProperty => throw ExceptionToThrow; - } - - readonly int E_FAIL = -2147467259; - - async Task InvokeDoitAsync() - { - await TestObject.DoitAsync(); - } - - [Fact] - public void TestAsyncAction() - { - var task = InvokeDoitAsync(); - Assert.False(task.Wait(25)); - TestObject.CompleteAsync(); - Assert.True(task.Wait(1000)); - Assert.Equal(TaskStatus.RanToCompletion, task.Status); - - task = InvokeDoitAsync(); - Assert.False(task.Wait(25)); - TestObject.CompleteAsync(E_FAIL); - var e = Assert.Throws(() => task.Wait(1000)); - Assert.Equal(E_FAIL, e.InnerException.HResult); - Assert.Equal(TaskStatus.Faulted, task.Status); - - var src = new CancellationTokenSource(); - task = TestObject.DoitAsync().AsTask(src.Token); - Assert.False(task.Wait(25)); - src.Cancel(); - e = Assert.Throws(() => task.Wait(1000)); - Assert.True(e.InnerException is TaskCanceledException); - Assert.Equal(TaskStatus.Canceled, task.Status); - } - - async Task InvokeDoitAsyncWithProgress() - { - await TestObject.DoitAsyncWithProgress(); - } - - [Fact] - public void TestAsyncActionWithProgress() - { - int progress = 0; - var evt = new AutoResetEvent(false); - var task = TestObject.DoitAsyncWithProgress().AsTask(new Progress((v) => - { - progress = v; - evt.Set(); - })); - - for (int i = 1; i <= 10; ++i) - { - TestObject.AdvanceAsync(10); - Assert.True(evt.WaitOne(1000)); - Assert.Equal(10 * i, progress); - } - - TestObject.CompleteAsync(); - Assert.True(task.Wait(1000)); - Assert.Equal(TaskStatus.RanToCompletion, task.Status); - - task = InvokeDoitAsyncWithProgress(); - TestObject.CompleteAsync(E_FAIL); - var e = Assert.Throws(() => task.Wait(1000)); - Assert.Equal(E_FAIL, e.InnerException.HResult); - Assert.Equal(TaskStatus.Faulted, task.Status); - - var src = new CancellationTokenSource(); - task = TestObject.DoitAsyncWithProgress().AsTask(src.Token); - Assert.False(task.Wait(25)); - src.Cancel(); - e = Assert.Throws(() => task.Wait(1000)); - Assert.True(e.InnerException is TaskCanceledException); - Assert.Equal(TaskStatus.Canceled, task.Status); - } - - async Task InvokeAddAsync(int lhs, int rhs) - { - return await TestObject.AddAsync(lhs, rhs); - } - - [Fact] - public void TestAsyncOperation() - { - var task = InvokeAddAsync(42, 8); - Assert.False(task.Wait(25)); - TestObject.CompleteAsync(); - Assert.True(task.Wait(1000)); - Assert.Equal(TaskStatus.RanToCompletion, task.Status); - Assert.Equal(50, task.Result); - - task = InvokeAddAsync(0, 0); - Assert.False(task.Wait(25)); - TestObject.CompleteAsync(E_FAIL); - var e = Assert.Throws(() => task.Wait(1000)); - Assert.Equal(E_FAIL, e.InnerException.HResult); - Assert.Equal(TaskStatus.Faulted, task.Status); - - var src = new CancellationTokenSource(); - task = TestObject.AddAsync(0, 0).AsTask(src.Token); - Assert.False(task.Wait(25)); - src.Cancel(); - e = Assert.Throws(() => task.Wait(1000)); - Assert.True(e.InnerException is TaskCanceledException); - Assert.Equal(TaskStatus.Canceled, task.Status); - } - - async Task InvokeAddAsyncWithProgress(int lhs, int rhs) - { - return await TestObject.AddAsyncWithProgress(lhs, rhs); - } - - [Fact] - public void TestAsyncOperationWithProgress() - { - int progress = 0; - var evt = new AutoResetEvent(false); - var task = TestObject.AddAsyncWithProgress(42, 8).AsTask(new Progress((v) => - { - progress = v; - evt.Set(); - })); - - for (int i = 1; i <= 10; ++i) - { - TestObject.AdvanceAsync(10); - Assert.True(evt.WaitOne(1000)); - Assert.Equal(10 * i, progress); - } - - TestObject.CompleteAsync(); - Assert.True(task.Wait(1000)); - Assert.Equal(TaskStatus.RanToCompletion, task.Status); - Assert.Equal(50, task.Result); - - task = InvokeAddAsyncWithProgress(0, 0); - TestObject.CompleteAsync(E_FAIL); - var e = Assert.Throws(() => task.Wait(1000)); - Assert.Equal(E_FAIL, e.InnerException.HResult); - Assert.Equal(TaskStatus.Faulted, task.Status); - - var src = new CancellationTokenSource(); - task = TestObject.AddAsyncWithProgress(0, 0).AsTask(src.Token); - Assert.False(task.Wait(25)); - src.Cancel(); - e = Assert.Throws(() => task.Wait(1000)); - Assert.True(e.InnerException is TaskCanceledException); - Assert.Equal(TaskStatus.Canceled, task.Status); - } - - [Fact] - public void TestPointTypeMapping() - { - var pt = new Point { X = 3.14, Y = 42 }; - TestObject.PointProperty = pt; - Assert.Equal(pt.X, TestObject.PointProperty.X); - Assert.Equal(pt.Y, TestObject.PointProperty.Y); - Assert.True(TestObject.PointProperty == pt); - Assert.Equal(pt, TestObject.GetPointReference().Value); - - var vector2 = TestObject.PointProperty.ToVector2(); - Assert.Equal(pt.X, vector2.X); - Assert.Equal(pt.Y, vector2.Y); - - TestObject.PointProperty = vector2.ToPoint(); - Assert.Equal(pt.X, TestObject.PointProperty.X); - Assert.Equal(pt.Y, TestObject.PointProperty.Y); - } - - [Fact] - public void TestRectTypeMapping() - { - var rect = new Rect { X = 3.14, Y = 42, Height = 3.14, Width = 42 }; - TestObject.RectProperty = rect; - Assert.Equal(rect.X, TestObject.RectProperty.X); - Assert.Equal(rect.Y, TestObject.RectProperty.Y); - Assert.Equal(rect.Height, TestObject.RectProperty.Height); - Assert.Equal(rect.Width, TestObject.RectProperty.Width); - Assert.True(TestObject.RectProperty == rect); - } - - [Fact] - public void TestSizeTypeMapping() - { - var size = new Size { Height = 3.14, Width = 42 }; - TestObject.SizeProperty = size; - Assert.Equal(size.Height, TestObject.SizeProperty.Height); - Assert.Equal(size.Width, TestObject.SizeProperty.Width); - Assert.True(TestObject.SizeProperty == size); - - var vector2 = TestObject.SizeProperty.ToVector2(); - Assert.Equal(size.Width, vector2.X); - Assert.Equal(size.Height, vector2.Y); - - TestObject.SizeProperty = vector2.ToSize(); - Assert.Equal(size.Width, TestObject.SizeProperty.Width); - Assert.Equal(size.Height, TestObject.SizeProperty.Height); - } - - [Fact] - public void TestColorTypeMapping() - { - var color = new Color { A = 0x20, R = 0x40, G = 0x60, B = 0x80 }; - TestObject.ColorProperty = color; - Assert.Equal(color.A, TestObject.ColorProperty.A); - Assert.Equal(color.R, TestObject.ColorProperty.R); - Assert.Equal(color.G, TestObject.ColorProperty.G); - Assert.Equal(color.B, TestObject.ColorProperty.B); - Assert.True(TestObject.ColorProperty == color); - } - - [Fact] - public void TestCornerRadiusTypeMapping() - { - var cornerRadius = new CornerRadius { TopLeft = 1, TopRight = 2, BottomRight = 3, BottomLeft = 4 }; - TestObject.CornerRadiusProperty = cornerRadius; - Assert.Equal(cornerRadius.TopLeft, TestObject.CornerRadiusProperty.TopLeft); - Assert.Equal(cornerRadius.TopRight, TestObject.CornerRadiusProperty.TopRight); - Assert.Equal(cornerRadius.BottomRight, TestObject.CornerRadiusProperty.BottomRight); - Assert.Equal(cornerRadius.BottomLeft, TestObject.CornerRadiusProperty.BottomLeft); - Assert.True(TestObject.CornerRadiusProperty == cornerRadius); - } - - [Fact] - public void TestDurationTypeMapping() - { - var duration = new Duration(TimeSpan.FromTicks(42)); - TestObject.DurationProperty = duration; - Assert.Equal(duration.TimeSpan, TestObject.DurationProperty.TimeSpan); - Assert.True(TestObject.DurationProperty == duration); - } - - [Fact] - public void TestGridLengthTypeMapping() - { - var gridLength = new GridLength(42, GridUnitType.Pixel); - TestObject.GridLengthProperty = gridLength; - Assert.Equal(gridLength.GridUnitType, TestObject.GridLengthProperty.GridUnitType); - Assert.Equal(gridLength.Value, TestObject.GridLengthProperty.Value); - Assert.True(TestObject.GridLengthProperty == gridLength); - } - - [Fact] - public void TestThicknessTypeMapping() - { - var thickness = new Thickness { Left = 1, Top = 2, Right = 3, Bottom = 4 }; - TestObject.ThicknessProperty = thickness; - Assert.Equal(thickness.Left, TestObject.ThicknessProperty.Left); - Assert.Equal(thickness.Top, TestObject.ThicknessProperty.Top); - Assert.Equal(thickness.Right, TestObject.ThicknessProperty.Right); - Assert.Equal(thickness.Bottom, TestObject.ThicknessProperty.Bottom); - Assert.True(TestObject.ThicknessProperty == thickness); - } - - [Fact] - public void TestGeneratorPositionTypeMapping() - { - var generatorPosition = new GeneratorPosition { Index = 1, Offset = 2 }; - TestObject.GeneratorPositionProperty = generatorPosition; - Assert.Equal(generatorPosition.Index, TestObject.GeneratorPositionProperty.Index); - Assert.Equal(generatorPosition.Offset, TestObject.GeneratorPositionProperty.Offset); - Assert.True(TestObject.GeneratorPositionProperty == generatorPosition); - } - - [Fact] - public void TestMatrixTypeMapping() - { - var matrix = new Matrix { M11 = 11, M12 = 12, M21 = 21, M22 = 22, OffsetX = 3, OffsetY = 4 }; - TestObject.MatrixProperty = matrix; - Assert.Equal(matrix.M11, TestObject.MatrixProperty.M11); - Assert.Equal(matrix.M12, TestObject.MatrixProperty.M12); - Assert.Equal(matrix.M21, TestObject.MatrixProperty.M21); - Assert.Equal(matrix.M22, TestObject.MatrixProperty.M22); - Assert.Equal(matrix.OffsetX, TestObject.MatrixProperty.OffsetX); - Assert.Equal(matrix.OffsetY, TestObject.MatrixProperty.OffsetY); - Assert.True(TestObject.MatrixProperty == matrix); - } - - [Fact] - public void TestKeyTimeTypeMapping() - { - var keyTime = KeyTime.FromTimeSpan(TimeSpan.FromTicks(42)); - TestObject.KeyTimeProperty = keyTime; - Assert.Equal(keyTime.TimeSpan, TestObject.KeyTimeProperty.TimeSpan); - Assert.True(TestObject.KeyTimeProperty == keyTime); - } - - [Fact] - public void TestRepeatBehaviorTypeMapping() - { - var repeatBehavior = new RepeatBehavior - { - Count = 1, - Duration = TimeSpan.FromTicks(42), - Type = RepeatBehaviorType.Forever - }; - TestObject.RepeatBehaviorProperty = repeatBehavior; - Assert.Equal(repeatBehavior.Count, TestObject.RepeatBehaviorProperty.Count); - Assert.Equal(repeatBehavior.Duration, TestObject.RepeatBehaviorProperty.Duration); - Assert.Equal(repeatBehavior.Type, TestObject.RepeatBehaviorProperty.Type); - Assert.True(TestObject.RepeatBehaviorProperty == repeatBehavior); - } - - [Fact] - public void TestMatrix3DTypeMapping() - { - var matrix3D = new Matrix3D { - M11 = 11, M12 = 12, M13 = 13, M14 = 14, - M21 = 21, M22 = 22, M23 = 23, M24 = 24, - M31 = 31, M32 = 32, M33 = 33, M34 = 34, - OffsetX = 41, OffsetY = 42, OffsetZ = 43,M44 = 44 }; - - TestObject.Matrix3DProperty = matrix3D; - Assert.Equal(matrix3D.M11, TestObject.Matrix3DProperty.M11); - Assert.Equal(matrix3D.M12, TestObject.Matrix3DProperty.M12); - Assert.Equal(matrix3D.M13, TestObject.Matrix3DProperty.M13); - Assert.Equal(matrix3D.M14, TestObject.Matrix3DProperty.M14); - Assert.Equal(matrix3D.M21, TestObject.Matrix3DProperty.M21); - Assert.Equal(matrix3D.M22, TestObject.Matrix3DProperty.M22); - Assert.Equal(matrix3D.M23, TestObject.Matrix3DProperty.M23); - Assert.Equal(matrix3D.M24, TestObject.Matrix3DProperty.M24); - Assert.Equal(matrix3D.M31, TestObject.Matrix3DProperty.M31); - Assert.Equal(matrix3D.M32, TestObject.Matrix3DProperty.M32); - Assert.Equal(matrix3D.M33, TestObject.Matrix3DProperty.M33); - Assert.Equal(matrix3D.M34, TestObject.Matrix3DProperty.M34); - Assert.Equal(matrix3D.OffsetX, TestObject.Matrix3DProperty.OffsetX); - Assert.Equal(matrix3D.OffsetY, TestObject.Matrix3DProperty.OffsetY); - Assert.Equal(matrix3D.OffsetZ, TestObject.Matrix3DProperty.OffsetZ); - Assert.Equal(matrix3D.M44, TestObject.Matrix3DProperty.M44); - Assert.True(TestObject.Matrix3DProperty == matrix3D); - } - - [Fact] - public void TestMatrix3x2TypeMapping() - { - var matrix3x2 = new Matrix3x2 - { - M11 = 11, - M12 = 12, - M21 = 21, - M22 = 22, - M31 = 31, - M32 = 32, - }; - TestObject.Matrix3x2Property = matrix3x2; - Assert.Equal(matrix3x2.M11, TestObject.Matrix3x2Property.M11); - Assert.Equal(matrix3x2.M12, TestObject.Matrix3x2Property.M12); - Assert.Equal(matrix3x2.M21, TestObject.Matrix3x2Property.M21); - Assert.Equal(matrix3x2.M22, TestObject.Matrix3x2Property.M22); - Assert.Equal(matrix3x2.M31, TestObject.Matrix3x2Property.M31); - Assert.Equal(matrix3x2.M32, TestObject.Matrix3x2Property.M32); - Assert.True(TestObject.Matrix3x2Property == matrix3x2); - } - - [Fact] - public void TestMatrix4x4TypeMapping() - { - var matrix4x4 = new Matrix4x4 - { - M11 = 11, M12 = 12, M13 = 13, M14 = 14, - M21 = 21, M22 = 22, M23 = 23, M24 = 24, - M31 = 31, M32 = 32, M33 = 33, M34 = 34, - M41 = 41, M42 = 42, M43 = 43, M44 = 44 - }; - TestObject.Matrix4x4Property = matrix4x4; - Assert.Equal(matrix4x4.M11, TestObject.Matrix4x4Property.M11); - Assert.Equal(matrix4x4.M12, TestObject.Matrix4x4Property.M12); - Assert.Equal(matrix4x4.M13, TestObject.Matrix4x4Property.M13); - Assert.Equal(matrix4x4.M14, TestObject.Matrix4x4Property.M14); - Assert.Equal(matrix4x4.M21, TestObject.Matrix4x4Property.M21); - Assert.Equal(matrix4x4.M22, TestObject.Matrix4x4Property.M22); - Assert.Equal(matrix4x4.M23, TestObject.Matrix4x4Property.M23); - Assert.Equal(matrix4x4.M24, TestObject.Matrix4x4Property.M24); - Assert.Equal(matrix4x4.M31, TestObject.Matrix4x4Property.M31); - Assert.Equal(matrix4x4.M32, TestObject.Matrix4x4Property.M32); - Assert.Equal(matrix4x4.M33, TestObject.Matrix4x4Property.M33); - Assert.Equal(matrix4x4.M34, TestObject.Matrix4x4Property.M34); - Assert.Equal(matrix4x4.M41, TestObject.Matrix4x4Property.M41); - Assert.Equal(matrix4x4.M42, TestObject.Matrix4x4Property.M42); - Assert.Equal(matrix4x4.M43, TestObject.Matrix4x4Property.M43); - Assert.Equal(matrix4x4.M44, TestObject.Matrix4x4Property.M44); - Assert.True(TestObject.Matrix4x4Property == matrix4x4); - } - - [Fact] - public void TestPlaneTypeMapping() - { - var plane = new Plane { D = 3.14F, Normal = new Vector3(1, 2, 3) }; - TestObject.PlaneProperty = plane; - Assert.Equal(plane.D, TestObject.PlaneProperty.D); - Assert.Equal(plane.Normal, TestObject.PlaneProperty.Normal); - Assert.True(TestObject.PlaneProperty == plane); - } - - [Fact] - public void TestQuaternionTypeMapping() - { - var quaternion = new Quaternion { W = 3.14F, X = 1, Y = 42, Z = 1729 }; - TestObject.QuaternionProperty = quaternion; - Assert.Equal(quaternion.W, TestObject.QuaternionProperty.W); - Assert.Equal(quaternion.X, TestObject.QuaternionProperty.X); - Assert.Equal(quaternion.Y, TestObject.QuaternionProperty.Y); - Assert.Equal(quaternion.Z, TestObject.QuaternionProperty.Z); - Assert.True(TestObject.QuaternionProperty == quaternion); - } - - [Fact] - public void TestVector2TypeMapping() - { - var vector2 = new Vector2 { X = 1, Y = 42 }; - TestObject.Vector2Property = vector2; - Assert.Equal(vector2.X, TestObject.Vector2Property.X); - Assert.Equal(vector2.Y, TestObject.Vector2Property.Y); - Assert.True(TestObject.Vector2Property == vector2); - } - - [Fact] - public void TestVector3TypeMapping() - { - var vector3 = new Vector3 { X = 1, Y = 42, Z = 1729 }; - TestObject.Vector3Property = vector3; - Assert.Equal(vector3.X, TestObject.Vector3Property.X); - Assert.Equal(vector3.Y, TestObject.Vector3Property.Y); - Assert.Equal(vector3.Z, TestObject.Vector3Property.Z); - Assert.True(TestObject.Vector3Property == vector3); - } - - [Fact] - public void TestVector4TypeMapping() - { - var vector4 = new Vector4 { W = 3.14F, X = 1, Y = 42, Z = 1729 }; - TestObject.Vector4Property = vector4; - Assert.Equal(vector4.W, TestObject.Vector4Property.W); - Assert.Equal(vector4.X, TestObject.Vector4Property.X); - Assert.Equal(vector4.Y, TestObject.Vector4Property.Y); - Assert.Equal(vector4.Z, TestObject.Vector4Property.Z); - Assert.True(TestObject.Vector4Property == vector4); - } - - [Fact] - public void TestTimeSpanMapping() - { - var ts = TimeSpan.FromSeconds(42); - TestObject.TimeSpanProperty = ts; - Assert.Equal(ts, TestObject.TimeSpanProperty); - Assert.Equal(ts, TestObject.GetTimeSpanReference().Value); - Assert.Equal(ts, Class.FromSeconds(42)); - } - - [Fact] - public void TestDateTimeMapping() - { - var now = DateTimeOffset.Now; - Assert.InRange((Class.Now() - now).Ticks, -TimeSpan.TicksPerSecond, TimeSpan.TicksPerSecond); // Unlikely to be the same, but should be within a second - TestObject.DateTimeProperty = now; - Assert.Equal(now, TestObject.DateTimeProperty); - Assert.Equal(now, TestObject.GetDateTimeProperty().Value); - } - - [Fact] - public void TestDateTimeMappingNegative() - { - var time = new DateTimeOffset(1501, 1, 1, 0, 0, 0, TimeSpan.Zero); - TestObject.DateTimeProperty = time; - Assert.Equal(time, TestObject.DateTimeProperty); - Assert.Equal(time, TestObject.GetDateTimeProperty().Value); - } - - [Fact] - public void TestExceptionMapping() - { - var ex = new ArgumentOutOfRangeException(); - - TestObject.HResultProperty = ex; - - Assert.IsType(TestObject.HResultProperty); - - TestObject.HResultProperty = null; - - Assert.Null(TestObject.HResultProperty); - } - - [Fact] - public void TestGeneratedRuntimeClassName() - { - IInspectable inspectable = new IInspectable(ComWrappersSupport.CreateCCWForObject(new ManagedProperties(2))); - Assert.Equal(typeof(IProperties1).FullName, inspectable.GetRuntimeClassName()); - } - - [Fact] - public void TestGeneratedRuntimeClassName_Primitive() - { - IInspectable inspectable = new IInspectable(ComWrappersSupport.CreateCCWForObject(2)); - Assert.Equal("Windows.Foundation.IReference`1", inspectable.GetRuntimeClassName()); - } - - [Fact] - public void TestGeneratedRuntimeClassName_Array() - { - IInspectable inspectable = new IInspectable(ComWrappersSupport.CreateCCWForObject(new int[0])); - Assert.Equal("Windows.Foundation.IReferenceArray`1", inspectable.GetRuntimeClassName()); - } - - [Fact] - public void TestValueBoxing() - { - int i = 42; - Assert.Equal(i, Class.UnboxInt32(i)); - - bool b = true; - Assert.Equal(b, Class.UnboxBoolean(b)); - - string s = "Hello World!"; - Assert.Equal(s, Class.UnboxString(s)); - - ProvideInt intHandler = () => 42; - Assert.Equal(intHandler, Class.UnboxDelegate(intHandler)); - - EnumValue enumValue = EnumValue.Two; - Assert.Equal(enumValue, Class.UnboxEnum(enumValue)); - } - - [Fact] - public void TestArrayBoxing() - { - int[] i = new[] { 42, 1, 4, 50, 0, -23 }; - Assert.Equal((IEnumerable)i, Class.UnboxInt32Array(i)); - - bool[] b = new[] { true, false, true, true, false }; - Assert.Equal((IEnumerable)b, Class.UnboxBooleanArray(b)); - - string[] s = new[] { "Hello World!", "WinRT", "C#", "Boxing" }; - Assert.Equal((IEnumerable)s, Class.UnboxStringArray(s)); - } - - [Fact] - public void TestArrayUnboxing() - { - int[] i = new[] { 42, 1, 4, 50, 0, -23 }; - - var obj = PropertyValue.CreateInt32Array(i); - Assert.IsType(obj); - Assert.Equal(i, (IEnumerable)obj); - } - - [Fact] - public void PrimitiveTypeInfo() - { - Assert.Equal(typeof(int), Class.Int32Type); - Assert.True(Class.VerifyTypeIsInt32Type(typeof(int))); - } - - [Fact] - public void WinRTTypeInfo() - { - Assert.Equal(typeof(Class), Class.ThisClassType); - Assert.True(Class.VerifyTypeIsThisClassType(typeof(Class))); - } - - [Fact] - public void ProjectedTypeInfo() - { - Assert.Equal(typeof(int?), Class.ReferenceInt32Type); - Assert.True(Class.VerifyTypeIsReferenceInt32Type(typeof(int?))); - } - - [Fact] - public void TypeInfoGenerics() - { - var typeName = Class.GetTypeNameForType(typeof(IList)); - - Assert.Equal("Windows.Foundation.Collections.IVector`1", typeName); - } - - [Fact] - public void TestGenericTypeMarshalling() - { - Assert.Equal(typeof(ABI.System.Type), Marshaler.AbiType); - } - - [Fact] - public void TestStringUnboxing() - { - var str1 = Class.EmptyString; - var str2 = Class.EmptyString; - Assert.IsType(str1); - Assert.IsType(str2); - Assert.Equal(string.Empty, (string)str1); - Assert.Equal(string.Empty, (string)str2); - } - - [Fact] - public void TestDelegateUnboxing() - { - var del = Class.BoxedDelegate; - Assert.IsType(del); - var provideUriDel = (ProvideUri) del; - Assert.Equal(new Uri("http://microsoft.com"), provideUriDel()); - } - - [Fact] - public void TestEnumUnboxing() - { - var enumVal = Class.BoxedEnum; - Assert.IsType(enumVal); - Assert.Equal(EnumValue.Two, enumVal); - } - - internal class ManagedType { } - - [Fact] - public void CCWOfListOfManagedType() - { - using var ccw = ComWrappersSupport.CreateCCWForObject(new List()); - using var qiResult = ccw.As(GuidGenerator.GetIID(typeof(global::System.Collections.Generic.IEnumerable).GetHelperType())); - } - - [Fact] - public void WeakReferenceOfManagedObject() - { - var properties = new ManagedProperties(42); - WeakRefNS.WeakReference weakReference = new WeakRefNS.WeakReference(properties); - Assert.True(weakReference.TryGetTarget(out var propertiesStrong)); - Assert.Same(properties, propertiesStrong); - } - - [Fact] - public void WeakReferenceOfNativeObject() - { - var weakReference = new WeakRefNS.WeakReference(TestObject); - Assert.True(weakReference.TryGetTarget(out var classStrong)); - Assert.Same(TestObject, classStrong); - } - - [Fact] - public void WeakReferenceOfNativeObjectRehydratedAfterWrapperIsCollected() - { - static (WeakRefNS.WeakReference winrt, WeakReference net, IObjectReference objRef) GetWeakReferences() - { - var obj = new Class(); - ComWrappersSupport.TryUnwrapObject(obj, out var objRef); - return (new WeakRefNS.WeakReference(obj), new WeakReference(obj), objRef); - } - - var (winrt, net, objRef) = GetWeakReferences(); - - GC.Collect(); - GC.WaitForPendingFinalizers(); - Assert.False(net.IsAlive); - Assert.True(winrt.TryGetTarget(out _)); - GC.KeepAlive(objRef); - } - - [Fact] - public void TestUnwrapInspectable() - { - using var objRef = MarshalInspectable.CreateMarshaler(TestObject); - var inspectable = IInspectable.FromAbi(objRef.ThisPtr); - Assert.True(ComWrappersSupport.TryUnwrapObject(inspectable, out _)); - - using var objRef2 = MarshalInspectable.CreateMarshaler(TestObject); - var inspectable2 = IInspectable.FromAbi(objRef2.ThisPtr); - Assert.True(ComWrappersSupport.TryUnwrapObject(inspectable2, out _)); - } - - [Fact] - public void TestManagedAgileObject() - { - using var testObjectAgileRef = TestObject.AsAgile(); - var agileTestObject = testObjectAgileRef.Get(); - Assert.Equal(TestObject, agileTestObject); - - IProperties1 properties = new ManagedProperties(42); - using var propertiesAgileRef = properties.AsAgile(); - var agileProperties = propertiesAgileRef.Get(); - Assert.Equal(properties.ReadWriteProperty, agileProperties.ReadWriteProperty); - - var agileObject = TestObject.As(); - Assert.NotNull(agileObject); - - IProperties1 properties2 = null; - using var properties2AgileRef = properties2.AsAgile(); - var agileProperties2 = properties2AgileRef.Get(); - Assert.Null(agileProperties2); - } - - class NonAgileClassCaller - { - public void AcquireObject() - { - Assert.Equal(ApartmentState.STA, Thread.CurrentThread.GetApartmentState()); - nonAgileObject = new Windows.UI.Popups.PopupMenu(); - nonAgileObject.Commands.Add(new Windows.UI.Popups.UICommand("test")); - nonAgileObject.Commands.Add(new Windows.UI.Popups.UICommand("test2")); - Assert.ThrowsAny(() => nonAgileObject.As()); - - agileReference = nonAgileObject.AsAgile(); - objectAcquired.Set(); - valueAcquired.WaitOne(); - - // Object gets proxied to the apartment. - Assert.Equal(2, proxyObject.Commands.Count); - agileReference.Dispose(); - - proxyObject2 = agileReference2.Get(); - } - - public void CheckValue() - { - objectAcquired.WaitOne(); - Assert.Equal(ApartmentState.MTA, Thread.CurrentThread.GetApartmentState()); - proxyObject = agileReference.Get(); - Assert.Equal(2, proxyObject.Commands.Count); - - nonAgileObject2 = new Windows.UI.Popups.PopupMenu(); - agileReference2 = nonAgileObject2.AsAgile(); - - valueAcquired.Set(); - } - - public void CallProxyObject() - { - // Call to a proxy object which we internally use an agile reference - // to resolve after the apartment is gone should throw. - Assert.ThrowsAny(() => proxyObject.Commands); - } - - private Windows.UI.Popups.PopupMenu nonAgileObject, nonAgileObject2; - private Windows.UI.Popups.PopupMenu proxyObject, proxyObject2; - private AgileReference agileReference, agileReference2; - private readonly AutoResetEvent objectAcquired = new AutoResetEvent(false); - private readonly AutoResetEvent valueAcquired = new AutoResetEvent(false); - } - - - [Fact] - public void TestNonAgileObjectCall() - { - NonAgileClassCaller caller = new NonAgileClassCaller(); - Thread staThread = new Thread(new ThreadStart(caller.AcquireObject)); - staThread.SetApartmentState(ApartmentState.STA); - staThread.Start(); - - Thread mtaThread = new Thread(new ThreadStart(caller.CheckValue)); - mtaThread.SetApartmentState(ApartmentState.MTA); - mtaThread.Start(); - mtaThread.Join(); - staThread.Join(); - - // Spin another STA thread after the other 2 threads are done and try to - // access one of the proxied objects. They should fail as there is no context - // to switch to in order to marshal it to the current apartment. - Thread anotherStaThread = new Thread(new ThreadStart(caller.CallProxyObject)); - anotherStaThread.SetApartmentState(ApartmentState.STA); - anotherStaThread.Start(); - anotherStaThread.Join(); - } - - [Fact] - public void TestNonAgileDelegateCall() - { - var expected = new int[] { 0, 1, 2 }; - var observable = new ManagedBindableObservable(expected); - var nonAgileClass = new NonAgileClass(); - nonAgileClass.Observe(observable); - observable.Add(3); - Assert.Equal(6, observable.Observation); - } - - [ComImport] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [Guid("EECDBF0E-BAE9-4CB6-A68E-9598E1CB57BB")] - internal interface IWindowNative - { - IntPtr WindowHandle { get; } - } - - [ComImport] - [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] - [Guid("3E68D4BD-7135-4D10-8018-9FB6D9F33FA1")] - internal interface IInitializeWithWindow - { - void Initialize(IntPtr hwnd); - } - - [Fact] - unsafe public void TestComImports() - { - static Object MakeObject() - { - Assert.Equal(0, ComImports.NumObjects); - var obj = ComImports.MakeObject(); - Assert.Equal(1, ComImports.NumObjects); - return obj; - } - - static void TestObject() => MakeObject(); - - static (IInitializeWithWindow, IWindowNative) MakeImports() - { - var obj = MakeObject(); - var initializeWithWindow = obj.As(); - var windowNative = obj.As(); - return (initializeWithWindow, windowNative); - } - - static void TestImports() - { - var (initializeWithWindow, windowNative) = MakeImports(); - - GC.Collect(); - GC.WaitForPendingFinalizers(); - - var hwnd = new IntPtr(0x12345678); - initializeWithWindow.Initialize(hwnd); - Assert.Equal(windowNative.WindowHandle, hwnd); - } - - TestObject(); - GC.Collect(); - GC.WaitForPendingFinalizers(); - Assert.Equal(0, ComImports.NumObjects); - - TestImports(); - GC.Collect(); - GC.WaitForPendingFinalizers(); - Assert.Equal(0, ComImports.NumObjects); - } - - [Fact] - public void TestInterfaceObjectMarshalling() - { - var nativeProperties = Class.NativeProperties1; - - TestObject.CopyProperties(nativeProperties); - - Assert.Equal(TestObject.ReadWriteProperty, nativeProperties.ReadWriteProperty); - } - - [Fact] + TestObject.CopyProperties(nativeProperties); + + Assert.Equal(TestObject.ReadWriteProperty, nativeProperties.ReadWriteProperty); + } + + [Fact] public void TestSetPropertyAcrossProjections() { var setPropertyClass = new TestComponentCSharp.AnotherAssembly.SetPropertyClass(); @@ -2510,62 +3165,111 @@ public void TestSetPropertyAcrossProjections() IProperties1 property = setPropertyClass; Assert.Equal(4, property.ReadWriteProperty); - } - - // Test scenario where type reported by runtimeclass name is not a valid type (i.e. internal type). - [Fact] - public void TestNonProjectedRuntimeClass() - { - string key = "....."; - IBuffer keyMaterial = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf8); - MacAlgorithmProvider mac = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha1); - CryptographicKey cryptoKey = mac.CreateKey(keyMaterial); - Assert.NotNull(cryptoKey); - } - - [Fact(Skip="Operation not supported")] - public void TestIBindableIterator() - { - CustomBindableIteratorTest bindableIterator = new CustomBindableIteratorTest(); - Assert.True(bindableIterator.MoveNext()); - Assert.True(bindableIterator.HasCurrent); - Assert.Equal(27861, bindableIterator.Current); - } - - [Fact] - public void TestIDisposable() - { - CustomDisposableTest disposable = new CustomDisposableTest(); - disposable.Dispose(); - } - - [Fact] - public void TestIBindableVector() - { - CustomBindableVectorTest vector = new CustomBindableVectorTest(); - Assert.NotNull(vector); - } - - [Fact] - public void TestCovariance() - { - var listOfListOfPoints = new List>() { - new List{ new Point(1, 1), new Point(1, 2), new Point(1, 3) }, - new List{ new Point(2, 1), new Point(2, 2), new Point(2, 3) }, - new List{ new Point(3, 1), new Point(3, 2), new Point(3, 3) } - }; - TestObject.IterableOfPointIterablesProperty = listOfListOfPoints; - Assert.True(TestObject.IterableOfPointIterablesProperty.SequenceEqual(listOfListOfPoints)); - - var listOfListOfUris = new List>() { - new List{ new Uri("http://aka.ms/cswinrt"), new Uri("http://github.com") }, - new List{ new Uri("http://aka.ms/cswinrt") }, - new List{ new Uri("http://aka.ms/cswinrt"), new Uri("http://microsoft.com") } - }; - TestObject.IterableOfObjectIterablesProperty = listOfListOfUris; - Assert.True(TestObject.IterableOfObjectIterablesProperty.SequenceEqual(listOfListOfUris)); - } - + } + + [Fact] + public void TestStaticPropertyImplementedAcrossInterfaces() + { + // Testing call doesn't fail. + WarningStatic.ReadWriteProperty = 4; // expected warning CA1416 + _ = WarningStatic.ReadWriteProperty; + } + + // Test scenario where type reported by runtimeclass name is not a valid type (i.e. internal type). + [Fact] + public void TestNonProjectedRuntimeClass() + { + string key = "....."; + IBuffer keyMaterial = CryptographicBuffer.ConvertStringToBinary(key, BinaryStringEncoding.Utf8); + MacAlgorithmProvider mac = MacAlgorithmProvider.OpenAlgorithm(MacAlgorithmNames.HmacSha1); + CryptographicKey cryptoKey = mac.CreateKey(keyMaterial); + Assert.NotNull(cryptoKey); + } + + [Fact] + public void TestIBindableIterator() + { + CustomBindableIteratorTest bindableIterator = new CustomBindableIteratorTest(); + Assert.True(bindableIterator.MoveNext()); + Assert.True(bindableIterator.HasCurrent); + Assert.Equal(27861, bindableIterator.Current); + } + + [Fact] + public void TestIDisposable() + { + CustomDisposableTest disposable = new CustomDisposableTest(); + disposable.Dispose(); + } + + [Fact] + public void TestIBindableVector() + { + CustomBindableVectorTest vector = new CustomBindableVectorTest(); + Assert.NotNull(vector); + Assert.Equal(1, vector.Count); + Assert.False(vector.IsSynchronized); + Assert.NotNull(vector.SyncRoot); + Assert.Equal(1, vector[0]); + + var enumerator = ((IEnumerable)vector).GetEnumerator(); + Assert.NotNull(enumerator); + } + + [Fact] + public void TestBindableObservableVector() + { + CustomBindableObservableVectorTest vector = new CustomBindableObservableVectorTest(); + Assert.Equal(1, vector.Count); + Assert.False(vector.IsSynchronized); + Assert.NotNull(vector.SyncRoot); + Assert.Equal(1, vector[0]); + vector.Clear(); + } + + [Fact] + public void TestNonProjectedBindableObservableVector() + { + var expected = new int[] { 0, 1, 2 }; + var observable = new ManagedBindableObservable(expected); + var nativeObservable = TestObject.GetBindableObservableVector(observable); + Assert.Equal(3, ((ICollection)(object)nativeObservable).Count); + Assert.Equal(3, nativeObservable.Count); + Assert.NotNull(nativeObservable.SyncRoot); + Assert.Equal(0, nativeObservable[0]); + nativeObservable.Clear(); + Assert.Equal(0, nativeObservable.Count); + } + + [Fact(Skip = "InvalidOperationException due to missing non-generic IEnumerator #1302")] + public void TestIterator() + { + CustomIteratorTest iterator = new CustomIteratorTest(); + iterator.MoveNext(); + Assert.Equal(2, iterator.Current); + Assert.Equal(2, ((IEnumerator)iterator).Current); + } + + [Fact] + public void TestCovariance() + { + var listOfListOfPoints = new List>() { + new List{ new Point(1, 1), new Point(1, 2), new Point(1, 3) }, + new List{ new Point(2, 1), new Point(2, 2), new Point(2, 3) }, + new List{ new Point(3, 1), new Point(3, 2), new Point(3, 3) } + }; + TestObject.IterableOfPointIterablesProperty = listOfListOfPoints; + Assert.True(TestObject.IterableOfPointIterablesProperty.SequenceEqual(listOfListOfPoints)); + + var listOfListOfUris = new List>() { + new List{ new Uri("http://aka.ms/cswinrt"), new Uri("http://github.com") }, + new List{ new Uri("http://aka.ms/cswinrt") }, + new List{ new Uri("http://aka.ms/cswinrt"), new Uri("http://microsoft.com") } + }; + TestObject.IterableOfObjectIterablesProperty = listOfListOfUris; + Assert.True(TestObject.IterableOfObjectIterablesProperty.SequenceEqual(listOfListOfUris)); + } + (System.WeakReference, System.WeakReference) TestEventDelegateCleanup() { // Both WinRT object and handler class alive. @@ -2594,60 +3298,60 @@ public void TestCovariance() var weakClassInstance = new System.WeakReference(classInstance); classInstance = null; return (weakClassInstance, weakEventHandlerClass); - } - - // Ensure that event subscription state is properly cached to enable later unsubscribes - [Fact] - public void TestEventSourceCaching() - { - bool eventCalled = false; - void Class_StaticIntPropertyChanged(object sender, int e) => eventCalled = (e == 3); - bool eventCalled2 = false; - void Class_StaticIntPropertyChanged2(object sender, int e) => eventCalled2 = (e == 3); - - // Test static codegen-based EventSource caching - Class.StaticIntPropertyChanged += Class_StaticIntPropertyChanged; - GC.Collect(2, GCCollectionMode.Forced, true); - GC.WaitForPendingFinalizers(); - Class.StaticIntPropertyChanged -= Class_StaticIntPropertyChanged; - Class.StaticIntProperty = 3; - Assert.False(eventCalled); - Class.StaticIntPropertyChanged += Class_StaticIntPropertyChanged; - GC.Collect(2, GCCollectionMode.Forced, true); - GC.WaitForPendingFinalizers(); - Class.StaticIntProperty = 3; - Assert.True(eventCalled); - eventCalled = false; - - // Test adding another delegate to validate COM reference tracking in EventSource - Class.StaticIntPropertyChanged += Class_StaticIntPropertyChanged2; - Class.StaticIntProperty = 3; + } + + // Ensure that event subscription state is properly cached to enable later unsubscribes + [Fact] + public void TestEventSourceCaching() + { + bool eventCalled = false; + void Class_StaticIntPropertyChanged(object sender, int e) => eventCalled = (e == 3); + bool eventCalled2 = false; + void Class_StaticIntPropertyChanged2(object sender, int e) => eventCalled2 = (e == 3); + + // Test static codegen-based EventSource caching + Class.StaticIntPropertyChanged += Class_StaticIntPropertyChanged; + GC.Collect(2, GCCollectionMode.Forced, true); + GC.WaitForPendingFinalizers(); + Class.StaticIntPropertyChanged -= Class_StaticIntPropertyChanged; + Class.StaticIntProperty = 3; + Assert.False(eventCalled); + Class.StaticIntPropertyChanged += Class_StaticIntPropertyChanged; + GC.Collect(2, GCCollectionMode.Forced, true); + GC.WaitForPendingFinalizers(); + Class.StaticIntProperty = 3; Assert.True(eventCalled); - Assert.True(eventCalled2); - GC.Collect(2, GCCollectionMode.Forced, true); - GC.WaitForPendingFinalizers(); - eventCalled = false; + eventCalled = false; + + // Test adding another delegate to validate COM reference tracking in EventSource + Class.StaticIntPropertyChanged += Class_StaticIntPropertyChanged2; + Class.StaticIntProperty = 3; + Assert.True(eventCalled); + Assert.True(eventCalled2); + GC.Collect(2, GCCollectionMode.Forced, true); + GC.WaitForPendingFinalizers(); + eventCalled = false; eventCalled2 = false; - Class.StaticIntPropertyChanged -= Class_StaticIntPropertyChanged; - Class.StaticIntProperty = 3; - Assert.False(eventCalled); - Assert.True(eventCalled2); - - // Test dynamic WeakRef-based EventSource caching - eventCalled = false; - static void Subscribe(EventHandler handler) => Singleton.Instance.IntPropertyChanged += handler; - static void Unsubscribe(EventHandler handler) => Singleton.Instance.IntPropertyChanged -= handler; - static void Assign(int value) => Singleton.Instance.IntProperty = value; - Subscribe(Class_StaticIntPropertyChanged); - GC.Collect(2, GCCollectionMode.Forced, true); - GC.WaitForPendingFinalizers(); - Unsubscribe(Class_StaticIntPropertyChanged); - Assign(3); - Assert.False(eventCalled); - Subscribe(Class_StaticIntPropertyChanged); - GC.Collect(2, GCCollectionMode.Forced, true); - GC.WaitForPendingFinalizers(); - Assign(3); + Class.StaticIntPropertyChanged -= Class_StaticIntPropertyChanged; + Class.StaticIntProperty = 3; + Assert.False(eventCalled); + Assert.True(eventCalled2); + + // Test dynamic WeakRef-based EventSource caching + eventCalled = false; + static void Subscribe(EventHandler handler) => Singleton.Instance.IntPropertyChanged += handler; + static void Unsubscribe(EventHandler handler) => Singleton.Instance.IntPropertyChanged -= handler; + static void Assign(int value) => Singleton.Instance.IntProperty = value; + Subscribe(Class_StaticIntPropertyChanged); + GC.Collect(2, GCCollectionMode.Forced, true); + GC.WaitForPendingFinalizers(); + Unsubscribe(Class_StaticIntPropertyChanged); + Assign(3); + Assert.False(eventCalled); + Subscribe(Class_StaticIntPropertyChanged); + GC.Collect(2, GCCollectionMode.Forced, true); + GC.WaitForPendingFinalizers(); + Assign(3); Assert.True(eventCalled); // Test that event delegates don't leak when not unsubscribed. @@ -2661,8 +3365,8 @@ public void TestEventSourceCaching() GC.WaitForPendingFinalizers(); Assert.False(weakClassInstance.TryGetTarget(out _)); Assert.False(weakEventHandlerClass.TryGetTarget(out _)); - } - + } + class EventHandlerClass { private readonly Action eventCalled; @@ -2673,16 +3377,16 @@ public EventHandlerClass(Action eventCalled) } public void IntPropertyChanged(object sender, int e) => eventCalled(); - } + } // Test scenario where events may be removed by the native event source without an unsubscribe. - [Fact] + [Fact] public void TestEventRemovalByEventSource() { - bool eventCalled = false; - void Class_IntPropertyChanged(object sender, int e) => eventCalled = (e == 3); - bool eventCalled2 = false; - void Class_IntPropertyChanged2(object sender, int e) => eventCalled2 = (e == 3); + bool eventCalled = false; + void Class_IntPropertyChanged(object sender, int e) => eventCalled = (e == 3); + bool eventCalled2 = false; + void Class_IntPropertyChanged2(object sender, int e) => eventCalled2 = (e == 3); var classInstance = new Class(); classInstance.IntPropertyChanged += Class_IntPropertyChanged; @@ -2712,8 +3416,8 @@ public void TestEventRemovalByEventSource() Assert.True(eventCalled2); } -#if NET - [Fact] +#if NET + [Fact] public void TestProxiedDelegate() { var obj = new OOPAsyncAction(); @@ -2734,7 +3438,7 @@ public void TestProxiedDelegate() var launchExePath = $"{currentExecutingDir}\\OOPExe.dll"; var proc = Process.Start("dotnet.exe", launchExePath); #endif - Thread.Sleep(1000); + Thread.Sleep(5000); obj.Close(); Assert.True(obj.delegateCalled); @@ -2742,114 +3446,114 @@ public void TestProxiedDelegate() { proc.Kill(); } - catch(Exception) + catch (Exception) { } } -#endif - - [Fact] +#endif + + [Fact] private async Task TestPnpPropertiesInLoop() { for (int i = 0; i < 10; i++) { await TestPnpPropertiesAsync(); } - } - - private async Task TestPnpPropertiesAsync() - { - var requestedDeviceProperties = new List() - { - "System.Devices.ClassGuid", - "System.Devices.ContainerId", - "System.Devices.DeviceHasProblem", - "System.Devices.DeviceInstanceId", - "System.Devices.Parent", - "System.Devices.Present", - "System.ItemNameDisplay", - "System.Devices.Children", - }; - var devicefilter = "System.Devices.Present:System.StructuredQueryType.Boolean#True"; - var presentDevices = (await PnpObject.FindAllAsync(PnpObjectType.Device, requestedDeviceProperties, devicefilter).AsTask().ConfigureAwait(false)).Select(pnpObject => { - var prop = pnpObject.Properties; - // Iterating through each key is necessary for this test even though we do not use each key directly - // This makes it more probable for a native pointer to get repeated and a value type to be cached and seen again. - foreach (var key in prop.Keys) - { - var val = prop[key]; - if (string.CompareOrdinal(key, "System.Devices.ContainerId") == 0 && val != null) - { - var val4 = pnpObject.Properties[key]; - if (val is not Guid || val4 is not Guid) - { - throw new Exception("Incorrect value type Guid. Actual type: " + val.GetType() + " " + val4.GetType()); - } - } - if (string.CompareOrdinal(key, "System.Devices.Parent") == 0 && val != null) - { - var val4 = pnpObject.Properties[key]; - if (val is not string || val4 is not string) - { - throw new Exception("Incorrect value type string Actual type: " + val.GetType() + " " + val4.GetType()); - } - } - - } - return pnpObject; - }).ToList(); - } - -#if NET - [TestComponentCSharp.Warning] // NO warning CA1416 - class WarningManaged { }; - - class WarningSubclass : WarningClass - { - void InvokeOverridableWarnings() - { - WarningOverridableMethod(); // warning CA1416 - WarningOverridableProperty = 0; // warning CA1416 - // see https://github.com/microsoft/cppwinrt/issues/782 - //WarningOverridableEvent += (object s, Int32 v) => { }; // warning CA1416 - } - } - - // Manual for now - verify that all APIs targeting 19041 generate a warning - private void TestSupportedOSPlatformWarnings() - { - // Types - var a = new WarningAttribute(); // warning CA1416 - Assert.NotNull(a); - var w = new WarningStruct{ i32 = 0 }; // warning CA1416 - Assert.Equal(0, w.i32); // warning CA1416 - var v = WarningEnum.Value; - Assert.NotEqual(WarningEnum.WarningValue, v); // warning CA1416 - - // Members - var o = new WarningClass(); // warning CA1416 - o = new WarningClass(WarningEnum.Value); // warning CA1416 - o.WarningMethod(); // warning CA1416 - var p = o.WarningProperty; // warning CA1416 - o.WarningProperty = 0; // warning CA1416 - p = o.WarningPropertySetter; - o.WarningPropertySetter = 0; // warning CA1416 - o.WarningEvent += (object s, Int32 v) => { }; // warning CA1416 - o.WarningInterfaceMethod(); // warning CA1416 - p = o.WarningInterfaceProperty; // warning CA1416 - o.WarningInterfaceProperty = 0; // warning CA1416 - p = o.WarningInterfacePropertySetter; - o.WarningInterfacePropertySetter = 0; // warning CA1416 - o.WarningInterfaceEvent += (object s, Int32 v) => { }; // warning CA1416 - - // Attributed statics - WarningStatic.WarningMethod(); // warning CA1416 - WarningStatic.WarningProperty = 0; // warning CA1416 - WarningStatic.WarningEvent += (object s, Int32 v) => { }; // warning CA1416 - } -#endif - - [Fact] + } + + private async Task TestPnpPropertiesAsync() + { + var requestedDeviceProperties = new List() + { + "System.Devices.ClassGuid", + "System.Devices.ContainerId", + "System.Devices.DeviceHasProblem", + "System.Devices.DeviceInstanceId", + "System.Devices.Parent", + "System.Devices.Present", + "System.ItemNameDisplay", + "System.Devices.Children", + }; + var devicefilter = "System.Devices.Present:System.StructuredQueryType.Boolean#True"; + var presentDevices = (await PnpObject.FindAllAsync(PnpObjectType.Device, requestedDeviceProperties, devicefilter).AsTask().ConfigureAwait(false)).Select(pnpObject => { + var prop = pnpObject.Properties; + // Iterating through each key is necessary for this test even though we do not use each key directly + // This makes it more probable for a native pointer to get repeated and a value type to be cached and seen again. + foreach (var key in prop.Keys) + { + var val = prop[key]; + if (string.CompareOrdinal(key, "System.Devices.ContainerId") == 0 && val != null) + { + var val4 = pnpObject.Properties[key]; + if (val is not Guid || val4 is not Guid) + { + throw new Exception("Incorrect value type Guid. Actual type: " + val.GetType() + " " + val4.GetType()); + } + } + if (string.CompareOrdinal(key, "System.Devices.Parent") == 0 && val != null) + { + var val4 = pnpObject.Properties[key]; + if (val is not string || val4 is not string) + { + throw new Exception("Incorrect value type string Actual type: " + val.GetType() + " " + val4.GetType()); + } + } + + } + return pnpObject; + }).ToList(); + } + +#if NET + [TestComponentCSharp.Warning] // NO warning CA1416 + class WarningManaged { }; + + class WarningSubclass : WarningClass + { + void InvokeOverridableWarnings() + { + WarningOverridableMethod(); // warning CA1416 + WarningOverridableProperty = 0; // warning CA1416 + // see https://github.com/microsoft/cppwinrt/issues/782 + //WarningOverridableEvent += (object s, Int32 v) => { }; // warning CA1416 + } + } + + // Manual for now - verify that all APIs targeting 19041 generate a warning + private void TestSupportedOSPlatformWarnings() + { + // Types + var a = new WarningAttribute(); // warning CA1416 + Assert.NotNull(a); + var w = new WarningStruct { i32 = 0 }; // warning CA1416 + Assert.Equal(0, w.i32); // warning CA1416 + var v = WarningEnum.Value; + Assert.NotEqual(WarningEnum.WarningValue, v); // warning CA1416 + + // Members + var o = new WarningClass(); // warning CA1416 + o = new WarningClass(WarningEnum.Value); // warning CA1416 + o.WarningMethod(); // warning CA1416 + var p = o.WarningProperty; // warning CA1416 + o.WarningProperty = 0; // warning CA1416 + p = o.WarningPropertySetter; + o.WarningPropertySetter = 0; // warning CA1416 + o.WarningEvent += (object s, Int32 v) => { }; // warning CA1416 + o.WarningInterfaceMethod(); // warning CA1416 + p = o.WarningInterfaceProperty; // warning CA1416 + o.WarningInterfaceProperty = 0; // warning CA1416 + p = o.WarningInterfacePropertySetter; + o.WarningInterfacePropertySetter = 0; // warning CA1416 + o.WarningInterfaceEvent += (object s, Int32 v) => { }; // warning CA1416 + + // Attributed statics + WarningStatic.WarningMethod(); // warning CA1416 + WarningStatic.WarningProperty = 0; // warning CA1416 + WarningStatic.WarningEvent += (object s, Int32 v) => { }; // warning CA1416 + } +#endif + + [Fact] public void TestObjectFunctions() { CustomEquals first = new() @@ -2904,13 +3608,242 @@ public void TestObjectFunctions() // Uses Equals defined on derived. Assert.True(eighth.Equals(seventh)); Assert.False(seventh.Equals(eighth)); - } - - // Manually verify warning for experimental. + } + + // Manually verify warning for experimental. private void TestExperimentAttribute() { CustomExperimentClass custom = new CustomExperimentClass(); custom.f(); - } - } -} + } + + void OnDeviceAdded(DeviceWatcher sender, DeviceInformation args) + { + } + + void OnDeviceUpdated(DeviceWatcher sender, DeviceInformationUpdate args) + { + } + + [Fact] + public void TestWeakReferenceEventsFromMultipleContexts() + { + SemaphoreSlim semaphore = new SemaphoreSlim(0); + DeviceWatcher watcher = null; + + Thread staThread = new Thread(() => + { + Assert.True(Thread.CurrentThread.GetApartmentState() == ApartmentState.STA); + + watcher = DeviceInformation.CreateWatcher(); + var exception = Record.Exception(() => { + watcher.Added += OnDeviceAdded; + }); + Assert.Null(exception); + + Thread mtaThread = new Thread(() => + { + Assert.True(Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA); + + exception = Record.Exception(() => { + watcher.Updated += OnDeviceUpdated; + }); + Assert.Null(exception); + }); + mtaThread.SetApartmentState(ApartmentState.MTA); + mtaThread.Start(); + mtaThread.Join(); + }); + staThread.SetApartmentState(ApartmentState.STA); + staThread.Start(); + staThread.Join(); + } + +#if NET + [Fact] + public void TestActivationFactoriesFromMultipleContexts() + { + Exception exception = null; + + Thread staThread = new Thread(() => + { + Assert.True(Thread.CurrentThread.GetApartmentState() == ApartmentState.STA); + + exception = Record.Exception(() => + { + var xmlDoc = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01); + _ = new ToastNotification(xmlDoc); + }); + + }); + staThread.SetApartmentState(ApartmentState.STA); + staThread.Start(); + staThread.Join(); + + Assert.Null(exception); + + Thread mtaThread = new Thread(() => + { + Assert.True(Thread.CurrentThread.GetApartmentState() == ApartmentState.MTA); + + exception = Record.Exception(() => + { + var xmlDoc = ToastNotificationManager.GetTemplateContent(ToastTemplateType.ToastText01); + _ = new ToastNotification(xmlDoc); + }); + }); + mtaThread.SetApartmentState(ApartmentState.MTA); + mtaThread.Start(); + mtaThread.Join(); + + Assert.Null(exception); + } + + [Guid("59C7966B-AE52-5283-AD7F-A1B9E9678ADD")] + [global::WinRT.WindowsRuntimeType("Windows.Foundation.UniversalApiContract")] + [global::WinRT.WindowsRuntimeHelperType(typeof(ICustomGuidHelperStatics))] + interface ICustomGuidHelperStatics + { + public static readonly IntPtr AbiToProjectionVftablePtr; + static unsafe ICustomGuidHelperStatics() + { + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(ICustomGuidHelperStatics), sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * 3); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[6] = &Do_Abi_CreateNewGuid_0; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[7] = &Do_Abi_get_Empty_1; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[8] = &Do_Abi_Equals_2; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_CreateNewGuid_0(IntPtr thisPtr, Guid* result) + { + + Guid __result = default; + + *result = default; + + try + { + __result = global::WinRT.ComWrappersSupport.FindObject(thisPtr).CreateNewGuid(); + *result = __result; + + } + catch (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_Equals_2(IntPtr thisPtr, Guid* target, Guid* value, byte* result) + { + + bool __result = default; + + *result = default; + + try + { + __result = global::WinRT.ComWrappersSupport.FindObject(thisPtr).Equals(*target, *value); + *result = (byte)(__result ? 1 : 0); + + } + catch (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_Empty_1(IntPtr thisPtr, Guid* value) + { + + Guid __value = default; + + *value = default; + + try + { + __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).Empty; + *value = __value; + + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + Guid CreateNewGuid(); + bool Equals(in Guid target, in Guid value); + Guid Empty { get; } + } + + class CustomGuidHelper : ICustomGuidHelperStatics + { + public static Guid Mock { get; set; } + + public Guid Empty => Mock; + + public Guid CreateNewGuid() + { + return Mock; + } + + public bool Equals(in Guid target, in Guid value) + { + return false; + } + }; + + [Fact] + public void TestActivationHandler() + { + ActivationFactory.ActivationHandler = (string name, Guid iid) => + { + Assert.Equal("Windows.Foundation.GuidHelper", name); + Assert.Equal(typeof(IGuidHelperStatics).GUID, iid); + + return MarshalInterface.FromManaged(new CustomGuidHelper()); + }; + + CustomGuidHelper.Mock = new Guid("78872A91-C365-4DDB-9509-1CCA002B6FD9"); + + Guid guid = GuidHelper.CreateNewGuid(); + Assert.Equal(CustomGuidHelper.Mock, guid); + + ActivationFactory.ActivationHandler = null; + } +#endif + + [Fact] + public void TestDictionary() + { + var intToIntDict = TestObject.GetIntToIntDictionary(); + Assert.Equal(8, intToIntDict[2]); + Assert.Equal(8, intToIntDict[2]); + Assert.Equal(12, intToIntDict[3]); + + var stringToBlittableDict = TestObject.GetStringToBlittableDictionary(); + Assert.Equal(5, stringToBlittableDict["alpha"].blittable.i32); + Assert.Equal(7, stringToBlittableDict["charlie"].blittable.i32); + Assert.Equal(5, stringToBlittableDict["alpha"].blittable.i32); + + var stringToNonBlittableDict = TestObject.GetStringToNonBlittableDictionary(); + Assert.Equal(1, stringToNonBlittableDict["String1"].blittable.i32); + Assert.Equal("String1", stringToNonBlittableDict["String1"].strings.str); + Assert.False(stringToNonBlittableDict["String1"].bools.w); + Assert.True(stringToNonBlittableDict["String1"].bools.x); + + var blittableToObjectDict = TestObject.GetBlittableToObjectDictionary(); + ComposedBlittableStruct key; + key.blittable.i32 = 4; + Assert.Equal("box", (string)blittableToObjectDict[key]); + Assert.Equal("box", (string)blittableToObjectDict[key]); + } + } +} diff --git a/src/Tests/UnitTest/TestComponent_Tests.cs b/src/Tests/UnitTest/TestComponent_Tests.cs index afc2d29f1..090a20983 100644 --- a/src/Tests/UnitTest/TestComponent_Tests.cs +++ b/src/Tests/UnitTest/TestComponent_Tests.cs @@ -835,6 +835,7 @@ public void Collections_Dictionary_Call() }); } +#if NET [Fact] public void Collections_ReadOnly_Dictionary_Call() { @@ -844,6 +845,7 @@ public void Collections_ReadOnly_Dictionary_Call() return new ReadOnlyDictionary(new Dictionary(b)); }); } +#endif [Fact] public void Collections_List_Call() @@ -1153,6 +1155,7 @@ public void Box_TimeSpanArray() Box_type(arr4, Tests.Box21); } +#if NET [Fact] public void Fast_Abi_Simple() { @@ -1198,6 +1201,7 @@ public void Fast_Abi_Composition() sv.ObjectProperty = new List { 1, 2, 3 }; Assert.Equal(3, ((List)sv.ObjectProperty).Count); } +#endif // Nota Bene: this test case must always remain the final one [Fact] diff --git a/src/Tests/UnitTest/TestModuleInitializer.cs b/src/Tests/UnitTest/TestModuleInitializer.cs new file mode 100644 index 000000000..576b0567d --- /dev/null +++ b/src/Tests/UnitTest/TestModuleInitializer.cs @@ -0,0 +1,61 @@ +using System; +using WinRT; + +#if !NET + +namespace UnitTest +{ + // In our .NET standard support, we generate most of the delegates needed by generic types as part of the projection. But for the ones + // passed or obtained as an Object, we are not able to statically detect that. On .NET Core, we are able to utilize Expression.GetDelegateType + // to dynamically create one in this case, but on .NET Framework we are not able to do that for ones with pointers in their parameters. + // This tests that scenario where the ABI delegates need to be manually declared and registered by the caller. + internal static class ProjectionTypesInitializer + { + [System.Runtime.CompilerServices.ModuleInitializer] + internal static void InitializeProjectionTypes() + { + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(ABI.TestComponent.NonBlittable).MakeByRefType(), typeof(int) }, typeof(_get_Value_NonBlittable)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(TestComponentCSharp.EnumValue).MakeByRefType(), typeof(int) }, typeof(_get_Value_EnumValue)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(uint), typeof(ABI.TestComponent.Composable).MakeByRefType(), typeof(int) }, typeof(_get_at_Composable)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(ABI.TestComponent.Composable), typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }, typeof(_index_of_Composable)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(uint), typeof(ABI.TestComponent.Composable), typeof(int) }, typeof(_set_at_Composable)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(ABI.TestComponent.Composable), typeof(int) }, typeof(_append_Composable)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(uint), typeof(Windows.Foundation.PropertyType), typeof(int) }, typeof(_set_at_PropertyType)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(Windows.Foundation.PropertyType), typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }, typeof(_index_of_PropertyType)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(Windows.Foundation.PropertyType), typeof(int) }, typeof(_append_PropertyType)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(Windows.Foundation.PropertyType).MakeByRefType(), typeof(int) }, typeof(_get_Current_PropertyType)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(uint), typeof(Windows.Foundation.PropertyType).MakeByRefType(), typeof(int) }, typeof(_get_at_PropertyType)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(uint), typeof(ABI.TestComponentCSharp.ComposedNonBlittableStruct), typeof(int) }, typeof(_set_at_ComposedNonBlittableStruct)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(ABI.TestComponentCSharp.ComposedNonBlittableStruct), typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }, typeof(_index_of_ComposedNonBlittableStruct)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(ABI.TestComponentCSharp.ComposedNonBlittableStruct), typeof(int) }, typeof(_append_ComposedNonBlittableStruct)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(ABI.TestComponentCSharp.ComposedNonBlittableStruct).MakeByRefType(), typeof(int) }, typeof(_get_Current_ComposedNonBlittableStruct)); + Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(uint), typeof(ABI.TestComponentCSharp.ComposedNonBlittableStruct).MakeByRefType(), typeof(int) }, typeof(_get_at_ComposedNonBlittableStruct)); + } + + internal unsafe delegate int _get_Value_NonBlittable(void* thisPtr, out ABI.TestComponent.NonBlittable __return_value__); + internal unsafe delegate int _get_Value_EnumValue(void* thisPtr, out TestComponentCSharp.EnumValue __return_value__); + internal unsafe delegate int _get_at_Composable(void* thisPtr, uint index, out ABI.TestComponent.Composable __return_value__); + internal unsafe delegate int _index_of_Composable(void* thisPtr, ABI.TestComponent.Composable value, out uint index, out byte found); + internal unsafe delegate int _set_at_Composable(void* thisPtr, uint index, ABI.TestComponent.Composable value); + internal unsafe delegate int _append_Composable(void* thisPtr, ABI.TestComponent.Composable value); + internal unsafe delegate int _set_at_PropertyType(void* thisPtr, uint index, Windows.Foundation.PropertyType value); + internal unsafe delegate int _index_of_PropertyType(void* thisPtr, Windows.Foundation.PropertyType value, out uint index, out byte found); + internal unsafe delegate int _append_PropertyType(void* thisPtr, Windows.Foundation.PropertyType value); + internal unsafe delegate int _get_Current_PropertyType(void* thisPtr, out Windows.Foundation.PropertyType __return_value__); + internal unsafe delegate int _get_at_PropertyType(void* thisPtr, uint index, out Windows.Foundation.PropertyType __return_value__); + internal unsafe delegate int _set_at_ComposedNonBlittableStruct(void* thisPtr, uint index, ABI.TestComponentCSharp.ComposedNonBlittableStruct value); + internal unsafe delegate int _index_of_ComposedNonBlittableStruct(void* thisPtr, ABI.TestComponentCSharp.ComposedNonBlittableStruct value, out uint index, out byte found); + internal unsafe delegate int _append_ComposedNonBlittableStruct(void* thisPtr, ABI.TestComponentCSharp.ComposedNonBlittableStruct value); + internal unsafe delegate int _get_Current_ComposedNonBlittableStruct(void* thisPtr, out ABI.TestComponentCSharp.ComposedNonBlittableStruct __return_value__); + internal unsafe delegate int _get_at_ComposedNonBlittableStruct(void* thisPtr, uint index, out ABI.TestComponentCSharp.ComposedNonBlittableStruct __return_value__); + + } +} + +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Method)] + internal sealed class ModuleInitializerAttribute : Attribute { } +} + +#endif diff --git a/src/Tests/UnitTest/UnitTest.csproj b/src/Tests/UnitTest/UnitTest.csproj index dc1e72ae7..ac711eb59 100644 --- a/src/Tests/UnitTest/UnitTest.csproj +++ b/src/Tests/UnitTest/UnitTest.csproj @@ -11,44 +11,44 @@ false - + true - + - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + all runtime; build; native; contentfiles; analyzers; buildtransitive - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + - + - - + + - + \ No newline at end of file diff --git a/src/WinRT.Runtime/ActivationFactory.cs b/src/WinRT.Runtime/ActivationFactory.cs new file mode 100644 index 000000000..781b9e7be --- /dev/null +++ b/src/WinRT.Runtime/ActivationFactory.cs @@ -0,0 +1,429 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Text; +using WinRT.Interop; + +namespace WinRT +{ + internal unsafe sealed class DllModule + { + private static readonly string _currentModuleDirectory = AppContext.BaseDirectory; + + private static readonly Dictionary _cache = new Dictionary(StringComparer.Ordinal); + + private readonly string _fileName; + private readonly IntPtr _moduleHandle; + private readonly delegate* unmanaged[Stdcall] _GetActivationFactory; + private readonly delegate* unmanaged[Stdcall] _CanUnloadNow; // TODO: Eventually periodically call + + public static bool TryLoad(string fileName, out DllModule module) + { + lock (_cache) + { + if (_cache.TryGetValue(fileName, out module)) + { + return true; + } + else if (TryCreate(fileName, out module)) + { + _cache[fileName] = module; + return true; + } + return false; + } + } + + private static bool TryCreate(string fileName, out DllModule module) + { + // Explicitly look for module in the same directory as this one, and + // use altered search path to ensure any dependencies in the same directory are found. + IntPtr moduleHandle = IntPtr.Zero; + moduleHandle = Platform.LoadLibraryExW(System.IO.Path.Combine(_currentModuleDirectory, fileName), IntPtr.Zero, /* LOAD_WITH_ALTERED_SEARCH_PATH */ 8); +#if NET + if (moduleHandle == IntPtr.Zero) + { + NativeLibrary.TryLoad(fileName, typeof(DllModule).Assembly, null, out moduleHandle); + } +#endif + if (moduleHandle == IntPtr.Zero) + { + module = null; + return false; + } + + void* getActivationFactory = null; + ReadOnlySpan functionName = +#if NET7_0_OR_GREATER || CsWinRT_LANG_11_FEATURES + "DllGetActivationFactory"u8; +#else + Encoding.ASCII.GetBytes("DllGetActivationFactory"); +#endif + getActivationFactory = (void*)Platform.TryGetProcAddress(moduleHandle, functionName); + if (getActivationFactory == null) + { + module = null; + return false; + } + + module = new DllModule( + fileName, + moduleHandle, + getActivationFactory); + return true; + } + + private DllModule(string fileName, IntPtr moduleHandle, void* getActivationFactory) + { + _fileName = fileName; + _moduleHandle = moduleHandle; + _GetActivationFactory = (delegate* unmanaged[Stdcall])getActivationFactory; + + void* canUnloadNow = null; + ReadOnlySpan functionName = +#if NET7_0_OR_GREATER || CsWinRT_LANG_11_FEATURES + "DllCanUnloadNow"u8; +#else + Encoding.ASCII.GetBytes("DllCanUnloadNow"); +#endif + canUnloadNow = (void*)Platform.TryGetProcAddress(_moduleHandle, functionName); + + if (canUnloadNow != null) + { + _CanUnloadNow = (delegate* unmanaged[Stdcall])canUnloadNow; + } + } + + public (ObjectReference obj, int hr) GetActivationFactory(string runtimeClassId) + { + IntPtr instancePtr = IntPtr.Zero; + try + { + MarshalString.Pinnable __runtimeClassId = new(runtimeClassId); + fixed (void* ___runtimeClassId = __runtimeClassId) + { + int hr = _GetActivationFactory(MarshalString.GetAbi(ref __runtimeClassId), &instancePtr); + if (hr == 0) + { + var objRef = ObjectReference.Attach(ref instancePtr, IID.IID_IActivationFactory); + return (objRef, hr); + } + else + { + return (null, hr); + } + } + } + finally + { + MarshalInspectable.DisposeAbi(instancePtr); + } + } + + ~DllModule() + { + System.Diagnostics.Debug.Assert(_CanUnloadNow == null || _CanUnloadNow() == 0); // S_OK + lock (_cache) + { + _cache.Remove(_fileName); + } + if ((_moduleHandle != IntPtr.Zero) && !Platform.FreeLibrary(_moduleHandle)) + { + Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); + } + } + } + + internal sealed class WinRTModule + { + private static volatile WinRTModule _instance; + private readonly IntPtr _mtaCookie; + private static WinRTModule MakeWinRTModule() + { + global::System.Threading.Interlocked.CompareExchange(ref _instance, new WinRTModule(), null); + return _instance; + } + public static WinRTModule Instance => _instance ?? MakeWinRTModule(); + + public unsafe WinRTModule() + { + IntPtr mtaCookie; + Marshal.ThrowExceptionForHR(Platform.CoIncrementMTAUsage(&mtaCookie)); + _mtaCookie = mtaCookie; + } + + public static unsafe (ObjectReference obj, int hr) GetActivationFactory(string runtimeClassId, Guid iid) + { + var module = Instance; // Ensure COM is initialized + IntPtr instancePtr = IntPtr.Zero; + try + { + MarshalString.Pinnable __runtimeClassId = new(runtimeClassId); + fixed (void* ___runtimeClassId = __runtimeClassId) + { + int hr = Platform.RoGetActivationFactory(MarshalString.GetAbi(ref __runtimeClassId), &iid, &instancePtr); + if (hr == 0) + { + var objRef = ObjectReference.Attach(ref instancePtr, iid); + return (objRef, hr); + } + else + { + return (null, hr); + } + } + } + finally + { + MarshalInspectable.DisposeAbi(instancePtr); + } + } + + ~WinRTModule() + { + Marshal.ThrowExceptionForHR(Platform.CoDecrementMTAUsage(_mtaCookie)); + } + } + +#nullable enable + /// + /// Provides support for activating WinRT types. + /// +#if EMBED + internal +#else + public +#endif + static class ActivationFactory + { +#if NET + /// + /// This provides a hook into activation to hook/mock activation of WinRT types. + /// + public static Func? ActivationHandler { get; set; } +#endif + + /// + /// Activates a WinRT type with the specified runtime class name. + /// + /// The ID of the activatable class (the fully qualified type name). + /// An instance wrapping an instance of the activated WinRT type. + /// Thrown if is not registered, CsWinRTEnableManifestFreeActivation is disabled, and CsWinRTManifestFreeActivationReportOriginalException is not set. + /// Thrown for any failure to activate the specified type (the exact exception type might be a derived type). + /// + /// This method will try to activate the target type as follows: + /// + /// If is set, it will be used first. + /// Otherwise, RoGetActivationFactory will be used. + /// Otherwise, the manifest-free fallback path will be used to try to resolve the target .dll to load based on . + /// + /// + public static IObjectReference Get(string typeName) + { +#if NET + // Check hook first + if (ActivationHandler != null) + { + var factoryFromhandler = GetFromActivationHandler(typeName, IID.IID_IActivationFactory); + if (factoryFromhandler != null) + { + return factoryFromhandler; + } + } +#endif + + // Prefer the RoGetActivationFactory HRESULT failure over the LoadLibrary/etc. failure + int hr; + ObjectReference factory; + (factory, hr) = WinRTModule.GetActivationFactory(typeName, IID.IID_IActivationFactory); + if (factory != null) + { + return factory; + } + + ThrowIfClassNotRegisteredAndManifestFreeActivationDisabled(typeName, hr); + + return ManifestFreeGet(typeName, hr); + } + + /// + /// Activates a WinRT type with the specified runtime class name and interface ID. + /// + /// The ID of the activatable class (the fully qualified type name). + /// The interface ID to use to dispatch the activated type. + /// An instance wrapping an instance of the activated WinRT type, dispatched with the specified interface ID. + /// +#if NET + public static IObjectReference Get(string typeName, Guid iid) +#else + public static ObjectReference Get(string typeName, Guid iid) +#endif + { +#if NET + // Check hook first + if (ActivationHandler != null) + { + var factoryFromhandler = GetFromActivationHandler(typeName, iid); + if (factoryFromhandler != null) + { + return factoryFromhandler; + } + } +#endif + + // Prefer the RoGetActivationFactory HRESULT failure over the LoadLibrary/etc. failure + int hr; +#if NET + ObjectReference factory; + (factory, hr) = WinRTModule.GetActivationFactory(typeName, iid); + if (factory != null) + { + return factory; + } + + ThrowIfClassNotRegisteredAndManifestFreeActivationDisabled(typeName, hr); + + return ManifestFreeGet(typeName, iid, hr); +#else + ObjectReference factory; + (factory, hr) = WinRTModule.GetActivationFactory(typeName, iid); + if (factory != null) + { + return factory; + } + + ThrowIfClassNotRegisteredAndManifestFreeActivationDisabled(typeName, hr); + + return ManifestFreeGet(typeName, iid, hr); +#endif + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private static IObjectReference ManifestFreeGet(string typeName, int hr) + { + var moduleName = typeName; + while (true) + { + var lastSegment = moduleName.LastIndexOf(".", StringComparison.Ordinal); + if (lastSegment <= 0) + { + Marshal.ThrowExceptionForHR(hr); + } + moduleName = moduleName.Remove(lastSegment); + + DllModule? module = null; + if (DllModule.TryLoad(moduleName + ".dll", out module)) + { + (ObjectReference factory, hr) = module.GetActivationFactory(typeName); + if (factory != null) + { + return factory; + } + } + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] +#if NET + private static IObjectReference ManifestFreeGet(string typeName, Guid iid, int hr) +#else + private static ObjectReference ManifestFreeGet(string typeName, Guid iid, int hr) +#endif + { + var moduleName = typeName; + while (true) + { + var lastSegment = moduleName.LastIndexOf(".", StringComparison.Ordinal); + if (lastSegment <= 0) + { + Marshal.ThrowExceptionForHR(hr); + } + moduleName = moduleName.Remove(lastSegment); + + if (DllModule.TryLoad(moduleName + ".dll", out DllModule module)) + { + ObjectReference activationFactory; + (activationFactory, hr) = module.GetActivationFactory(typeName); + if (activationFactory != null) + { + using (activationFactory) + { +#if NET + return activationFactory.As(iid); +#else + return activationFactory.As(iid); +#endif + } + } + } + } + } + + private static void ThrowIfClassNotRegisteredAndManifestFreeActivationDisabled(string typeName, int hr) + { + // If manifest free activation is enabled, we never throw here. + // Callers will always try the fallback path if we didn't succeed. + if (FeatureSwitches.EnableManifestFreeActivation) + { + return; + } + + // Throw the exception directly, if the user requested to preserve it + if (FeatureSwitches.ManifestFreeActivationReportOriginalException) + { + Marshal.ThrowExceptionForHR(hr); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static Exception GetException(string typeName, int hr) + { + Exception exception = Marshal.GetExceptionForHR(hr)!; + + if (hr == ExceptionHelpers.REGDB_E_CLASSNOTREG) + { + throw new NotSupportedException( + $"Failed to activate type with runtime class name '{typeName}' with 'RoGetActivationFactory' (it returned 0x80040154, ie. 'REGDB_E_CLASSNOTREG'). Make sure to add the activatable class id for the type " + + "to the APPX manifest, or enable the manifest free activation fallback path by setting the 'CsWinRTEnableManifestFreeActivation' property (note: the fallback path incurs a performance hit).", exception); + } + + return exception; + } + + // Explicit throw so the JIT can see it and correctly optimize for always throwing paths. + // The exception instantiation is in a separate method so that codegen remains separate. + throw GetException(typeName, hr); + } + +#if NET + private static IObjectReference? GetFromActivationHandler(string typeName, Guid iid) + { + var activationHandler = ActivationHandler; + if (activationHandler != null) + { + IntPtr instancePtr = IntPtr.Zero; + try + { + instancePtr = activationHandler(typeName, iid); + if (instancePtr != IntPtr.Zero) + { + return ObjectReference.Attach(ref instancePtr, iid); + } + } + finally + { + MarshalInspectable.DisposeAbi(instancePtr); + } + } + + return null; + } +#endif + } +#nullable restore +} diff --git a/src/WinRT.Runtime/AgileReference.cs b/src/WinRT.Runtime/AgileReference.cs index 3d86cb2ae..c51db9252 100644 --- a/src/WinRT.Runtime/AgileReference.cs +++ b/src/WinRT.Runtime/AgileReference.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using WinRT.Interop; @@ -14,54 +15,86 @@ namespace WinRT #endif class AgileReference : IDisposable { - private readonly static Guid CLSID_StdGlobalInterfaceTable = new(0x00000323, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46); - private readonly static Lazy Git = new Lazy(() => GetGitTable()); + private static readonly Guid CLSID_StdGlobalInterfaceTable = new(0x00000323, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46); + private static readonly object _lock = new(); +#if NET + private static volatile ABI.WinRT.Interop.IGlobalInterfaceTable _git; +#else + private static volatile global::WinRT.Interop.IGlobalInterfaceTable _git; +#endif + + // Simple singleton lazy-initialization scheme (and saving the Lazy size) +#if NET + internal static ABI.WinRT.Interop.IGlobalInterfaceTable Git +#else + internal static global::WinRT.Interop.IGlobalInterfaceTable Git +#endif + { + get + { + return _git ?? Git_Slow(); + + [MethodImpl(MethodImplOptions.NoInlining)] +#if NET + static ABI.WinRT.Interop.IGlobalInterfaceTable Git_Slow() +#else + static global::WinRT.Interop.IGlobalInterfaceTable Git_Slow() +#endif + { + lock (_lock) + { + return _git ??= GetGitTable(); + } + } + } + } + private readonly IObjectReference _agileReference; private readonly IntPtr _cookie; - private bool disposed; - - public AgileReference(IObjectReference instance) - : this(instance?.ThisPtr ?? IntPtr.Zero) - { - } - - internal AgileReference(in ObjectReferenceValue instance) - : this(instance.GetAbi()) - { - } - - internal unsafe AgileReference(IntPtr thisPtr) + private bool disposed; + + public AgileReference(IObjectReference instance) + : this(instance?.ThisPtr ?? IntPtr.Zero) + { + } + + internal AgileReference(in ObjectReferenceValue instance) + : this(instance.GetAbi()) + { + } + + internal unsafe AgileReference(IntPtr thisPtr) { if (thisPtr == IntPtr.Zero) { return; - } - + } + IntPtr agileReference = default; - Guid iid = IUnknownVftbl.IID; + Guid iid = IID.IID_IUnknown; try { Marshal.ThrowExceptionForHR(Platform.RoGetAgileReference( 0 /*AGILEREFERENCE_DEFAULT*/, - ref iid, + &iid, thisPtr, - &agileReference)); - _agileReference = ObjectReference.Attach(ref agileReference); + &agileReference)); + _agileReference = ObjectReference.Attach(ref agileReference, IID.IID_IUnknown); } catch (TypeLoadException) { - _cookie = Git.Value.RegisterInterfaceInGlobal(thisPtr, iid); + _cookie = Git.RegisterInterfaceInGlobal(thisPtr, iid); } finally { - MarshalInterface.DisposeAbi(agileReference); + MarshalInterface.DisposeAbi(agileReference); } } - public IObjectReference Get() => _cookie == IntPtr.Zero ? ABI.WinRT.Interop.IAgileReferenceMethods.Resolve(_agileReference, IUnknownVftbl.IID) : Git.Value?.GetInterfaceFromGlobal(_cookie, IUnknownVftbl.IID); - - internal ObjectReference Get(Guid iid) => _cookie == IntPtr.Zero ? ABI.WinRT.Interop.IAgileReferenceMethods.Resolve(_agileReference, iid) : Git.Value?.GetInterfaceFromGlobal(_cookie, IUnknownVftbl.IID)?.As(iid); + public IObjectReference Get() => _cookie == IntPtr.Zero ? ABI.WinRT.Interop.IAgileReferenceMethods.Resolve(_agileReference, IID.IID_IUnknown) : Git.GetInterfaceFromGlobal(_cookie, IID.IID_IUnknown); + + internal ObjectReference Get(Guid iid) => _cookie == IntPtr.Zero ? ABI.WinRT.Interop.IAgileReferenceMethods.Resolve(_agileReference, iid) : Git.GetInterfaceFromGlobal(_cookie, IID.IID_IUnknown)?.As(iid); protected virtual void Dispose(bool disposing) { @@ -69,38 +102,51 @@ protected virtual void Dispose(bool disposing) { if (_cookie != IntPtr.Zero) { +#if NET + Git.TryRevokeInterfaceFromGlobal(_cookie); +#else try { - Git.Value.RevokeInterfaceFromGlobal(_cookie); + Git.RevokeInterfaceFromGlobal(_cookie); } - catch(ArgumentException) + catch (ArgumentException) { // Revoking cookie from GIT table may fail if apartment is gone. } +#endif } disposed = true; } } - private static unsafe IGlobalInterfaceTable GetGitTable() +#if NET + private static unsafe ABI.WinRT.Interop.IGlobalInterfaceTable GetGitTable() +#else + private static unsafe global::WinRT.Interop.IGlobalInterfaceTable GetGitTable() +#endif { Guid gitClsid = CLSID_StdGlobalInterfaceTable; - Guid gitIid = ABI.WinRT.Interop.IGlobalInterfaceTable.IID; + Guid gitIid = IID.IID_IGlobalInterfaceTable; IntPtr gitPtr = default; try { Marshal.ThrowExceptionForHR(Platform.CoCreateInstance( - ref gitClsid, + &gitClsid, IntPtr.Zero, 1 /*CLSCTX_INPROC_SERVER*/, - ref gitIid, + &gitIid, &gitPtr)); - return ABI.WinRT.Interop.IGlobalInterfaceTable.FromAbi(gitPtr).AsType(); + +#if NET + return new ABI.WinRT.Interop.IGlobalInterfaceTable(gitPtr); +#else + return new ABI.WinRT.Interop.IGlobalInterfaceTable(ABI.WinRT.Interop.IGlobalInterfaceTable.FromAbi(gitPtr)); +#endif } finally { - MarshalInterface.DisposeAbi(gitPtr); + MarshalExtensions.ReleaseIfNotNull(gitPtr); } } @@ -127,9 +173,9 @@ sealed class AgileReference : AgileReference public AgileReference(IObjectReference instance) : base(instance) { - } - - internal AgileReference(in ObjectReferenceValue instance) + } + + internal AgileReference(in ObjectReferenceValue instance) : base(instance) { } diff --git a/src/WinRT.Runtime/ApiCompatBaseline.net5.0.txt b/src/WinRT.Runtime/ApiCompatBaseline.net5.0.txt deleted file mode 100644 index 43c05f44f..000000000 --- a/src/WinRT.Runtime/ApiCompatBaseline.net5.0.txt +++ /dev/null @@ -1,13 +0,0 @@ -Compat issues with assembly WinRT.Runtime: -CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.CompilerGeneratedAttribute' exists on 'WinRT.IInspectable.WinRT.IWinRTObject.get_AdditionalTypeData()' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.CompilerGeneratedAttribute' exists on 'WinRT.IInspectable.WinRT.IWinRTObject.get_QueryInterfaceCache()' in the contract but not the implementation. -MembersMustExist : Member 'public WinRT.MarshalString.HStringHeader WinRT.MarshalString.HStringHeader WinRT.MarshalString._header' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.IntPtr System.IntPtr WinRT.MarshalString._handle' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public System.Runtime.InteropServices.GCHandle System.Runtime.InteropServices.GCHandle WinRT.MarshalString._gchandle' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void WinRT.MarshalString..ctor()' does not exist in the implementation but it does exist in the contract. -TypesMustExist : Type 'WinRT.MarshalString.HStringHeader' does not exist in the implementation but it does exist in the contract. -CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.CompilerGeneratedAttribute' exists on 'WinRT.SingleInterfaceOptimizedObject.WinRT.IWinRTObject.get_AdditionalTypeData()' in the contract but not the implementation. -CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.CompilerGeneratedAttribute' exists on 'WinRT.SingleInterfaceOptimizedObject.WinRT.IWinRTObject.get_QueryInterfaceCache()' in the contract but not the implementation. -MembersMustExist : Member 'public function System.Int32 (System.IntPtr, System.Guid, System.IntPtr) WinRT.Interop.IUnknownVftbl.QueryInterface.get()' does not exist in the implementation but it does exist in the contract. -MembersMustExist : Member 'public void WinRT.Interop.IUnknownVftbl.QueryInterface.set(function System.Int32 (System.IntPtr, System.Guid, System.IntPtr))' does not exist in the implementation but it does exist in the contract. -Total Issues: 11 diff --git a/src/WinRT.Runtime/ApiCompatBaseline.net8.0.txt b/src/WinRT.Runtime/ApiCompatBaseline.net8.0.txt new file mode 100644 index 000000000..98b465ea4 --- /dev/null +++ b/src/WinRT.Runtime/ApiCompatBaseline.net8.0.txt @@ -0,0 +1,8 @@ +Compat issues with assembly WinRT.Runtime: +CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'WinRT.GeneratedBindableCustomPropertyAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Class, Inherited=false, AllowMultiple=false)]' in the contract to '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Struct, Inherited=false, AllowMultiple=false)]' in the implementation. +CannotRemoveAttribute : Attribute 'System.ComponentModel.EditorBrowsableAttribute' exists on 'WinRT.WinRTExposedTypeAttribute' in the contract but not the implementation. +ParameterNamesCannotChange : Parameter name on member 'WinRT.ActivationFactory.Get(System.String)' is 'activatableClassId' in the implementation but 'typeName' in the contract. +ParameterNamesCannotChange : Parameter name on member 'WinRT.ActivationFactory.Get(System.String, System.Guid)' is 'activatableClassId' in the implementation but 'typeName' in the contract. +CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.NullableAttribute' exists on 'WinRT.EventRegistrationTokenTable' in the contract but not the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.NullableContextAttribute' exists on 'WinRT.EventRegistrationTokenTable' in the contract but not the implementation. +Total Issues: 6 diff --git a/src/WinRT.Runtime/AttributeMessages.net5.cs b/src/WinRT.Runtime/AttributeMessages.net5.cs new file mode 100644 index 000000000..ad9509af8 --- /dev/null +++ b/src/WinRT.Runtime/AttributeMessages.net5.cs @@ -0,0 +1,33 @@ +namespace WinRT +{ + /// + /// Messages for attributes used on CsWinRT APIs. + /// + internal static class AttributeMessages + { + /// + /// Message for a generic deprecated message that's annotated with . + /// + public const string GenericDeprecatedMessage = "This method is deprecated and will be removed in a future release."; + + /// + /// Message for a generic annotation for a method using . + /// + public const string GenericRequiresUnreferencedCodeMessage = "This method is not trim-safe, and is only supported for use when not using trimming (or AOT)."; + + /// + /// Message for marshalling or generic code requiring . + /// + public const string MarshallingOrGenericInstantiationsRequiresDynamicCode = "The necessary marshalling code or generic instantiations might not be available."; + + /// + /// Message for APIs not supported when dynamic code is not available (ie. in AOT environments). + /// + public const string NotSupportedIfDynamicCodeIsNotAvailable = "The annotated API is not supported when dynamic code is not available (ie. in AOT environments)."; + + /// + /// Message for suppressing trim warnings for calls with ABI types as type arguments for constrained type parameters. + /// + public const string AbiTypesNeverHaveConstructors = "All ABI types never have a constructor that would need to be accessed via reflection."; + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime/Attributes.cs b/src/WinRT.Runtime/Attributes.cs index 79cbf58fb..41a410351 100644 --- a/src/WinRT.Runtime/Attributes.cs +++ b/src/WinRT.Runtime/Attributes.cs @@ -1,14 +1,15 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.ComponentModel; using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; namespace WinRT { [EditorBrowsable(EditorBrowsableState.Never)] - [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] #if EMBED internal #else @@ -30,6 +31,9 @@ public ProjectedRuntimeClassAttribute(Type defaultInterface) public Type DefaultInterface { get; } } +#if NET + [Obsolete("This attribute is only used for the .NET Standard 2.0 projections.")] +#endif [EditorBrowsable(EditorBrowsableState.Never)] [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] #if EMBED @@ -64,9 +68,16 @@ public WindowsRuntimeTypeAttribute(string sourceMetadata = null) SourceMetadata = sourceMetadata; } + public WindowsRuntimeTypeAttribute(string sourceMetadata, string guidSignature) + :this(sourceMetadata) + { + GuidSignature = guidSignature; + } + public string SourceMetadata { get; } - } - + public string GuidSignature { get; } + } + /// /// When applied to a type, it specifies the provided type, if one is provided, is the ABI helper type for this type. /// @@ -75,29 +86,334 @@ public WindowsRuntimeTypeAttribute(string sourceMetadata = null) #if EMBED internal #else - public + public #endif sealed class WindowsRuntimeHelperTypeAttribute : Attribute - { - // Indicates no associated helper types (i.e. blittable types). + { + // Indicates no associated helper types (i.e. blittable types). public WindowsRuntimeHelperTypeAttribute() { } - public WindowsRuntimeHelperTypeAttribute( + public WindowsRuntimeHelperTypeAttribute( #if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicFields)] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicFields)] #endif Type helperType) { HelperType = helperType; - } - + } + #if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicFields)] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicFields)] #endif public Type HelperType { get; } } + +#if NET + + /// + /// An interface implemented by types used with to provide vtable entries for the annotated type. + /// Types implementing this interface can supply precomputed vtable entries to ensure WinRT marshalling can work with trimming and AOT. + /// + /// + /// This interface is primarily meant to be used by types provided by CsWinRT, or generated at compile time by its source generators. + /// However, it is possible to implement it manually as well, to support advanced scenarios where further customization was needed. + /// +#if EMBED + internal +#else + public +#endif + interface IWinRTExposedTypeDetails + { + /// + /// Gets the array of values representing vtable entries to expose to native code. + /// + /// The vtable entries to expose to native code for the annotated type. + ComWrappers.ComInterfaceEntry[] GetExposedInterfaces(); + } + + /// + /// An attribute that allows associating an implementation type with an annotated type. + /// + /// + /// This attribute is usually automatically generated by the CsWinRT AOT generator, for all types implementing WinRT interfaces + /// or deriving from a WinRT exposed type. However, it is also possible to use this manually to further customize the behavior. + /// When that is the case, the AOT generator will detect the presence of this attribute and skip processing that type. + /// +#if !NET8_0_OR_GREATER + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Delegate | AttributeTargets.Struct | AttributeTargets.Enum, Inherited = false, AllowMultiple = false)] +#if EMBED + internal +#else + public +#endif + sealed class WinRTExposedTypeAttribute : Attribute + { + /// + /// Creates a new instance. + /// + /// + /// Using this constructor will supply no additional vtable entries to the annotated type, other than the default ones (eg. for ). + /// + public WinRTExposedTypeAttribute() + { + } + + /// + /// Creates a new instance with the specified parameters. + /// + /// The implementation type to use to retrieve vtable entries for the annotated type. + public WinRTExposedTypeAttribute( + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] + Type winrtExposedTypeDetails) + { + WinRTExposedTypeDetails = winrtExposedTypeDetails; + } + + /// + public ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + { + return WinRTExposedTypeDetails is not null ? + ((IWinRTExposedTypeDetails)Activator.CreateInstance(WinRTExposedTypeDetails)).GetExposedInterfaces() : + Array.Empty(); + } + +#if NET + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] +#endif + internal Type WinRTExposedTypeDetails { get; } + } + +#if NET8_0_OR_GREATER + /// + /// An implementation for types that are explicitly not meant to be marshalled to native code. + /// + /// + /// This type can be used as argument for on any managed types that are not meant to be marshalled, + /// but that would otherwise trigger the CsWinRT AOT generator (because they implement a WinRT interface, or derive from a WinRT exposed + /// type). When support for marshalling is not needed, using this type allows suppressing warnings, and disabling that code generation. + /// This can also provide a small binary size improvement in such cases, as that unnecessary marshalling code will not be emitted. + /// +#if EMBED + internal +#else + public +#endif + sealed class WinRTManagedOnlyTypeDetails : IWinRTExposedTypeDetails + { + /// + public ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + { + ThrowNotSupportedException(); + + return Array.Empty(); + } + + /// + /// Throws a to signal that a blocked type is being marshalled. + /// + [DoesNotReturn] + private static void ThrowNotSupportedException() + { + throw new NotSupportedException( + "The annotated type does not support WinRT marshalling, and can only be used by managed code. If you do intend " + + "on marshalling it, remove the '[WinRTExposedType(typeof(WinRTManagedOnlyTypeDetails))]' annotation on it, " + + "and mark the type as partial, to allow the CsWinRT AOT generator to emit the necessary marshalling code for it."); + } + } +#endif + + /// + /// An attributes used for generated RCW types, to expose a factory method for them. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] +#if EMBED + internal +#else + public +#endif + abstract class WinRTImplementationTypeRcwFactoryAttribute : Attribute + { + /// + /// Creates a new instance of a given RCW type, from an input object. + /// + /// The native object to use to construct the RCW instance. + /// The resulting RCW instance wrapping the same native object as . + public abstract object CreateInstance(IInspectable inspectable); + } + + /// + /// An attributes used to explicitly indicate ther runtime class name to use for WinRT exposed types. + /// + /// This attribute is emitted by the CsWinRT generator for non-authored types implementing WinRT interfaces. + [EditorBrowsable(EditorBrowsableState.Never)] + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] +#if EMBED + internal +#else + public +#endif + sealed class WinRTRuntimeClassNameAttribute : Attribute + { + /// + /// Creates a new instance with the specified parameters. + /// + /// The runtime class name to use. + public WinRTRuntimeClassNameAttribute(string runtimeClassName) + { + RuntimeClassName = runtimeClassName; + } + + /// + /// Gets the runtime class name for the current instance. + /// + public string RuntimeClassName { get; } + } + + /// + /// An attribute used to indicate which generated type contains the exported functions for a given WinRT component assembly. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = false)] +#if EMBED + internal +#else + public +#endif + sealed class WinRTAssemblyExportsTypeAttribute : Attribute + { + /// + /// Creates a new instance with the specified parameters. + /// + /// The type containing the exported functions for the current WinRT component assembly. + public WinRTAssemblyExportsTypeAttribute(Type type) + { + Type = type; + } + + /// + /// Gets the type containing the exported functions for the current WinRT component assembly + /// + public Type Type { get; } + } + + /// + /// An attribute used to indicate the properties which are bindable via the implementation + /// provided for use in WinUI scenarios. The type which this attribute is placed on also needs to be marked partial and needs to be non-generic. + /// + /// + /// This type also provides equivalent support for the UWP XAML interface (as it shares the same IID as the WinUI type). + /// +#if NET8_0_OR_GREATER + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)] +#else + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] +#endif +#if EMBED + internal +#else + public +#endif + sealed class GeneratedBindableCustomPropertyAttribute : Attribute + { + /// + /// Marks all public properties as bindable. + /// + public GeneratedBindableCustomPropertyAttribute() + { + } + + /// + /// Marks the specified public properties as bindable. + /// + /// The name of the non-indexer public properties to mark as bindable. + /// The parameter type of the indexer public properties to mark as bindable. + public GeneratedBindableCustomPropertyAttribute(string[] propertyNames, Type[] indexerPropertyTypes) + { + PropertyNames = propertyNames; + IndexerPropertyTypes = indexerPropertyTypes; + } + + internal string[] PropertyNames { get; } + internal Type[] IndexerPropertyTypes { get; } + } +#endif + +#if NET8_0_OR_GREATER + /// + /// An attribute used to indicate that the type definition it is put on + /// is exposed to the WinRT ABI and needs a vtable generated for it. + /// The type which this attribute is placed on also needs to be marked partial. + /// + [AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)] +#if EMBED + internal +#else + public +#endif + sealed class GeneratedWinRTExposedTypeAttribute : Attribute + { + /// + /// Marks the type it is put on as exposed to the WinRT ABI + /// so that a vtable can be generated for it. + /// + public GeneratedWinRTExposedTypeAttribute() + { + } + } + + /// + /// An attribute used to indicate that the the type passed to the constructor + /// is exposed to the WinRT ABI and needs a vtable generated for it. + /// + [AttributeUsage(AttributeTargets.Assembly, Inherited = false, AllowMultiple = true)] +#if EMBED + internal +#else + public +#endif + sealed class GeneratedWinRTExposedExternalTypeAttribute : Attribute + { + /// + /// Marks the given type as exposed to the WinRT ABI so that a vtable can be generated for it. + /// + public GeneratedWinRTExposedExternalTypeAttribute(Type type) + { + Type = type; + } + + internal Type Type { get; } + } +#endif + +#if NET8_0_OR_GREATER + /// + /// An attribute that indicates that the annotated member or field (initializer) is performing a dynamic cast to a Windows Runtime type. + /// This in turns ensures that the necessary metadata is kept during trimming so that the cast can work correctly in all scenarios. + /// + /// + [AttributeUsage(AttributeTargets.Constructor | AttributeTargets.Method | AttributeTargets.Field, AllowMultiple = true, Inherited = false)] +#if EMBED + internal +#else + public +#endif + sealed class DynamicWindowsRuntimeCastAttribute : Attribute + { + /// + /// Creates a new instance with the specified parameters. + /// + /// The Windows Runtime type being used for the cast. + public DynamicWindowsRuntimeCastAttribute(Type type) + { + } + } +#endif } namespace System.Runtime.InteropServices.WindowsRuntime @@ -106,7 +422,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime #if EMBED internal #else - public + public #endif sealed class ReadOnlyArrayAttribute : Attribute { @@ -116,7 +432,7 @@ sealed class ReadOnlyArrayAttribute : Attribute #if EMBED internal #else - public + public #endif sealed class WriteOnlyArrayAttribute : Attribute { diff --git a/src/WinRT.Runtime/CastExtensions.cs b/src/WinRT.Runtime/CastExtensions.cs index 525da25a9..a8a120b82 100644 --- a/src/WinRT.Runtime/CastExtensions.cs +++ b/src/WinRT.Runtime/CastExtensions.cs @@ -2,7 +2,10 @@ // Licensed under the MIT License. using System; +#if !NET using System.Reflection; +#endif +using System.Runtime.CompilerServices; using WinRT.Interop; namespace WinRT @@ -28,9 +31,11 @@ static class CastExtensions /// Thrown if the runtime type of is not a projected type (if the object is a managed object). public static TInterface As(this object value) { + IObjectReference objRef; + if (typeof(TInterface) == typeof(object)) { - if (TryGetRefForObject(value, allowComposed: false, out IObjectReference objRef)) + if (TryGetRefForObject(value, allowComposed: false, out objRef)) { using (objRef) { @@ -44,10 +49,31 @@ public static TInterface As(this object value) return convertableInMetadata; } - using (var objRef = GetRefForObject(value)) + if (TryGetRefForObject(value, allowComposed: true, out objRef)) { - return objRef.AsInterface(); + using (objRef) + { + return objRef.AsInterface(); + } + } + + // Shared helper to get an exception with a helpful message, that we can efficiently throw + [MethodImpl(MethodImplOptions.NoInlining)] + static Exception GetArgumentExceptionForFailedCast(object value) + { + if (value is null) + { + return new ArgumentNullException( + paramName: nameof(value), + message: $"The source object being cast to type '{typeof(TInterface)}' is 'null'."); + } + + return new ArgumentException( + paramName: nameof(value), + message: $"The source object type ('{value.GetType()}') being cast to type '{typeof(TInterface)}' is not a projected type, nor does it inherit from a projected type."); } + + throw GetArgumentExceptionForFailedCast(value); } /// @@ -68,46 +94,32 @@ public static AgileReference AsAgile(this T value) where T : class return new AgileReference(null); } - var marshal = Marshaler.CreateMarshaler2(value); + var objrefValue = MarshalInspectable.CreateMarshaler2(value); try { - if (marshal is IObjectReference objref) - { - return new AgileReference(objref); - } - else if (marshal is ObjectReferenceValue objrefValue) - { - return new AgileReference(objrefValue); - } + return new AgileReference(objrefValue); } finally { - Marshaler.DisposeMarshaler(marshal); + objrefValue.Dispose(); } - throw new InvalidOperationException($"Object type is not a projected type: {nameof(value)}."); } private static bool TryGetRefForObject(object value, bool allowComposed, out IObjectReference reference) { if (ComWrappersSupport.TryUnwrapObject(value, out var objRef)) { - reference = objRef.As(); + reference = objRef.As(IID.IID_IUnknown); return true; } else if (allowComposed && TryGetComposedRefForQI(value, out objRef)) { - reference = objRef.As(); + reference = objRef.As(IID.IID_IUnknown); return true; } reference = null; return false; } - - private static IObjectReference GetRefForObject(object value) - { - return TryGetRefForObject(value, allowComposed: true, out var objRef) ? objRef - : throw new ArgumentException("Source object type is not a projected type and does not inherit from a projected type.", nameof(value)); - } private static bool TryGetComposedRefForQI(object value, out IObjectReference objRef) { diff --git a/src/WinRT.Runtime/ComWrappersSupport.cs b/src/WinRT.Runtime/ComWrappersSupport.cs index a97ab73ac..cd38421a2 100644 --- a/src/WinRT.Runtime/ComWrappersSupport.cs +++ b/src/WinRT.Runtime/ComWrappersSupport.cs @@ -7,8 +7,6 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -23,6 +21,16 @@ namespace WinRT { + internal static class KeyValuePairHelper + { + internal static readonly ConcurrentDictionary KeyValuePairCCW = new(); + + internal static void TryAddKeyValuePairCCW(Type keyValuePairType, Guid iid, IntPtr abiToProjectionVftablePtr) + { + KeyValuePairCCW.TryAdd(keyValuePairType, new ComInterfaceEntry { IID = iid, Vtable = abiToProjectionVftablePtr }); + } + } + #if EMBED internal #else @@ -30,9 +38,10 @@ namespace WinRT #endif static partial class ComWrappersSupport { - private readonly static ConcurrentDictionary> TypedObjectFactoryCacheForType = new ConcurrentDictionary>(); - private readonly static ConditionalWeakTable CCWTable = new ConditionalWeakTable(); - private readonly static ConcurrentDictionary> DelegateFactoryCache = new ConcurrentDictionary>(); + internal const int GC_PRESSURE_BASE = 1000; + + private static readonly ConcurrentDictionary> TypedObjectFactoryCacheForType = new(); + private static readonly ConcurrentDictionary> DelegateFactoryCache = new(); public static TReturn MarshalDelegateInvoke(IntPtr thisPtr, Func invoke) where TDelegate : class, Delegate @@ -67,22 +76,23 @@ public static void MarshalDelegateInvoke(IntPtr thisPtr, Action invoke) // If we are free threaded, we do not need to keep track of context. // This can either be if the object implements IAgileObject or the free threaded marshaler. - internal unsafe static bool IsFreeThreaded(IObjectReference objRef) + internal unsafe static bool IsFreeThreaded(IntPtr iUnknown) { - if (objRef.TryAs(ABI.WinRT.Interop.IAgileObject.IID, out var agilePtr) >= 0) + if (Marshal.QueryInterface(iUnknown, ref Unsafe.AsRef(in IID.IID_IAgileObject), out var agilePtr) >= 0) { Marshal.Release(agilePtr); return true; } - else if (objRef.TryAs(ABI.WinRT.Interop.IMarshal.IID, out var marshalPtr) >= 0) + + if (Marshal.QueryInterface(iUnknown, ref Unsafe.AsRef(in IID.IID_IMarshal), out var marshalPtr) >= 0) { try { - Guid iid_IUnknown = IUnknownVftbl.IID; + Guid iid_IUnknown = IID.IID_IUnknown; Guid iid_unmarshalClass; Marshal.ThrowExceptionForHR((**(ABI.WinRT.Interop.IMarshal.Vftbl**)marshalPtr).GetUnmarshalClass_0( marshalPtr, &iid_IUnknown, IntPtr.Zero, MSHCTX.InProc, IntPtr.Zero, MSHLFLAGS.Normal, &iid_unmarshalClass)); - if (iid_unmarshalClass == ABI.WinRT.Interop.IMarshal.IID_InProcFreeThreadedMarshaler.Value) + if (iid_unmarshalClass == ABI.WinRT.Interop.IMarshal.IID_InProcFreeThreadedMarshaler) { return true; } @@ -95,11 +105,25 @@ internal unsafe static bool IsFreeThreaded(IObjectReference objRef) return false; } + internal unsafe static bool IsFreeThreaded(IObjectReference objRef) + { + var isFreeThreaded = IsFreeThreaded(objRef.ThisPtr); + // ThisPtr is owned by objRef, so need to make sure objRef stays alive. + GC.KeepAlive(objRef); + return isFreeThreaded; + } + public static IObjectReference GetObjectReferenceForInterface(IntPtr externalComObject) { - return GetObjectReferenceForInterface(externalComObject); + // Here the ptr itself might not point to IUnknown, but we are using IUnknown for the purposes of getting + // an agile reference if needed. Due to that and to keep back compat, making sure to not trigger a QI + // In addition, the ptr is already pointing to the correct interface which the IObjectReference is expected for. + return GetObjectReferenceForInterface(externalComObject, IID.IID_IUnknown, requireQI: false); } +#if NET + [RequiresUnreferencedCode(AttributeMessages.GenericRequiresUnreferencedCodeMessage)] +#endif public static ObjectReference GetObjectReferenceForInterface(IntPtr externalComObject) { if (externalComObject == IntPtr.Zero) @@ -107,46 +131,44 @@ public static ObjectReference GetObjectReferenceForInterface(IntPtr extern return null; } - ObjectReference objRef = ObjectReference.FromAbi(externalComObject); - if (IsFreeThreaded(objRef)) - { - return objRef; - } - else - { - using (objRef) - { - return new ObjectReferenceWithContext( - objRef.GetRef(), - Context.GetContextCallback(), - Context.GetContextToken()); - } - } + return ObjectReference.FromAbi(externalComObject); } public static ObjectReference GetObjectReferenceForInterface(IntPtr externalComObject, Guid iid) + { + return GetObjectReferenceForInterface(externalComObject, iid, requireQI: true); + } + + /// + /// Creates a object for a given COM pointer. + /// As part of this, the IID is set in the object + /// which is used in non agile scenarios. In addition, if is set to true, a QI to that is done. + /// Otherwise it is assumed, the passed COM pointer already points to the interface represented by the . + /// + /// The native object for which to construct the object. + /// The IID that represents the interface which the resulting object will be pointing to. + /// Whether to QI as part of returning the object. + /// The holding onto the pointer passed or its QI. + public static IObjectReference GetObjectReferenceForInterface(IntPtr externalComObject, Guid iid, bool requireQI) + { + return GetObjectReferenceForInterface(externalComObject, iid, requireQI); + } + + internal static ObjectReference GetObjectReferenceForInterface(IntPtr externalComObject, Guid iid, bool requireQI) { if (externalComObject == IntPtr.Zero) { return null; } - Marshal.ThrowExceptionForHR(Marshal.QueryInterface(externalComObject, ref iid, out IntPtr ptr)); - ObjectReference objRef = ObjectReference.Attach(ref ptr); - if (IsFreeThreaded(objRef)) + if (requireQI) { - return objRef; + Marshal.ThrowExceptionForHR(Marshal.QueryInterface(externalComObject, ref iid, out IntPtr ptr)); + return ObjectReference.Attach(ref ptr, iid); } else { - using (objRef) - { - return new ObjectReferenceWithContext( - objRef.GetRef(), - Context.GetContextCallback(), - Context.GetContextToken(), - iid); - } + return ObjectReference.FromAbi(externalComObject, iid); } } @@ -154,134 +176,268 @@ public static ObjectReference GetObjectReferenceForInterface(IntPtr extern public static void RegisterProjectionTypeBaseTypeMapping(IDictionary typeNameToBaseTypeNameMapping) => TypeNameSupport.RegisterProjectionTypeBaseTypeMapping(typeNameToBaseTypeNameMapping); - internal static object GetRuntimeClassCCWTypeIfAny(object obj) - { - var type = obj.GetType(); - var ccwType = type.GetRuntimeClassCCWType(); - if (ccwType != null) - { - return CCWTable.GetValue(obj, obj => { - var ccwConstructor = ccwType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.CreateInstance | BindingFlags.Instance, null, new[] { type }, null); - return ccwConstructor.Invoke(new[] { obj }); - }); - } - - return obj; - } + public static void RegisterAuthoringMetadataTypeLookup(Func authoringMetadataTypeLookup) => TypeExtensions.RegisterAuthoringMetadataTypeLookup(authoringMetadataTypeLookup); - internal static List GetInterfaceTableEntries( -#if NET6_0_OR_GREATER - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#elif NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] + public static void RegisterHelperType( + Type type, +#if NET + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | + DynamicallyAccessedMemberTypes.PublicFields)] #endif - Type type) + Type helperType) => TypeExtensions.HelperTypeCache.TryAdd(type, helperType); + + internal static List GetInterfaceTableEntries(Type type) { var entries = new List(); - bool hasCustomIMarshalInterface = false; + bool hasUserImplementedIMarshalInterface = false; + bool hasUserImplementedICustomPropertyProviderInterface = false; + bool hasWinrtExposedClassAttribute = false; - if (type.IsDelegate()) - { - // Delegates have no interfaces that they implement, so adding default WinRT entries. - var helperType = type.FindHelperType(); - if (helperType is object) - { - entries.Add(new ComInterfaceEntry - { - IID = GuidGenerator.GetIID(type), - Vtable = helperType.GetAbiToProjectionVftblPtr() - }); - } +#if NET + // Check whether the type itself has the WinRTTypeExposed attribute and if so + // use the new source generator approach. + var winrtExposedClassAttribute = type.GetCustomAttribute(false); - if (ShouldProvideIReference(type)) + // Handle scenario where it can be an authored type + // which means the attribute lives on the authoring metadata type. + if (winrtExposedClassAttribute == null) + { + // Using GetCCWType rather than GetRuntimeClassCCWType given we want to handle boxed value types. + var authoringMetadaType = type.GetCCWType(); + if (authoringMetadaType != null) { - entries.Add(IPropertyValueEntry); - entries.Add(ProvideIReference(type)); + winrtExposedClassAttribute = authoringMetadaType.GetCustomAttribute(false); } } - else + + if (winrtExposedClassAttribute != null) { - var objType = type.GetRuntimeClassCCWType() ?? type; - var interfaces = objType.GetInterfaces(); - foreach (var iface in interfaces) + hasWinrtExposedClassAttribute = true; + entries.AddRange(winrtExposedClassAttribute.GetExposedInterfaces()); + if (type.IsClass) { - if (Projections.IsTypeWindowsRuntimeType(iface)) + // Manual helper to save binary size (no LINQ, no lambdas) and get better performance + static bool GetHasCustomIMarshalInterface(List entries) { - var ifaceAbiType = iface.FindHelperType(); - Guid iid = GuidGenerator.GetIID(ifaceAbiType); - entries.Add(new ComInterfaceEntry + foreach (ref readonly ComInterfaceEntry entry in CollectionsMarshal.AsSpan(entries)) { - IID = iid, - Vtable = (IntPtr)ifaceAbiType.GetAbiToProjectionVftblPtr() - }); + if (entry.IID == IID.IID_IMarshal) + { + return true; + } + } + + return false; + } - if (!hasCustomIMarshalInterface && iid == ABI.WinRT.Interop.IMarshal.IID) + // Same as above, for 'ICustomPropertyProvider' (separate method for a small perf boost). + // The method is very tiny, so the code duplication is not really a concern here. + static bool GetHasICustomPropertyProviderInterface(List entries) + { + foreach (ref readonly ComInterfaceEntry entry in CollectionsMarshal.AsSpan(entries)) { - hasCustomIMarshalInterface = true; + if (entry.IID == IID.IID_ICustomPropertyProvider) + { + return true; + } } + + return false; } - if (iface.IsConstructedGenericType - && Projections.TryGetCompatibleWindowsRuntimeTypesForVariantType(iface, out var compatibleIfaces)) + hasUserImplementedIMarshalInterface = GetHasCustomIMarshalInterface(entries); + hasUserImplementedICustomPropertyProviderInterface = GetHasICustomPropertyProviderInterface(entries); + } + } + else if (type == typeof(global::System.EventHandler)) + { + hasWinrtExposedClassAttribute = true; + entries.AddRange(Projections.GetAbiEventHandlerExposedInterfaces()); + } + else if (type == typeof(global::System.ComponentModel.PropertyChangedEventHandler)) + { + hasWinrtExposedClassAttribute = true; + entries.AddRange(Projections.GetAbiPropertyChangedEventHandlerExposedInterfaces()); + } + else if (type == typeof(global::System.Collections.Specialized.NotifyCollectionChangedEventHandler)) + { + hasWinrtExposedClassAttribute = true; + entries.AddRange(Projections.GetAbiNotifyCollectionChangedEventHandlerExposedInterfaces()); + } + else if (ComInterfaceEntriesForType.TryGetValue(type, out var registeredEntries)) + { + hasWinrtExposedClassAttribute = true; + entries.AddRange(registeredEntries); + } + else if (!type.IsEnum && GetComInterfaceEntriesForTypeFromLookupTable(type) is var lookupTableEntries && lookupTableEntries != null) + { + hasWinrtExposedClassAttribute = true; + entries.AddRange(lookupTableEntries); + } + else if (RuntimeFeature.IsDynamicCodeCompiled) +#endif + { + static void AddInterfaceToVtable( + Type iface, + List entries, + ref bool hasUserImplementedIMarshalInterface, + ref bool hasUserImplementedICustomPropertyProviderInterface) + { + var interfaceHelperType = iface.FindHelperType(); + Guid iid = GuidGenerator.GetIID(interfaceHelperType); + entries.Add(new ComInterfaceEntry { - foreach (var compatibleIface in compatibleIfaces) + IID = GuidGenerator.GetIID(interfaceHelperType), + Vtable = interfaceHelperType.GetAbiToProjectionVftblPtr() + }); + + if (!hasUserImplementedIMarshalInterface && iid == IID.IID_IMarshal) + { + hasUserImplementedIMarshalInterface = true; + } + + if (!hasUserImplementedICustomPropertyProviderInterface && iid == IID.IID_ICustomPropertyProvider) + { + hasUserImplementedICustomPropertyProviderInterface = true; + } + } + +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Fallback method for JIT environments that is not trim-safe by design.")] + [UnconditionalSuppressMessage("Trimming", "IL2067", Justification = "Fallback method for JIT environments that is not trim-safe by design.")] + [UnconditionalSuppressMessage("Trimming", "IL2070", Justification = "Fallback method for JIT environments that is not trim-safe by design.")] + [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Fallback method for JIT environments that is not trim-safe by design.")] + [MethodImpl(MethodImplOptions.NoInlining)] +#endif + static void GetInterfaceTableEntriesForJitEnvironment( + Type type, + List entries, + ref bool hasUserImplementedIMarshalInterface, + ref bool hasUserImplementedICustomPropertyProviderInterface) + { + if (type.IsDelegate()) + { + // Delegates have no interfaces that they implement, so adding default WinRT entries. + var helperType = type.FindHelperType(); + if (helperType is object) { - var compatibleIfaceAbiType = compatibleIface.FindHelperType(); entries.Add(new ComInterfaceEntry { - IID = GuidGenerator.GetIID(compatibleIfaceAbiType), - Vtable = (IntPtr)compatibleIfaceAbiType.GetAbiToProjectionVftblPtr() + IID = GuidGenerator.GetIID(type), + Vtable = helperType.GetAbiToProjectionVftblPtr() }); } + + if (type.ShouldProvideIReference()) + { + entries.Add(IPropertyValueEntry); + entries.Add(ProvideIReference(type)); + } + } + else + { + var objType = type.GetRuntimeClassCCWType() ?? type; + var interfaces = objType.GetInterfaces(); + foreach (var iface in interfaces) + { + if (Projections.IsTypeWindowsRuntimeType(iface)) + { + AddInterfaceToVtable(iface, entries, ref hasUserImplementedIMarshalInterface, ref hasUserImplementedICustomPropertyProviderInterface); + } + + if (iface.IsConstructedGenericType +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + && Projections.TryGetCompatibleWindowsRuntimeTypesForVariantType(iface, null, out var compatibleIfaces)) +#pragma warning restore IL3050 + { + foreach (var compatibleIface in compatibleIfaces) + { + AddInterfaceToVtable(compatibleIface, entries, ref hasUserImplementedIMarshalInterface, ref hasUserImplementedICustomPropertyProviderInterface); + } + } + } } } - if (objType.IsGenericType && objType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.KeyValuePair<,>)) + GetInterfaceTableEntriesForJitEnvironment(type, entries, ref hasUserImplementedIMarshalInterface, ref hasUserImplementedICustomPropertyProviderInterface); + } + +#if !NET + // We can't easily determine from just the type + // if the array is an "single dimension index from zero"-array in .NET Standard 2.0, + // so just approximate it. + // (Other array types will be blocked in other code-paths anyway where we have an object.) + if (type.IsArray && type.GetArrayRank() == 1) +#else + if (type.IsSZArray) +#endif + { + // We treat arrays as if they implemented IIterable, IVector, and IVectorView (WinRT only) + var elementType = type.GetElementType(); + if (elementType.ShouldProvideIReference()) { - var ifaceAbiType = objType.FindHelperType(); + entries.Add(IPropertyValueEntry); + entries.Add(ProvideIReferenceArray(type)); + } + } + else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.KeyValuePair<,>) && Projections.IsTypeWindowsRuntimeType(type)) + { + if (KeyValuePairHelper.KeyValuePairCCW.TryGetValue(type, out var entry)) + { + entries.Add(entry); + } + else + { + var ifaceAbiType = type.FindHelperType(); entries.Add(new ComInterfaceEntry { IID = GuidGenerator.GetIID(ifaceAbiType), Vtable = (IntPtr)ifaceAbiType.GetAbiToProjectionVftblPtr() }); } - else if (ShouldProvideIReference(type)) + } + else if (!hasWinrtExposedClassAttribute) + { + // Splitting this check to ensure the linker can recognize the pattern correctly. + // See: https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. + if (type.ShouldProvideIReference()) { entries.Add(IPropertyValueEntry); entries.Add(ProvideIReference(type)); } - else if (ShouldProvideIReferenceArray(type)) - { - entries.Add(IPropertyValueEntry); - entries.Add(ProvideIReferenceArray(type)); - } } - + entries.Add(new ComInterfaceEntry { - IID = ManagedIStringableVftbl.IID, + IID = IID.IID_IStringable, Vtable = ManagedIStringableVftbl.AbiToProjectionVftablePtr }); - entries.Add(new ComInterfaceEntry + // There are two scenarios where we want to support 'ICustomPropertyProvider': + // - The user is explicitly implementing the interface on their type + // - The user is using '[GeneratedBindableCustomProperty]', which uses our internal CCW + if (FeatureSwitches.EnableICustomPropertyProviderSupport && !hasUserImplementedICustomPropertyProviderInterface) { - IID = ManagedCustomPropertyProviderVftbl.IID, - Vtable = ManagedCustomPropertyProviderVftbl.AbiToProjectionVftablePtr - }); + entries.Add(new ComInterfaceEntry + { + IID = IID.IID_ICustomPropertyProvider, + Vtable = ManagedCustomPropertyProviderVftbl.AbiToProjectionVftablePtr + }); + } entries.Add(new ComInterfaceEntry { - IID = ABI.WinRT.Interop.IWeakReferenceSource.IID, + IID = IID.IID_IWeakReferenceSource, Vtable = ABI.WinRT.Interop.IWeakReferenceSource.AbiToProjectionVftablePtr }); // Add IMarhal implemented using the free threaded marshaler // to all CCWs if it doesn't already have its own. - if (!hasCustomIMarshalInterface) + if (!hasUserImplementedIMarshalInterface) { entries.Add(new ComInterfaceEntry { - IID = ABI.WinRT.Interop.IMarshal.IID, + IID = IID.IID_IMarshal, Vtable = ABI.WinRT.Interop.IMarshal.Vftbl.AbiToProjectionVftablePtr }); } @@ -289,20 +445,20 @@ internal static List GetInterfaceTableEntries( // Add IAgileObject to all CCWs entries.Add(new ComInterfaceEntry { - IID = ABI.WinRT.Interop.IAgileObject.IID, + IID = IID.IID_IAgileObject, Vtable = IUnknownVftbl.AbiToProjectionVftblPtr }); entries.Add(new ComInterfaceEntry { - IID = InterfaceIIDs.IInspectable_IID, + IID = IID.IID_IInspectable, Vtable = IInspectable.Vftbl.AbiToProjectionVftablePtr }); // This should be the last entry as it is included / excluded based on the flags. entries.Add(new ComInterfaceEntry { - IID = IUnknownVftbl.IID, + IID = IID.IID_IUnknown, Vtable = IUnknownVftbl.AbiToProjectionVftblPtr }); @@ -310,7 +466,7 @@ internal static List GetInterfaceTableEntries( } #if NET - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + [UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode", Justification = "The existence of the ABI type implies the non-ABI type exists, as in authoring scenarios the ABI type is constructed from the non-ABI type.")] #endif internal static (InspectableInfo inspectableInfo, List interfaceTableEntries) PregenerateNativeTypeInformation(Type type) @@ -332,21 +488,6 @@ internal static (InspectableInfo inspectableInfo, List interf interfaceTableEntries); } - private static bool IsNullableT(Type implementationType) - { - return implementationType.IsGenericType && implementationType.GetGenericTypeDefinition() == typeof(System.Nullable<>); - } - - private static bool IsAbiNullableDelegate(Type implementationType) - { - return implementationType.IsGenericType && implementationType.GetGenericTypeDefinition() == typeof(ABI.System.Nullable_Delegate<>); - } - - private static bool IsIReferenceArray(Type implementationType) - { - return implementationType.IsGenericType && implementationType.GetGenericTypeDefinition() == typeof(Windows.Foundation.IReferenceArray<>); - } - private static Func CreateKeyValuePairFactory(Type type) { var createRcwFunc = (Func) type.GetHelperType().GetMethod("CreateRcw", BindingFlags.Public | BindingFlags.Static). @@ -354,88 +495,105 @@ private static Func CreateKeyValuePairFactory(Type type) return createRcwFunc; } - internal static Func CreateDelegateFactory(Type type) + internal static Func GetOrCreateDelegateFactory(Type type) { - return DelegateFactoryCache.GetOrAdd(type, (type) => - { - var createRcwFunc = (Func)type.GetHelperType().GetMethod("CreateRcw", BindingFlags.Public | BindingFlags.Static). - CreateDelegate(typeof(Func)); - var iid = GuidGenerator.GetIID(type); + return DelegateFactoryCache.GetOrAdd(type, CreateDelegateFactory); + } + +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2067", + Justification = "The type is a delegate type, so 'GuidGenerator.GetIID' doesn't need to access public fields from it (it uses the helper type).")] +#endif + private static Func CreateDelegateFactory(Type type) + { + var createRcwFunc = (Func)type.GetHelperType().GetMethod("CreateRcw", BindingFlags.Public | BindingFlags.Static). + CreateDelegate(typeof(Func)); + var iid = GuidGenerator.GetIID(type); - return (IntPtr externalComObject) => + return (IntPtr externalComObject) => + { + // The CreateRCW function for delegates expect the pointer to be the delegate interface in CsWinRT 1.5. + // But CreateObject is passed the IUnknown interface. This would typically be fine for delegates as delegates + // don't implement interfaces and implementations typically have both the IUnknown vtable and the delegate + // vtable point to the same vtable. But when the pointer is to a proxy, that can not be relied on. + Marshal.ThrowExceptionForHR(Marshal.QueryInterface(externalComObject, ref iid, out var ptr)); + try { - // The CreateRCW function for delegates expect the pointer to be the delegate interface in CsWinRT 1.5. - // But CreateObject is passed the IUnknown interface. This would typically be fine for delegates as delegates - // don't implement interfaces and implementations typically have both the IUnknown vtable and the delegate - // vtable point to the same vtable. But when the pointer is to a proxy, that can not be relied on. - Marshal.ThrowExceptionForHR(Marshal.QueryInterface(externalComObject, ref iid, out var ptr)); - try - { - return createRcwFunc(ptr); - } - finally - { - Marshal.Release(ptr); - } - }; - }); + return createRcwFunc(ptr); + } + finally + { + Marshal.Release(ptr); + } + }; } - private static Func CreateNullableTFactory(Type implementationType) + public static bool RegisterDelegateFactory(Type implementationType, Func delegateFactory) => DelegateFactoryCache.TryAdd(implementationType, delegateFactory); + + internal static Func CreateNullableTFactory(Type implementationType) { - var getValueMethod = implementationType.GetHelperType().GetMethod("GetValue", BindingFlags.Static | BindingFlags.NonPublic); + var getValueMethod = implementationType.GetHelperType().GetMethod("GetValue", BindingFlags.Static | BindingFlags.Public); return (IInspectable obj) => getValueMethod.Invoke(null, new[] { obj }); } - private static Func CreateAbiNullableTFactory( + internal static Func CreateAbiNullableTFactory( #if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicMethods)] + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] #endif Type implementationType) { - var getValueMethod = implementationType.GetMethod("GetValue", BindingFlags.Static | BindingFlags.NonPublic); - return (IInspectable obj) => getValueMethod.Invoke(null, new[] { obj }); + // This method is only called when 'implementationType' has been validated to be some ABI.System.Nullable_Delegate. + // As such, we know that the type definitely has a method with signature 'static Nullable GetValue(IInspectable)'. + var getValueMethod = implementationType.GetMethod("GetValue", BindingFlags.Static | BindingFlags.Public); + + return (Func)getValueMethod.CreateDelegate(typeof(Func)); } private static Func CreateArrayFactory(Type implementationType) { - var getValueFunc = (Func)implementationType.GetHelperType().GetMethod("GetValue", BindingFlags.Static | BindingFlags.NonPublic). - CreateDelegate(typeof(Func)); - return getValueFunc; + // This method is only called when 'implementationType' is some 'Windows.Foundation.IReferenceArray' type. + // That interface is only implemented by 'ABI.Windows.Foundation.IReferenceArray', and the method is public. + var getValueMethod = implementationType.GetHelperType().GetMethod("GetValue", BindingFlags.Static | BindingFlags.Public); + + return (Func)getValueMethod.CreateDelegate(typeof(Func)); } // This is used to hold the reference to the native value type object (IReference) until the actual value in it (boxed as an object) gets cleaned up by GC // This is done to avoid pointer reuse until GC cleans up the boxed object - private static readonly ConditionalWeakTable _boxedValueReferenceCache = new(); + internal static readonly ConditionalWeakTable BoxedValueReferenceCache = new(); - private static Func CreateReferenceCachingFactory(Func internalFactory) + internal static Func CreateReferenceCachingFactory(Func internalFactory) { - return inspectable => - { - object resultingObject = internalFactory(inspectable); - _boxedValueReferenceCache.Add(resultingObject, inspectable); - return resultingObject; - }; + return internalFactory.InvokeWithBoxedValueReferenceCacheInsertion; } - private static Func CreateCustomTypeMappingFactory(Type customTypeHelperType) + private static Func CreateCustomTypeMappingFactory( +#if NET + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] +#endif + Type customTypeHelperType) { - var fromAbiMethod = customTypeHelperType.GetMethod("FromAbi", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + var fromAbiMethod = customTypeHelperType.GetMethod("FromAbi", BindingFlags.Public | BindingFlags.Static); if (fromAbiMethod is null) { throw new MissingMethodException(); } var fromAbiMethodFunc = (Func) fromAbiMethod.CreateDelegate(typeof(Func)); - return (IInspectable obj) => fromAbiMethodFunc(obj.ThisPtr); + return (IInspectable obj) => + { + var fromAbiMethod = fromAbiMethodFunc(obj.ThisPtr); + GC.KeepAlive(obj); + return fromAbiMethod; + }; } - internal static Func CreateTypedRcwFactory( -#if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] -#endif - Type implementationType, - string runtimeClassName = null) + internal static Func CreateTypedRcwFactory(Type implementationType) + { + return CreateTypedRcwFactory(implementationType, null); + } + + internal static Func CreateTypedRcwFactory(Type implementationType, string runtimeClassName) { // If runtime class name is empty or "Object", then just use IInspectable. if (implementationType == null || implementationType == typeof(object)) @@ -445,58 +603,67 @@ internal static Func CreateTypedRcwFactory( return (IInspectable obj) => obj; } - if (implementationType == typeof(ABI.System.Nullable_string)) - { - return CreateReferenceCachingFactory((IInspectable obj) => ABI.System.Nullable_string.GetValue(obj)); - } - else if (implementationType == typeof(ABI.System.Nullable_Type)) - { - return CreateReferenceCachingFactory((IInspectable obj) => ABI.System.Nullable_Type.GetValue(obj)); - } - - var customHelperType = Projections.FindCustomHelperTypeMapping(implementationType, true); - if (customHelperType != null) + if (implementationType == typeof(string) || + implementationType == typeof(Type) || + implementationType == typeof(Exception) || + implementationType.IsDelegate()) { - return CreateReferenceCachingFactory(CreateCustomTypeMappingFactory(customHelperType)); - } - - if (implementationType.IsGenericType && implementationType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.KeyValuePair<,>)) - { - return CreateReferenceCachingFactory(CreateKeyValuePairFactory(implementationType)); + return ABI.System.NullableType.GetValueFactory(implementationType); } if (implementationType.IsValueType) { - if (IsNullableT(implementationType)) + if (implementationType.IsGenericType && implementationType.GetGenericTypeDefinition() == typeof(System.Collections.Generic.KeyValuePair<,>)) { - return CreateReferenceCachingFactory(CreateNullableTFactory(implementationType)); + return CreateReferenceCachingFactory(CreateKeyValuePairFactory(implementationType)); + } + else if (implementationType.IsNullableT()) + { + return ABI.System.NullableType.GetValueFactory(implementationType.GetGenericArguments()[0]); } else { - return CreateReferenceCachingFactory(CreateNullableTFactory(typeof(System.Nullable<>).MakeGenericType(implementationType))); + return ABI.System.NullableType.GetValueFactory(implementationType); } } - else if (IsAbiNullableDelegate(implementationType)) + + var customHelperType = Projections.FindCustomHelperTypeMapping(implementationType, true); + if (customHelperType != null) { - return CreateReferenceCachingFactory(CreateAbiNullableTFactory(implementationType)); + return CreateReferenceCachingFactory(CreateCustomTypeMappingFactory(customHelperType)); } - else if (IsIReferenceArray(implementationType)) + + if (implementationType.IsIReferenceArray()) { return CreateReferenceCachingFactory(CreateArrayFactory(implementationType)); } +#if NET + if (implementationType.IsSZArray) + { + return ABI.Windows.Foundation.IReferenceArrayType.GetValueFactory(implementationType.GetElementType()); + } +#endif + return CreateFactoryForImplementationType(runtimeClassName, implementationType); } -#if NET - [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] -#endif internal static Type GetRuntimeClassForTypeCreation(IInspectable inspectable, Type staticallyDeterminedType) { string runtimeClassName = inspectable.GetRuntimeClassName(noThrow: true); + bool hasRuntimeClassName = !string.IsNullOrEmpty(runtimeClassName); + Type implementationType = null; - if (!string.IsNullOrEmpty(runtimeClassName)) + if (hasRuntimeClassName) { + // Check if this is a nullable type where there are no references to the nullable version, but + // there is to the actual type. + if (runtimeClassName.StartsWith("Windows.Foundation.IReference`1<", StringComparison.Ordinal)) + { + // runtimeClassName is of format Windows.Foundation.IReference`1. + return TypeNameSupport.FindRcwTypeByNameCached(runtimeClassName.Substring(32, runtimeClassName.Length - 33)); + } + implementationType = TypeNameSupport.FindRcwTypeByNameCached(runtimeClassName); } @@ -512,62 +679,70 @@ internal static Type GetRuntimeClassForTypeCreation(IInspectable inspectable, Ty // If it isn't, we use the statically determined type as it is a tear off. if (!(implementationType != null && (staticallyDeterminedType == implementationType || - staticallyDeterminedType.IsAssignableFrom(implementationType) || - staticallyDeterminedType.IsGenericType && implementationType.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == staticallyDeterminedType.GetGenericTypeDefinition())))) + staticallyDeterminedType.IsAssignableFrom(implementationType)))) { + // We register the type we ended up using so that if we need to create an RCW for the same runtime class name + // again without static type information, we can use this previous knowledge. This is specifcally useful in + // weak reference scenarios where ComWrappers may call us to rehydrate an RCW where we won't have static type information. + if (hasRuntimeClassName && staticallyDeterminedType.IsClass) + { + TypeNameSupport.RegisterBaseTypeForTypeName(runtimeClassName, staticallyDeterminedType); + } return staticallyDeterminedType; } } +#if NET + // On AOT, RCW type factories for generic interfaces are registered by the projection before it asks to create + // an RCW for it. But we can have scenarios where the actual implementation type is different than the statically + // known type and the implementation type's RCW factory hasn't been initialized. i.e. IList and IEnumerable + // where the latter is the statically known type whose RCW factory is added already but IList hasn't been added. + // In this case, since the caller just needs a IEnumerable, we fallback to the statically known type if IList + // isn't already added. On JIT, we can construct it at runtime. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + if (implementationType != null && + implementationType.IsInterface && + implementationType.IsGenericType && + !TypedObjectFactoryCacheForType.ContainsKey(implementationType)) + { + return staticallyDeterminedType; + } + } +#endif + return implementationType; } - private readonly static ConcurrentDictionary IsIReferenceTypeCache = new ConcurrentDictionary(); - private static bool IsIReferenceType(Type type) + private static ComInterfaceEntry IPropertyValueEntry { - static bool IsIReferenceTypeHelper(Type type) + get { - if (type.IsDefined(typeof(WindowsRuntimeTypeAttribute)) || - WinRT.Projections.IsTypeWindowsRuntimeType(type)) - return true; - type = type.GetAuthoringMetadataType(); - if (type is object) + if (!FeatureSwitches.EnableIReferenceSupport) { - if (type.IsDefined(typeof(WindowsRuntimeTypeAttribute)) || - WinRT.Projections.IsTypeWindowsRuntimeType(type)) - return true; + throw new NotSupportedException("Support for 'IPropertyValue' is not enabled (it depends on the support for 'IReference')."); } - return false; - } - return IsIReferenceTypeCache.GetOrAdd(type, (type) => - { - if (type == typeof(string) || type.IsTypeOfType()) - return true; - if (type.IsDelegate()) - return IsIReferenceTypeHelper(type); - if (!type.IsValueType) - return false; - return type.IsPrimitive || IsIReferenceTypeHelper(type); - }); + return new ComInterfaceEntry + { + IID = IID.IID_IPropertyValue, + Vtable = ManagedIPropertyValueImpl.AbiToProjectionVftablePtr + }; + } } - private static bool ShouldProvideIReference(Type type) => IsIReferenceType(type); - - private static ComInterfaceEntry IPropertyValueEntry => - new ComInterfaceEntry - { - IID = ManagedIPropertyValueImpl.IID, - Vtable = ManagedIPropertyValueImpl.AbiToProjectionVftablePtr - }; - private static ComInterfaceEntry ProvideIReference(Type type) { + if (!FeatureSwitches.EnableIReferenceSupport) + { + throw new NotSupportedException("Support for 'IReference' is not enabled."); + } + if (type == typeof(int)) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_int.IID, + IID = IID.IID_NullableInt, Vtable = ABI.System.Nullable_int.Vftbl.AbiToProjectionVftablePtr }; } @@ -575,7 +750,7 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_string.IID, + IID = IID.IID_NullableString, Vtable = ABI.System.Nullable_string.Vftbl.AbiToProjectionVftablePtr }; } @@ -583,7 +758,7 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_byte.IID, + IID = IID.IID_NullableByte, Vtable = ABI.System.Nullable_byte.Vftbl.AbiToProjectionVftablePtr }; } @@ -591,7 +766,7 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_short.IID, + IID = IID.IID_NullableShort, Vtable = ABI.System.Nullable_short.Vftbl.AbiToProjectionVftablePtr }; } @@ -599,7 +774,7 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_ushort.IID, + IID = IID.IID_NullableUShort, Vtable = ABI.System.Nullable_ushort.Vftbl.AbiToProjectionVftablePtr }; } @@ -607,7 +782,7 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_uint.IID, + IID = IID.IID_NullableUInt, Vtable = ABI.System.Nullable_uint.Vftbl.AbiToProjectionVftablePtr }; } @@ -615,7 +790,7 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_long.IID, + IID = IID.IID_NullableLong, Vtable = ABI.System.Nullable_long.Vftbl.AbiToProjectionVftablePtr }; } @@ -623,7 +798,7 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_ulong.IID, + IID = IID.IID_NullableULong, Vtable = ABI.System.Nullable_ulong.Vftbl.AbiToProjectionVftablePtr }; } @@ -631,7 +806,7 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_float.IID, + IID = IID.IID_NullableFloat, Vtable = ABI.System.Nullable_float.Vftbl.AbiToProjectionVftablePtr }; } @@ -639,7 +814,7 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_double.IID, + IID = IID.IID_NullableDouble, Vtable = ABI.System.Nullable_double.Vftbl.AbiToProjectionVftablePtr }; } @@ -647,7 +822,7 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_char.IID, + IID = IID.IID_NullableChar, Vtable = ABI.System.Nullable_char.Vftbl.AbiToProjectionVftablePtr }; } @@ -655,7 +830,7 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_bool.IID, + IID = IID.IID_NullableBool, Vtable = ABI.System.Nullable_bool.Vftbl.AbiToProjectionVftablePtr }; } @@ -663,7 +838,7 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_guid.IID, + IID = IID.IID_NullableGuid, Vtable = ABI.System.Nullable_guid.Vftbl.AbiToProjectionVftablePtr }; } @@ -671,7 +846,7 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_DateTimeOffset.IID, + IID = IID.IID_NullableDateTimeOffset, Vtable = ABI.System.Nullable_DateTimeOffset.Vftbl.AbiToProjectionVftablePtr }; } @@ -679,7 +854,7 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_TimeSpan.IID, + IID = IID.IID_NullableTimeSpan, Vtable = ABI.System.Nullable_TimeSpan.Vftbl.AbiToProjectionVftablePtr }; } @@ -687,7 +862,7 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_Object.IID, + IID = IID.IID_NullableObject, Vtable = ABI.System.Nullable_Object.Vftbl.AbiToProjectionVftablePtr }; } @@ -695,7 +870,7 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_Type.IID, + IID = IID.IID_NullableType, Vtable = ABI.System.Nullable_Type.Vftbl.AbiToProjectionVftablePtr }; } @@ -703,41 +878,149 @@ private static ComInterfaceEntry ProvideIReference(Type type) { return new ComInterfaceEntry { - IID = ABI.System.Nullable_sbyte.IID, + IID = IID.IID_NullableSByte, Vtable = ABI.System.Nullable_sbyte.Vftbl.AbiToProjectionVftablePtr }; } + if (type.IsEnum) + { + if (type.IsDefined(typeof(FlagsAttribute))) + { + return new ComInterfaceEntry + { + IID = ABI.System.Nullable_FlagsEnum.GetIID(type), + Vtable = ABI.System.Nullable_FlagsEnum.AbiToProjectionVftablePtr + }; + } + else + { + return new ComInterfaceEntry + { + IID = ABI.System.Nullable_IntEnum.GetIID(type), + Vtable = ABI.System.Nullable_IntEnum.AbiToProjectionVftablePtr + }; + } + } + if (type == typeof(EventHandler)) + { + return new ComInterfaceEntry + { + IID = IID.IID_NullableEventHandler, + Vtable = ABI.System.Nullable_EventHandler.AbiToProjectionVftablePtr + }; + } if (type.IsDelegate()) { +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"Cannot provide IReference`1 support for delegate type '{type}'."); + } +#endif + +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 var delegateHelperType = typeof(ABI.System.Nullable_Delegate<>).MakeGenericType(type); return new ComInterfaceEntry { IID = global::WinRT.GuidGenerator.GetIID(delegateHelperType), Vtable = delegateHelperType.GetAbiToProjectionVftblPtr() }; +#pragma warning restore IL3050 } + if (type == typeof(System.Numerics.Matrix3x2)) + { + return new ComInterfaceEntry + { + IID = IID.IID_IReferenceMatrix3x2, + Vtable = BoxedValueIReferenceImpl.AbiToProjectionVftablePtr + }; + } + if (type == typeof(System.Numerics.Matrix4x4)) + { + return new ComInterfaceEntry + { + IID = IID.IID_IReferenceMatrix4x4, + Vtable = BoxedValueIReferenceImpl.AbiToProjectionVftablePtr + }; + } + if (type == typeof(System.Numerics.Plane)) + { + return new ComInterfaceEntry + { + IID = IID.IID_IReferencePlane, + Vtable = BoxedValueIReferenceImpl.AbiToProjectionVftablePtr + }; + } + if (type == typeof(System.Numerics.Quaternion)) + { + return new ComInterfaceEntry + { + IID = IID.IID_IReferenceQuaternion, + Vtable = BoxedValueIReferenceImpl.AbiToProjectionVftablePtr + }; + } + if (type == typeof(System.Numerics.Vector2)) + { + return new ComInterfaceEntry + { + IID = IID.IID_IReferenceVector2, + Vtable = BoxedValueIReferenceImpl.AbiToProjectionVftablePtr + }; + } + if (type == typeof(System.Numerics.Vector3)) + { + return new ComInterfaceEntry + { + IID = IID.IID_IReferenceVector3, + Vtable = BoxedValueIReferenceImpl.AbiToProjectionVftablePtr + }; + } + if (type == typeof(System.Numerics.Vector4)) + { + return new ComInterfaceEntry + { + IID = IID.IID_IReferenceVector4, + Vtable = BoxedValueIReferenceImpl.AbiToProjectionVftablePtr + }; + } + if (type.IsTypeOfException()) + { + return new ComInterfaceEntry + { + IID = IID.IID_NullableException, + Vtable = ABI.System.Nullable_Exception.Vftbl.AbiToProjectionVftablePtr + }; + } + +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"Cannot provide IReference`1 support for type '{type}'."); + } +#endif +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 return new ComInterfaceEntry { IID = global::WinRT.GuidGenerator.GetIID(typeof(ABI.System.Nullable<>).MakeGenericType(type)), Vtable = typeof(BoxedValueIReferenceImpl<>).MakeGenericType(type).GetAbiToProjectionVftblPtr() }; - } - - private static bool ShouldProvideIReferenceArray(Type type) - { - // Check if one dimensional array with lower bound of 0 - return type.IsArray && type == type.GetElementType().MakeArrayType() && !type.GetElementType().IsArray; +#pragma warning restore IL3050 } private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { + if (!FeatureSwitches.EnableIReferenceSupport) + { + throw new NotSupportedException("Support for 'IReferenceArray' is not enabled."); + } + Type type = arrayType.GetElementType(); if (type == typeof(int)) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfInt32, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } @@ -745,7 +1028,7 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfString, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } @@ -753,7 +1036,7 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfByte, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } @@ -761,7 +1044,7 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfInt16, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } @@ -769,7 +1052,7 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfUInt16, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } @@ -777,7 +1060,7 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfUInt32, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } @@ -785,7 +1068,7 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfInt64, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } @@ -793,7 +1076,7 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfUInt64, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } @@ -801,7 +1084,7 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfSingle, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } @@ -809,7 +1092,7 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfDouble, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } @@ -817,7 +1100,7 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfChar, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } @@ -825,7 +1108,7 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfBoolean, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } @@ -833,7 +1116,7 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfGuid, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } @@ -841,7 +1124,7 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfDateTimeOffset, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } @@ -849,7 +1132,7 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfTimeSpan, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } @@ -857,7 +1140,7 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfObject, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } @@ -865,27 +1148,110 @@ private static ComInterfaceEntry ProvideIReferenceArray(Type arrayType) { return new ComInterfaceEntry { - IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray)), + IID = IID.IID_IReferenceArrayOfType, Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr }; } + if (type == typeof(System.Numerics.Matrix3x2)) + { + return new ComInterfaceEntry + { + IID = IID.IID_IReferenceArrayOfMatrix3x2, + Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr + }; + } + if (type == typeof(System.Numerics.Matrix4x4)) + { + return new ComInterfaceEntry + { + IID = IID.IID_IReferenceArrayOfMatrix4x4, + Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr + }; + } + if (type == typeof(System.Numerics.Plane)) + { + return new ComInterfaceEntry + { + IID = IID.IID_IReferenceArrayOfPlane, + Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr + }; + } + if (type == typeof(System.Numerics.Quaternion)) + { + return new ComInterfaceEntry + { + IID = IID.IID_IReferenceArrayOfQuaternion, + Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr + }; + } + if (type == typeof(System.Numerics.Vector2)) + { + return new ComInterfaceEntry + { + IID = IID.IID_IReferenceArrayOfVector2, + Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr + }; + } + if (type == typeof(System.Numerics.Vector3)) + { + return new ComInterfaceEntry + { + IID = IID.IID_IReferenceArrayOfVector3, + Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr + }; + } + if (type == typeof(System.Numerics.Vector4)) + { + return new ComInterfaceEntry + { + IID = IID.IID_IReferenceArrayOfVector4, + Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr + }; + } + if (type.IsTypeOfException()) + { + return new ComInterfaceEntry + { + IID = IID.IID_IReferenceArrayOfException, + Vtable = BoxedArrayIReferenceArrayImpl.AbiToProjectionVftablePtr + }; + } + +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"Cannot provide IReferenceArray`1 support for element type '{type}'."); + } +#endif + +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 return new ComInterfaceEntry { IID = global::WinRT.GuidGenerator.GetIID(typeof(IReferenceArray<>).MakeGenericType(type)), Vtable = (IntPtr)typeof(BoxedArrayIReferenceArrayImpl<>).MakeGenericType(type).GetAbiToProjectionVftblPtr() }; +#pragma warning restore IL3050 } internal sealed class InspectableInfo { - private readonly Lazy runtimeClassName; + private readonly Type _type; public Guid[] IIDs { get; } - public string RuntimeClassName => runtimeClassName.Value; + + private volatile string _runtimeClassName; + + private string MakeRuntimeClassName() + { + global::System.Threading.Interlocked.CompareExchange(ref _runtimeClassName, TypeNameSupport.GetNameForType(_type, TypeNameGenerationFlags.GenerateBoxedName | TypeNameGenerationFlags.ForGetRuntimeClassName), null); + return _runtimeClassName; + } + + public string RuntimeClassName => _runtimeClassName ?? MakeRuntimeClassName(); internal InspectableInfo(Type type, Guid[] iids) { - runtimeClassName = new Lazy(() => TypeNameSupport.GetNameForType(type, TypeNameGenerationFlags.GenerateBoxedName | TypeNameGenerationFlags.NoCustomTypeName)); + _type = type; IIDs = iids; } } @@ -893,7 +1259,7 @@ internal InspectableInfo(Type type, Guid[] iids) internal static ObjectReference CreateCCWForObject(object obj, Guid iid) { IntPtr ccw = CreateCCWForObjectForABI(obj, iid); - return ObjectReference.Attach(ref ccw); + return ObjectReference.Attach(ref ccw, iid); } internal static ObjectReferenceValue CreateCCWForObjectForMarshaling(object obj, Guid iid) diff --git a/src/WinRT.Runtime/ComWrappersSupport.net5.cs b/src/WinRT.Runtime/ComWrappersSupport.net5.cs index 24a0a6dc4..d144f385f 100644 --- a/src/WinRT.Runtime/ComWrappersSupport.net5.cs +++ b/src/WinRT.Runtime/ComWrappersSupport.net5.cs @@ -1,54 +1,53 @@ // Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections; +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using WinRT.Interop; -using static System.Runtime.InteropServices.ComWrappers; - -namespace WinRT -{ -#if EMBED - internal -#else - public -#endif - static partial class ComWrappersSupport - { - // Instance field and property for Singleton pattern: ComWrappers `set` method should be idempotent - private static DefaultComWrappers _instance; - private static DefaultComWrappers DefaultComWrappersInstance - { - get - { - if (_instance == null) - { - _instance = new DefaultComWrappers(); - } - return _instance; - } - } - - internal static readonly ConditionalWeakTable InspectableInfoTable = new ConditionalWeakTable(); +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using WinRT.Interop; +using static System.Runtime.InteropServices.ComWrappers; + +namespace WinRT +{ +#if EMBED + internal +#else + public +#endif + static partial class ComWrappersSupport + { + // Instance field and property for Singleton pattern: ComWrappers `set` method should be idempotent + private static DefaultComWrappers _instance; + private static DefaultComWrappers DefaultComWrappersInstance + { + get + { + if (_instance == null) + { + _instance = new DefaultComWrappers(); + } + return _instance; + } + } + + internal static readonly ConditionalWeakTable InspectableInfoTable = new(); [ThreadStatic] - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] internal static Type CreateRCWType; - - private static ComWrappers _comWrappers; - private static object _comWrappersLock = new object(); - private static ComWrappers ComWrappers - { - get - { - if (_comWrappers is null) - { + + private static ComWrappers _comWrappers; + private static readonly object _comWrappersLock = new object(); + private static ComWrappers ComWrappers + { + get + { + if (_comWrappers is null) + { lock (_comWrappersLock) { if (_comWrappers is null) @@ -57,13 +56,13 @@ private static ComWrappers ComWrappers #if !EMBED ComWrappers.RegisterForTrackerSupport(comWrappersToSet); #endif - _comWrappers = comWrappersToSet; - } - } - } - return _comWrappers; - } - set + _comWrappers = comWrappersToSet; + } + } + } + return _comWrappers; + } + set { lock (_comWrappersLock) { @@ -75,37 +74,37 @@ private static ComWrappers ComWrappers #if !EMBED ComWrappers.RegisterForTrackerSupport(comWrappersToSet); #endif - _comWrappers = comWrappersToSet; - } - } - } - - internal static unsafe InspectableInfo GetInspectableInfo(IntPtr pThis) - { - var _this = FindObject(pThis); - return InspectableInfoTable.GetValue(_this.GetType(), o => PregenerateNativeTypeInformation(o).inspectableInfo); - } - - public static T CreateRcwForComObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] T>(IntPtr ptr) + _comWrappers = comWrappersToSet; + } + } + } + + internal static unsafe InspectableInfo GetInspectableInfo(IntPtr pThis) + { + var _this = FindObject(pThis); + return InspectableInfoTable.GetValue(_this.GetType(), o => PregenerateNativeTypeInformation(o).inspectableInfo); + } + + public static T CreateRcwForComObject(IntPtr ptr) { return CreateRcwForComObject(ptr, true); - } - - private static T CreateRcwForComObject<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] T>(IntPtr ptr, bool tryUseCache) - { - if (ptr == IntPtr.Zero) - { - return default; - } - - // CreateRCWType is a thread local which is set here to communicate the statically known type - // when we are called by the ComWrappers API to create the object. We can't pass this through the - // ComWrappers API surface, so we are achieving it via a thread local. We unset it after in case - // there is other calls to it via other means. - CreateRCWType = typeof(T); - - var flags = tryUseCache ? CreateObjectFlags.TrackerObject : CreateObjectFlags.TrackerObject | CreateObjectFlags.UniqueInstance; - var rcw = ComWrappers.GetOrCreateObjectForComInstance(ptr, flags); + } + + private static T CreateRcwForComObject(IntPtr ptr, bool tryUseCache) + { + if (ptr == IntPtr.Zero) + { + return default; + } + + // CreateRCWType is a thread local which is set here to communicate the statically known type + // when we are called by the ComWrappers API to create the object. We can't pass this through the + // ComWrappers API surface, so we are achieving it via a thread local. We unset it after in case + // there is other calls to it via other means. + CreateRCWType = typeof(T); + + var flags = tryUseCache ? CreateObjectFlags.TrackerObject : CreateObjectFlags.TrackerObject | CreateObjectFlags.UniqueInstance; + var rcw = ComWrappers.GetOrCreateObjectForComInstance(ptr, flags); CreateRCWType = null; // Because .NET will de-duplicate strings and WinRT doesn't, // our RCW factory returns a wrapper of our string instance. @@ -115,12 +114,6 @@ internal static unsafe InspectableInfo GetInspectableInfo(IntPtr pThis) // We need to do the same thing for System.Type because there can be multiple WUX.Interop.TypeName's // for a single System.Type. - // Resurrect IWinRTObject's disposed IObjectReferences, if necessary - if (rcw is IWinRTObject winrtObj) - { - winrtObj.Resurrect(); - } - return rcw switch { ABI.System.Nullable nt => (T)nt.Value, @@ -128,7 +121,7 @@ internal static unsafe InspectableInfo GetInspectableInfo(IntPtr pThis) _ when tryUseCache => CreateRcwForComObject(ptr, false), _ => throw new ArgumentException(string.Format("Unable to create a wrapper object. The WinRT object {0} has type {1} which cannot be assigned to type {2}", ptr, rcw.GetType(), typeof(T))) }; - + } public static bool TryUnwrapObject(object o, out IObjectReference objRef) @@ -152,91 +145,144 @@ public static bool TryUnwrapObject(object o, out IObjectReference objRef) return false; } - public static void RegisterObjectForInterface(object obj, IntPtr thisPtr, CreateObjectFlags createObjectFlags) => - ComWrappers.GetOrRegisterObjectForComInstance(thisPtr, createObjectFlags, obj); + public static void RegisterObjectForInterface(object obj, IntPtr thisPtr, CreateObjectFlags createObjectFlags) => + ComWrappers.GetOrRegisterObjectForComInstance(thisPtr, createObjectFlags, obj); + + internal static void RegisterObjectForInterface(object obj, IntPtr thisPtr, IntPtr inner, CreateObjectFlags createObjectFlags) => + ComWrappers.GetOrRegisterObjectForComInstance(thisPtr, createObjectFlags, obj, inner); + + public static void RegisterObjectForInterface(object obj, IntPtr thisPtr) => + TryRegisterObjectForInterface(obj, thisPtr); - public static void RegisterObjectForInterface(object obj, IntPtr thisPtr) => - TryRegisterObjectForInterface(obj, thisPtr); - public static object TryRegisterObjectForInterface(object obj, IntPtr thisPtr) { - var rcw = ComWrappers.GetOrRegisterObjectForComInstance(thisPtr, CreateObjectFlags.TrackerObject, obj); + return ComWrappers.GetOrRegisterObjectForComInstance(thisPtr, CreateObjectFlags.TrackerObject, obj); + } + + internal static IntPtr CreateCCWForObjectUnsafe(object obj) + { + return ComWrappers.GetOrCreateComInterfaceForObject(obj, CreateComInterfaceFlags.TrackerSupport); + } - // Resurrect IWinRTObject's disposed IObjectReferences, if necessary - var target = rcw is Delegate del ? del.Target : rcw; - if (target is IWinRTObject winrtObj) - { - winrtObj.Resurrect(); - } - return rcw; - } - - public static IObjectReference CreateCCWForObject(object obj) - { - IntPtr ccw = ComWrappers.GetOrCreateComInterfaceForObject(obj, CreateComInterfaceFlags.TrackerSupport); - return ObjectReference.Attach(ref ccw); + public static IObjectReference CreateCCWForObject(object obj) + { + IntPtr ccw = ComWrappers.GetOrCreateComInterfaceForObject(obj, CreateComInterfaceFlags.TrackerSupport); + return ObjectReference.AttachFreeThreadedUnsafe(ref ccw); } internal static IntPtr CreateCCWForObjectForABI(object obj, Guid iid) { - IntPtr ccw = ComWrappers.GetOrCreateComInterfaceForObject(obj, CreateComInterfaceFlags.TrackerSupport); - try - { - Marshal.ThrowExceptionForHR(Marshal.QueryInterface(ccw, ref iid, out var iidCcw)); - return iidCcw; - } - finally + IntPtr ccw = ComWrappers.GetOrCreateComInterfaceForObject(obj, CreateComInterfaceFlags.TrackerSupport); + + int hr = Marshal.QueryInterface(ccw, ref iid, out var iidCcw); + + Marshal.Release(ccw); + + if (hr < 0) { - MarshalInspectable.DisposeAbi(ccw); + [MethodImpl(MethodImplOptions.NoInlining)] + static void ThrowException(object obj, Guid iid, int hr) + { + // Special case 'E_NOINTERFACE' to provide a better exception message + if (hr == ExceptionHelpers.E_NOINTERFACE) + { + throw new InvalidCastException($"Failed to create a CCW for object of type '{obj.GetType()}' for interface with IID '{iid.ToString().ToUpperInvariant()}': the specified cast is not valid."); + } + + ExceptionHelpers.ThrowExceptionForHR(hr); + } + + ThrowException(obj, iid, hr); } + + return iidCcw; + } + + public static unsafe T FindObject(IntPtr ptr) + where T : class => ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)ptr); + + public static IUnknownVftbl IUnknownVftbl => DefaultComWrappers.IUnknownVftbl; + public static IntPtr IUnknownVftblPtr => DefaultComWrappers.IUnknownVftblPtr; + + public static IntPtr AllocateVtableMemory(Type vtableType, int size) => RuntimeHelpers.AllocateTypeAssociatedMemory(vtableType, size); + + /// + /// Initialize the global instance to use for WinRT. + /// + /// The wrappers instance to use, or the default if null. + /// + /// A custom ComWrappers instance can be supplied to enable programs to fast-track some type resolution + /// instead of using reflection when the full type closure is known. + /// + public static void InitializeComWrappers(ComWrappers wrappers = null) + { + ComWrappers = wrappers; } - - public static unsafe T FindObject(IntPtr ptr) - where T : class => ComInterfaceDispatch.GetInstance((ComInterfaceDispatch*)ptr); - - private static T FindDelegate(IntPtr thisPtr) - where T : class, System.Delegate => FindObject(thisPtr); - - public static IUnknownVftbl IUnknownVftbl => DefaultComWrappers.IUnknownVftbl; - public static IntPtr IUnknownVftblPtr => DefaultComWrappers.IUnknownVftblPtr; - - public static IntPtr AllocateVtableMemory(Type vtableType, int size) => RuntimeHelpers.AllocateTypeAssociatedMemory(vtableType, size); - - /// - /// Initialize the global instance to use for WinRT. - /// - /// The wrappers instance to use, or the default if null. - /// - /// A custom ComWrappers instance can be supplied to enable programs to fast-track some type resolution - /// instead of using reflection when the full type closure is known. - /// - public static void InitializeComWrappers(ComWrappers wrappers = null) - { - ComWrappers = wrappers; - } - - internal static Func GetTypedRcwFactory([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type implementationType) => TypedObjectFactoryCacheForType.GetOrAdd(implementationType, classType => CreateTypedRcwFactory(classType)); - - private static Func CreateFactoryForImplementationType(string runtimeClassName, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] Type implementationType) - { + + internal static Func GetTypedRcwFactory(Type implementationType) => TypedObjectFactoryCacheForType.GetOrAdd(implementationType, CreateTypedRcwFactory); + + public static bool RegisterTypedRcwFactory(Type implementationType, Func rcwFactory) => TypedObjectFactoryCacheForType.TryAdd(implementationType, rcwFactory); + + private static Func CreateFactoryForImplementationType(string runtimeClassName, Type implementationType) + { if (implementationType.IsGenericType) { + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"Cannot create an RCW factory for implementation type '{implementationType}'."); + } + +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 Type genericImplType = GetGenericImplType(implementationType); +#pragma warning restore IL3050 if (genericImplType != null) { var createRcw = genericImplType.GetMethod("CreateRcw", BindingFlags.Public | BindingFlags.Static, null, new[] { typeof(IInspectable) }, null); return (Func)createRcw.CreateDelegate(typeof(Func)); } - } - - if (implementationType.IsInterface) - { - return obj => obj; } - var constructor = implementationType.GetConstructor(BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.Instance, null, new[] { typeof(IObjectReference) }, null); - return (IInspectable obj) => constructor.Invoke(new[] { obj.ObjRef }); + if (implementationType.IsInterface) + { + return obj => obj; + } + + // We never look for attributes on base types, since each [WinRTImplementationTypeRcwFactory] type acts as + // a factory type specifically for the annotated implementation type, so it has to be on that derived type. + var attribute = implementationType.GetCustomAttribute(inherit: false); + + // For update projections, get the derived [WinRTImplementationTypeRcwFactory] + // attribute instance and use its overridden 'CreateInstance' method as factory. + if (attribute is not null) + { + return attribute.CreateInstance; + } + + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException( + $"Cannot create an RCW factory for implementation type '{implementationType}', because it doesn't have " + + "a [WinRTImplementationTypeRcwFactory] derived attribute on it. The fallback path for older projections " + + "is not trim-safe, and isn't supported in AOT environments. Make sure to reference updated projections."); + } + return CreateRcwFallback(implementationType); + + [UnconditionalSuppressMessage("Trimming", "IL2070", Justification = "This fallback path is not trim-safe by design (to avoid annotations).")] + static Func CreateRcwFallback(Type implementationType) + { + var constructor = implementationType.GetConstructor( + bindingAttr: BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.Instance, + binder: null, + types: new[] { typeof(IObjectReference) }, + modifiers: null); + + return (IInspectable obj) => constructor.Invoke(new[] { obj.ObjRef }); + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] static Type GetGenericImplType(Type implementationType) { @@ -263,6 +309,10 @@ static Type GetGenericImplType(Type implementationType) { genericImplType = typeof(IEnumerableImpl<>); } + else if (genericType == typeof(IEnumerator<>)) + { + genericImplType = typeof(IEnumeratorImpl<>); + } else { return null; @@ -270,96 +320,154 @@ static Type GetGenericImplType(Type implementationType) return genericImplType.MakeGenericType(implementationType.GetGenericArguments()); } - } + } + + private static readonly List> ComInterfaceEntriesLookup = new(); + + public static void RegisterTypeComInterfaceEntriesLookup(Func comInterfaceEntriesLookup) + { + ComInterfaceEntriesLookup.Add(comInterfaceEntriesLookup); + } + + internal static ComInterfaceEntry[] GetComInterfaceEntriesForTypeFromLookupTable(Type type) + { + // Using for loop to avoid exception from list changing when using for each. + // List is only added to and if any are added while looping, we can ignore those. + int count = ComInterfaceEntriesLookup.Count; + for (int i = 0; i < count; i++) + { + var comInterfaceEntries = ComInterfaceEntriesLookup[i](type); + if (comInterfaceEntries != null) + { + return comInterfaceEntries; + } + } + + return null; + } + + private static readonly List> RuntimeClassNameForNonWinRTTypeLookup = new(); + + public static void RegisterTypeRuntimeClassNameLookup(Func runtimeClassNameLookup) + { + RuntimeClassNameForNonWinRTTypeLookup.Add(runtimeClassNameLookup); + } + + internal static string GetRuntimeClassNameForNonWinRTTypeFromLookupTable(Type type) + { + // Using for loop to avoid exception from list changing when using for each. + // List is only added to and if any are added while looping, we can ignore those. + int count = RuntimeClassNameForNonWinRTTypeLookup.Count; + for (int i = 0; i < count; i++) + { + var runtimeClasName = RuntimeClassNameForNonWinRTTypeLookup[i](type); + if (!string.IsNullOrEmpty(runtimeClasName)) + { + return runtimeClasName; + } + } + + return null; + } + + private static readonly ConcurrentDictionary ComInterfaceEntriesForType = new(); + public static void RegisterComInterfaceEntries(Type implementationType, ComInterfaceEntry[] comInterfaceEntries) => ComInterfaceEntriesForType.TryAdd(implementationType, comInterfaceEntries); } #if EMBED internal #else public -#endif - class ComWrappersHelper +#endif + class ComWrappersHelper { public unsafe static void Init( bool isAggregation, object thisInstance, IntPtr newInstance, IntPtr inner, - out IObjectReference objRef) + out IObjectReference objRef) { - objRef = ComWrappersSupport.GetObjectReferenceForInterface(isAggregation ? inner : newInstance); - + // To keep pre-existing behavior when we don't know the IID, passing it as IUnknown + // as we would have done before. + Init(isAggregation, thisInstance, newInstance, inner, IID.IID_IUnknown, out objRef); + } + + public unsafe static void Init( + bool isAggregation, + object thisInstance, + IntPtr newInstance, + IntPtr inner, + Guid iidForNewInstance, + out IObjectReference objRef) + { + objRef = ComWrappersSupport.GetObjectReferenceForInterface( + isAggregation ? inner : newInstance, + isAggregation ? IID.IID_IInspectable : iidForNewInstance, + requireQI: false); + IntPtr referenceTracker; - { - // Determine if the instance supports IReferenceTracker (e.g. WinUI). - // Acquiring this interface is useful for: - // 1) Providing an indication of what value to pass during RCW creation. - // 2) Informing the Reference Tracker runtime during non-aggregation - // scenarios about new references. - // - // If aggregation, query the inner since that will have the implementation - // otherwise the new instance will be used. Since the inner was composed - // it should answer immediately without going through the outer. Either way - // the reference count will go to the new instance. - Guid iid = IReferenceTrackerVftbl.IID; - int hr = Marshal.QueryInterface(objRef.ThisPtr, ref iid, out referenceTracker); - if (hr != 0) - { - referenceTracker = default; - } - } - - { - // Determine flags needed for native object wrapper (i.e. RCW) creation. - var createObjectFlags = CreateObjectFlags.None; + { + // Determine if the instance supports IReferenceTracker (e.g. WinUI). + // Acquiring this interface is useful for: + // 1) Providing an indication of what value to pass during RCW creation. + // 2) Informing the Reference Tracker runtime during non-aggregation + // scenarios about new references. + // + // If aggregation, query the inner since that will have the implementation + // otherwise the new instance will be used. Since the inner was composed + // it should answer immediately without going through the outer. Either way + // the reference count will go to the new instance. + int hr = Marshal.QueryInterface(objRef.ThisPtr, ref Unsafe.AsRef(in IID.IID_IReferenceTracker), out referenceTracker); + if (hr != 0) + { + referenceTracker = default; + } + } + + { + // Determine flags needed for native object wrapper (i.e. RCW) creation. + var createObjectFlags = CreateObjectFlags.None; IntPtr instanceToWrap = newInstance; // The instance supports IReferenceTracker. - if (referenceTracker != default(IntPtr)) + if (referenceTracker != default) { createObjectFlags |= CreateObjectFlags.TrackerObject; } - // Update flags if the native instance is being used in an aggregation scenario. - if (isAggregation) - { - // Indicate the scenario is aggregation - createObjectFlags |= (CreateObjectFlags)4; - - // The instance supports IReferenceTracker. - if (referenceTracker != default(IntPtr)) - { - // IReferenceTracker is not needed in aggregation scenarios. - // It is not needed because all QueryInterface() calls on an - // object are followed by an immediately release of the returned - // pointer - see below for details. - Marshal.Release(referenceTracker); - - // .NET 5 limitation - // - // For aggregated scenarios involving IReferenceTracker - // the API handles object cleanup. In .NET 5 the API - // didn't expose an option to handle this so we pass the inner - // in order to handle its lifetime. - // - // The API doesn't handle inner lifetime in any other scenario - // in the .NET 5 timeframe. - instanceToWrap = inner; - } - } - // Create a native object wrapper (i.e. RCW). // // Note this function will call QueryInterface() on the supplied instance, // therefore it is important that the enclosing CCW forwards to its inner // if aggregation is involved. This is typically accomplished through an // implementation of ICustomQueryInterface. - ComWrappersSupport.RegisterObjectForInterface(thisInstance, instanceToWrap, createObjectFlags); - } - + if (isAggregation) + { + // Indicate the scenario is aggregation + createObjectFlags |= CreateObjectFlags.Aggregation; + + // The instance supports IReferenceTracker. + if (referenceTracker != default) + { + // IReferenceTracker is not needed in aggregation scenarios. + // It is not needed because all QueryInterface() calls on an + // object are followed by an immediately release of the returned + // pointer - see below for details. + Marshal.Release(referenceTracker); + } + + ComWrappersSupport.RegisterObjectForInterface(thisInstance, instanceToWrap, inner, createObjectFlags); + } + else + { + ComWrappersSupport.RegisterObjectForInterface(thisInstance, instanceToWrap, createObjectFlags); + } + } + // The following sets up the object reference to correctly handle AddRefs and releases - // based on the scenario. - if (isAggregation) + // based on the scenario. + if (isAggregation) { // Aggregation scenarios should avoid calling AddRef() on the // newInstance value. This is due to the semantics of COM Aggregation @@ -375,25 +483,25 @@ public unsafe static void Init( // IsAggregated and PreventReleaseOnDispose properties on IObjectReference. objRef.IsAggregated = true; // In WinUI scenario don't release inner - objRef.PreventReleaseOnDispose = referenceTracker != default(IntPtr); - } - else - { - if (referenceTracker != default(IntPtr)) - { - // WinUI scenario - // This instance should be used to tell the - // Reference Tracker runtime whenever an AddRef()/Release() - // is performed on newInstance. + objRef.PreventReleaseOnDispose = referenceTracker != default; + } + else + { + if (referenceTracker != default) + { + // WinUI scenario + // This instance should be used to tell the + // Reference Tracker runtime whenever an AddRef()/Release() + // is performed on newInstance. objRef.ReferenceTrackerPtr = referenceTracker; // This instance is already AddRefFromTrackerSource by the CLR, // so it would also ReleaseFromTrackerSource on destruction. objRef.PreventReleaseFromTrackerSourceOnDispose = true; - Marshal.Release(referenceTracker); - } - + Marshal.Release(referenceTracker); + } + Marshal.Release(newInstance); } } @@ -402,9 +510,8 @@ public unsafe static void Init(IObjectReference objRef, bool addRefFromTrackerSo { if (objRef.ReferenceTrackerPtr == IntPtr.Zero) { - Guid iid = IReferenceTrackerVftbl.IID; - int hr = Marshal.QueryInterface(objRef.ThisPtr, ref iid, out var referenceTracker); - if (hr == 0) + int hr = Marshal.QueryInterface(objRef.ThisPtr, ref Unsafe.AsRef(in IID.IID_IReferenceTracker), out var referenceTracker); + if (hr == 0) { // WinUI scenario // This instance should be used to tell the @@ -421,46 +528,40 @@ public unsafe static void Init(IObjectReference objRef, bool addRefFromTrackerSo objRef.PreventReleaseFromTrackerSourceOnDispose = true; } - Marshal.Release(referenceTracker); + Marshal.Release(referenceTracker); } } - } - } - -#if EMBED - internal -#else - public -#endif - class DefaultComWrappers : ComWrappers - { - private static readonly ConditionalWeakTable TypeVtableEntryTable = new ConditionalWeakTable(); - public static unsafe IUnknownVftbl IUnknownVftbl => Unsafe.AsRef(IUnknownVftblPtr.ToPointer()); - - internal static IntPtr IUnknownVftblPtr { get; } - - static unsafe DefaultComWrappers() - { - GetIUnknownImpl(out var qi, out var addRef, out var release); - - IUnknownVftblPtr = RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(IUnknownVftbl), sizeof(IUnknownVftbl)); - (*(IUnknownVftbl*)IUnknownVftblPtr) = new IUnknownVftbl - { - QueryInterface = (delegate* unmanaged[Stdcall])qi, - AddRef = (delegate* unmanaged[Stdcall])addRef, - Release = (delegate* unmanaged[Stdcall])release, - }; - } - - protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) + } + } + +#if EMBED + internal +#else + public +#endif + class DefaultComWrappers : ComWrappers + { + private static readonly ConditionalWeakTable TypeVtableEntryTable = new(); + public static unsafe IUnknownVftbl IUnknownVftbl => Unsafe.AsRef(IUnknownVftblPtr.ToPointer()); + + internal static IntPtr IUnknownVftblPtr { get; } + + static unsafe DefaultComWrappers() { - var vtableEntries = TypeVtableEntryTable.GetValue(obj.GetType(), ( -#if NET6_0_OR_GREATER - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#elif NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] -#endif - type) => + GetIUnknownImpl(out var qi, out var addRef, out var release); + + IUnknownVftblPtr = RuntimeHelpers.AllocateTypeAssociatedMemory(typeof(IUnknownVftbl), sizeof(IUnknownVftbl)); + (*(IUnknownVftbl*)IUnknownVftblPtr) = new IUnknownVftbl + { + QueryInterface = (delegate* unmanaged[Stdcall])qi, + AddRef = (delegate* unmanaged[Stdcall])addRef, + Release = (delegate* unmanaged[Stdcall])release, + }; + } + + protected override unsafe ComInterfaceEntry* ComputeVtables(object obj, CreateComInterfaceFlags flags, out int count) + { + var vtableEntries = TypeVtableEntryTable.GetValue(obj.GetType(), static (type) => { if (IsRuntimeImplementedRCW(type)) { @@ -471,53 +572,60 @@ static unsafe DefaultComWrappers() return new VtableEntries(ComWrappersSupport.GetInterfaceTableEntries(type), type); }); - count = vtableEntries.Count; + count = vtableEntries.Count; if (count != 0 && !flags.HasFlag(CreateComInterfaceFlags.CallerDefinedIUnknown)) { // The vtable list unconditionally has the last entry as IUnknown, but it should // only be included if the flag is set. We achieve that by excluding the last entry // from the count if the flag isn't set. count -= 1; - } - - return vtableEntries.Data; - } - - private static unsafe bool IsRuntimeImplementedRCW(Type objType) - { - bool isRcw = objType.IsCOMObject; - if (objType.IsGenericType) - { - foreach (var arg in objType.GetGenericArguments()) - { - if (arg.IsCOMObject) - { - isRcw = true; - break; - } - } - } - return isRcw; + } + + return vtableEntries.Data; + } + + private static unsafe bool IsRuntimeImplementedRCW(Type objType) + { + // Built-in COM interop isn't supported in AOT environments, + // so this method can only ever return false. Just inline it. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return false; + } + + bool isRcw = objType.IsCOMObject; + if (objType.IsGenericType) + { + foreach (var arg in objType.GetGenericArguments()) + { + if (arg.IsCOMObject) + { + isRcw = true; + break; + } + } + } + return isRcw; } private static object CreateObject(IntPtr externalComObject) { - Guid inspectableIID = InterfaceIIDs.IInspectable_IID; - Guid weakReferenceIID = ABI.WinRT.Interop.IWeakReference.IID; + Guid inspectableIID = IID.IID_IInspectable; + Guid weakReferenceIID = IID.IID_IWeakReference; IntPtr ptr = IntPtr.Zero; try { if (ComWrappersSupport.CreateRCWType != null && ComWrappersSupport.CreateRCWType.IsDelegate()) { - return ComWrappersSupport.CreateDelegateFactory(ComWrappersSupport.CreateRCWType)(externalComObject); + return ComWrappersSupport.GetOrCreateDelegateFactory(ComWrappersSupport.CreateRCWType)(externalComObject); } else if (Marshal.QueryInterface(externalComObject, ref inspectableIID, out ptr) == 0) { - var inspectableObjRef = ComWrappersSupport.GetObjectReferenceForInterface(ptr); + var inspectableObjRef = ComWrappersSupport.GetObjectReferenceForInterface(ptr, IID.IID_IInspectable, false); ComWrappersHelper.Init(inspectableObjRef); - IInspectable inspectable = new IInspectable(inspectableObjRef); + IInspectable inspectable = new(inspectableObjRef); if (ComWrappersSupport.CreateRCWType != null && ComWrappersSupport.CreateRCWType.IsSealed) @@ -539,31 +647,28 @@ private static object CreateObject(IntPtr externalComObject) { // IWeakReference is IUnknown-based, so implementations of it may not (and likely won't) implement // IInspectable. As a result, we need to check for them explicitly. - var iunknownObjRef = ComWrappersSupport.GetObjectReferenceForInterface(ptr); + var iunknownObjRef = ComWrappersSupport.GetObjectReferenceForInterface(ptr, weakReferenceIID, false); ComWrappersHelper.Init(iunknownObjRef); return new SingleInterfaceOptimizedObject(typeof(IWeakReference), iunknownObjRef, false); - } + } else { // If the external COM object isn't IInspectable or IWeakReference, we can't handle it. // If we're registered globally, we want to let the runtime fall back for IUnknown and IDispatch support. // Return null so the runtime can fall back gracefully in IUnknown and IDispatch scenarios. return null; - } - } - finally - { - if (ptr != IntPtr.Zero) - { - Marshal.Release(ptr); } - } - } - - protected override object CreateObject(IntPtr externalComObject, CreateObjectFlags flags) - { - var obj = CreateObject(externalComObject); + } + finally + { + MarshalExtensions.ReleaseIfNotNull(ptr); + } + } + + protected override object CreateObject(IntPtr externalComObject, CreateObjectFlags flags) + { + var obj = CreateObject(externalComObject); if (obj is IWinRTObject winrtObj && winrtObj.HasUnwrappableNativeObject && winrtObj.NativeObject != null) { // Handle the scenario where the CLR has already done an AddRefFromTrackerSource on the instance @@ -571,46 +676,41 @@ protected override object CreateObject(IntPtr externalComObject, CreateObjectFla // on destruction as the CLR would do it. winrtObj.NativeObject.ReleaseFromTrackerSource(); winrtObj.NativeObject.PreventReleaseFromTrackerSourceOnDispose = true; - } - - return obj; - } - - protected override void ReleaseObjects(IEnumerable objects) - { - foreach (var obj in objects) - { - if (ComWrappersSupport.TryUnwrapObject(obj, out var objRef)) - { - objRef.Dispose(); - } - else - { - throw new InvalidOperationException("Cannot release objects that are not runtime wrappers of native WinRT objects."); - } - } - } - - unsafe sealed class VtableEntries - { - public ComInterfaceEntry* Data { get; } - public int Count { get; } - + } + + return obj; + } + + protected override void ReleaseObjects(IEnumerable objects) + { + foreach (var obj in objects) + { + if (ComWrappersSupport.TryUnwrapObject(obj, out var objRef)) + { + objRef.Dispose(); + } + } + } + + unsafe sealed class VtableEntries + { + public ComInterfaceEntry* Data { get; } + public int Count { get; } + public VtableEntries() { Data = null; Count = 0; - } - - public VtableEntries(List entries, Type type) + } + + public VtableEntries(List entries, Type type) { Data = (ComInterfaceEntry*)RuntimeHelpers.AllocateTypeAssociatedMemory(type, sizeof(ComInterfaceEntry) * entries.Count); - for (int i = 0; i < entries.Count; i++) - { - Data[i] = entries[i]; - } - Count = entries.Count; - } - } - } -} + + CollectionsMarshal.AsSpan(entries).CopyTo(new Span(Data, entries.Count)); + + Count = entries.Count; + } + } + } +} diff --git a/src/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs b/src/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs index 6377a0e0c..f67ab9afd 100644 --- a/src/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs +++ b/src/WinRT.Runtime/ComWrappersSupport.netstandard2.0.cs @@ -23,18 +23,25 @@ static partial class ComWrappersSupport private static ConditionalWeakTable ComWrapperCache = new ConditionalWeakTable(); private static ConcurrentDictionary> RuntimeWrapperCache = new ConcurrentDictionary>(); - private readonly static ConcurrentDictionary> TypeObjectRefFuncCache = new ConcurrentDictionary>(); + private static readonly ConcurrentDictionary> TypeObjectRefFuncCache = new ConcurrentDictionary>(); - internal static InspectableInfo GetInspectableInfo(IntPtr pThis) => UnmanagedObject.FindObject(pThis).InspectableInfo; + internal static InspectableInfo GetInspectableInfo(IntPtr pThis) => UnmanagedObject.FindObject(pThis).InspectableInfo; + + public static T CreateRcwForComObject(IntPtr ptr) + { + return CreateRcwForComObject(ptr, true); + } + + internal static Func GetTypedRcwFactory(Type implementationType) => TypedObjectFactoryCacheForType.GetOrAdd(implementationType, CreateTypedRcwFactory); - public static T CreateRcwForComObject(IntPtr ptr) + private static T CreateRcwForComObject(IntPtr ptr, bool tryUseCache) { if (ptr == IntPtr.Zero) { return default; } - IObjectReference identity = GetObjectReferenceForInterface(ptr).As(); + IObjectReference identity = GetObjectReferenceForInterface(ptr, IID.IID_IUnknown); object keepAliveSentinel = null; @@ -43,13 +50,21 @@ public static T CreateRcwForComObject(IntPtr ptr) object runtimeWrapper = null; if (typeof(T).IsDelegate()) { - runtimeWrapper = CreateDelegateFactory(typeof(T))(ptr); + runtimeWrapper = GetOrCreateDelegateFactory(typeof(T))(ptr); } else if (identity.TryAs(out var inspectableRef) == 0) { - var inspectable = new IInspectable(identity); - Type runtimeClassType = GetRuntimeClassForTypeCreation(inspectable, typeof(T)); - runtimeWrapper = runtimeClassType == null ? inspectable : TypedObjectFactoryCacheForType.GetOrAdd(runtimeClassType, classType => CreateTypedRcwFactory(classType))(inspectable); + var inspectable = new IInspectable(identity); + + if (typeof(T).IsSealed) + { + runtimeWrapper = GetTypedRcwFactory(typeof(T))(inspectable); + } + else + { + Type runtimeClassType = GetRuntimeClassForTypeCreation(inspectable, typeof(T)); + runtimeWrapper = runtimeClassType == null ? inspectable : TypedObjectFactoryCacheForType.GetOrAdd(runtimeClassType, CreateTypedRcwFactory)(inspectable); + } } else if (identity.TryAs(out var weakRef) == 0) { @@ -62,17 +77,25 @@ public static T CreateRcwForComObject(IntPtr ptr) return runtimeWrapperReference; }; - RuntimeWrapperCache.AddOrUpdate( - identity.ThisPtr, - rcwFactory, - (ptr, oldValue) => - { - if (!oldValue.TryGetTarget(out keepAliveSentinel)) - { - return rcwFactory(ptr); - } - return oldValue; - }).TryGetTarget(out object rcw); + object rcw; + if (tryUseCache) + { + RuntimeWrapperCache.AddOrUpdate( + identity.ThisPtr, + rcwFactory, + (ptr, oldValue) => + { + if (!oldValue.TryGetTarget(out keepAliveSentinel)) + { + return rcwFactory(ptr); + } + return oldValue; + }).TryGetTarget(out rcw); + } + else + { + rcwFactory(ptr).TryGetTarget(out rcw); + } GC.KeepAlive(keepAliveSentinel); @@ -86,7 +109,9 @@ public static T CreateRcwForComObject(IntPtr ptr) return rcw switch { ABI.System.Nullable nt => (T)nt.Value, - _ => (T)rcw + T castRcw => castRcw, + _ when tryUseCache => CreateRcwForComObject(ptr, false), + _ => throw new ArgumentException(string.Format("Unable to create a wrapper object. The WinRT object {0} has type {1} which cannot be assigned to type {2}", ptr, rcw.GetType(), typeof(T))) }; } @@ -107,7 +132,7 @@ public static bool TryUnwrapObject(object o, out IObjectReference objRef) return TryUnwrapObject(del.Target, out objRef); } - var objRefFunc = TypeObjectRefFuncCache.GetOrAdd(o.GetType(), (type) => + var objRefFunc = TypeObjectRefFuncCache.GetOrAdd(o.GetType(), static (type) => { ObjectReferenceWrapperAttribute objRefWrapper = type.GetCustomAttribute(); if (objRefWrapper is object) @@ -336,7 +361,7 @@ public ComCallableWrapper(object obj) InitializeManagedQITable(interfaceTableEntries); - IdentityPtr = _managedQITable[IUnknownVftbl.IID]; + IdentityPtr = _managedQITable[IID.IID_IUnknown]; } ~ComCallableWrapper() diff --git a/src/WinRT.Runtime/Configuration/FeatureSwitches.cs b/src/WinRT.Runtime/Configuration/FeatureSwitches.cs new file mode 100644 index 000000000..6968c19eb --- /dev/null +++ b/src/WinRT.Runtime/Configuration/FeatureSwitches.cs @@ -0,0 +1,233 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.CompilerServices; + +namespace WinRT +{ + /// + /// A container for all shared configuration switches for CsWinRT. + /// + /// + /// + /// This type uses a very specific setup for configuration switches to ensure ILLink can work the best. + /// This mirrors the architecture of feature switches in the runtime as well, and it's needed so that + /// no static constructor is generated for the type. + /// + /// + /// For more info, see . + /// + /// + internal static class FeatureSwitches + { + /// + /// The configuration property name for . + /// + private const string EnableDynamicObjectsSupportPropertyName = "CSWINRT_ENABLE_DYNAMIC_OBJECTS_SUPPORT"; + + /// + /// The configuration property name for . + /// + private const string UseExceptionResourceKeysPropertyName = "CSWINRT_USE_EXCEPTION_RESOURCE_KEYS"; + + /// + /// The configuration property name for . + /// + private const string EnableDefaultCustomTypeMappingsPropertyName = "CSWINRT_ENABLE_DEFAULT_CUSTOM_TYPE_MAPPINGS"; + + /// + /// The configuration property name for . + /// + private const string EnableICustomPropertyProviderSupportPropertyName = "CSWINRT_ENABLE_ICUSTOMPROPERTYPROVIDER_SUPPORT"; + + /// + /// The configuration property name for . + /// + private const string EnableIReferenceSupportPropertyName = "CSWINRT_ENABLE_IREFERENCE_SUPPORT"; + + /// + /// The configuration property name for . + /// + private const string EnableIDynamicInterfaceCastableSupportPropertyName = "CSWINRT_ENABLE_IDYNAMICINTERFACECASTABLE"; + + /// + /// The configuration property name for . + /// + private const string EnableManifestFreeActivationPropertyName = "CSWINRT_ENABLE_MANIFEST_FREE_ACTIVATION"; + + /// + /// The configuration property name for . + /// + private const string ManifestFreeActivationReportOriginalExceptionPropertyName = "CSWINRT_MANIFEST_FREE_ACTIVATION_REPORT_ORIGINAL_EXCEPTION"; + + /// + /// The configuration property name for . + /// + private const string UseWindowsUIXamlProjectionsPropertyName = "CSWINRT_USE_WINDOWS_UI_XAML_PROJECTIONS"; + + /// + /// The backing field for . + /// + private static int _enableDynamicObjectsSupport; + + /// + /// The backing field for . + /// + private static int _useExceptionResourceKeys; + + /// + /// The backing field for . + /// + private static int _enableDefaultCustomTypeMappings; + + /// + /// The backing field for . + /// + private static int _enableICustomPropertyProviderSupport; + + /// + /// The backing field for . + /// + private static int _enableIReferenceSupport; + + /// + /// The backing field for . + /// + private static int _enableIDynamicInterfaceCastableSupport; + + /// + /// The backing field for . + /// + private static int _enableManifestFreeActivation; + + /// + /// The backing field for . + /// + private static int _manifestFreeActivationReportOriginalException; + + /// + /// The backing field for . + /// + private static int _useWindowsUIXamlProjections; + + /// + /// Gets a value indicating whether or not projections support for dynamic objects is enabled (defaults to ). + /// + public static bool EnableDynamicObjectsSupport + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetConfigurationValue(EnableDynamicObjectsSupportPropertyName, ref _enableDynamicObjectsSupport, true); + } + + /// + /// Gets a value indicating whether or not exceptions should use resource keys rather than localized messages (defaults to ). + /// + public static bool UseExceptionResourceKeys + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetConfigurationValue(UseExceptionResourceKeysPropertyName, ref _useExceptionResourceKeys, false); + } + + /// + /// Gets a value indicating whether or not should initialize all default type mappings automatically (defaults to ). + /// + public static bool EnableDefaultCustomTypeMappings + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetConfigurationValue(EnableDefaultCustomTypeMappingsPropertyName, ref _enableDefaultCustomTypeMappings, true); + } + + /// + /// Gets a value indicating whether or not should be enabled (defaults to ). + /// + public static bool EnableICustomPropertyProviderSupport + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetConfigurationValue(EnableICustomPropertyProviderSupportPropertyName, ref _enableICustomPropertyProviderSupport, true); + } + + /// + /// Gets a value indicating whether or not IReference<T>, IReferenceArray<T> and IPropertyValue CCW implementations should be supported (defaults to ). + /// + public static bool EnableIReferenceSupport + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetConfigurationValue(EnableIReferenceSupportPropertyName, ref _enableIReferenceSupport, true); + } + + /// + /// Gets a value indicating whether or not should be supported by RCW types (defaults to ). + /// + public static bool EnableIDynamicInterfaceCastableSupport + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetConfigurationValue(EnableIDynamicInterfaceCastableSupportPropertyName, ref _enableIDynamicInterfaceCastableSupport, true); + } + + /// + /// Gets a value indicating whether or not manifest free WinRT activation is supported (defaults to ). + /// + public static bool EnableManifestFreeActivation + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetConfigurationValue(EnableManifestFreeActivationPropertyName, ref _enableManifestFreeActivation, true); + } + + /// + /// Gets a value indicating whether or not the original exception should be thrown if activation fails when is disabled (defaults to ). + /// + public static bool ManifestFreeActivationReportOriginalException + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetConfigurationValue(ManifestFreeActivationReportOriginalExceptionPropertyName, ref _manifestFreeActivationReportOriginalException, false); + } + + /// + /// Gets a value indicating whether to project .NET types to their Windows.UI.Xaml equivalents instead of their Microsoft.UI.Xaml equivalents. + /// + public static bool UseWindowsUIXamlProjections + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetConfigurationValue(UseWindowsUIXamlProjectionsPropertyName, ref _useWindowsUIXamlProjections, false); + } + + /// + /// Gets a configuration value for a specified property. + /// + /// The property name to retrieve the value for. + /// The cached result for the target configuration value. + /// The value of the specified configuration setting. + private static bool GetConfigurationValue(string propertyName, ref int cachedResult, bool defaultValue) + { + // The cached switch value has 3 states: + // 0: unknown. + // 1: true + // -1: false + // + // This method doesn't need to worry about concurrent accesses to the cached result, + // as even if the configuration value is retrieved twice, that'll always be the same. + if (cachedResult < 0) + { + return false; + } + + if (cachedResult > 0) + { + return true; + } + + // Get the configuration switch value, or its default. + // All feature switches have a default set in the .targets file. + if (!AppContext.TryGetSwitch(propertyName, out bool isEnabled)) + { + isEnabled = defaultValue; + } + + // Update the cached result + cachedResult = isEnabled ? 1 : -1; + + return isEnabled; + } + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime/Configuration/ILLink.Substitutions.xml b/src/WinRT.Runtime/Configuration/ILLink.Substitutions.xml new file mode 100644 index 000000000..b9b55cff1 --- /dev/null +++ b/src/WinRT.Runtime/Configuration/ILLink.Substitutions.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/WinRT.Runtime/Context.cs b/src/WinRT.Runtime/Context.cs index 1b5dc5ffb..5ff479bc1 100644 --- a/src/WinRT.Runtime/Context.cs +++ b/src/WinRT.Runtime/Context.cs @@ -3,47 +3,58 @@ using System; using System.Runtime.InteropServices; +using ABI.WinRT.Interop; using WinRT.Interop; namespace WinRT { - static class Context + internal static partial class Context { - [DllImport("api-ms-win-core-com-l1-1-0.dll")] - private static extern unsafe int CoGetContextToken(IntPtr* contextToken); - - [DllImport("api-ms-win-core-com-l1-1-0.dll")] - private static extern int CoGetObjectContext(ref Guid riid, out IntPtr ppv); - - private static readonly Guid IID_ICallbackWithNoReentrancyToApplicationSTA = new(0x0A299774, 0x3E4E, 0xFC42, 0x1D, 0x9D, 0x72, 0xCE, 0xE1, 0x05, 0xCA, 0x57); - - public static IntPtr GetContextCallback() - { - Guid riid = ABI.WinRT.Interop.IContextCallback.IID; - Marshal.ThrowExceptionForHR(CoGetObjectContext(ref riid, out IntPtr contextCallbackPtr)); - return contextCallbackPtr; - } - public unsafe static IntPtr GetContextToken() { IntPtr contextToken; - Marshal.ThrowExceptionForHR(CoGetContextToken(&contextToken)); + Marshal.ThrowExceptionForHR(Platform.CoGetContextToken(&contextToken)); return contextToken; } + public static unsafe IntPtr GetContextCallback() + { + Guid iid = IID.IID_IContextCallback; + IntPtr contextCallbackPtr; + Marshal.ThrowExceptionForHR(Platform.CoGetObjectContext(&iid, &contextCallbackPtr)); + return contextCallbackPtr; + } + // Calls the given callback in the right context. // On any exception, calls onFail callback if any set. // If not set, exception is handled due to today we don't // have any scenario to propagate it from here. - public unsafe static void CallInContext(IntPtr contextCallbackPtr, IntPtr contextToken, Action callback, Action onFailCallback) + // + // On modern .NET, we can use function pointers to avoid the + // small binary size increase from all generated fields and + // logic to cache delegates, since we don't need any of that. + public unsafe static void CallInContext( + IntPtr contextCallbackPtr, + IntPtr contextToken, +#if NET && CsWinRT_LANG_11_FEATURES + delegate* callback, + delegate* onFailCallback, +#else + Action callback, + Action onFailCallback, +#endif + object state) { // Check if we are already on the same context, if so we do not need to switch. if(contextCallbackPtr == IntPtr.Zero || GetContextToken() == contextToken) { - callback(); + callback(state); return; } +#if NET && CsWinRT_LANG_11_FEATURES + IContextCallbackVftbl.ContextCallback(contextCallbackPtr, callback, onFailCallback, state); +#else ComCallData data = default; var contextCallback = new ABI.WinRT.Interop.IContextCallback(ObjectReference.FromAbi(contextCallbackPtr)); @@ -51,14 +62,15 @@ public unsafe static void CallInContext(IntPtr contextCallbackPtr, IntPtr contex { contextCallback.ContextCallback(_ => { - callback(); + callback(state); return 0; - }, &data, IID_ICallbackWithNoReentrancyToApplicationSTA, 5); + }, &data, IID.IID_ICallbackWithNoReentrancyToApplicationSTA, 5); } - catch(Exception) + catch (Exception) { - onFailCallback?.Invoke(); + onFailCallback?.Invoke(state); } +#endif } public static void DisposeContextCallback(IntPtr contextCallbackPtr) diff --git a/src/WinRT.Runtime/DelegateExtensions.cs b/src/WinRT.Runtime/DelegateExtensions.cs new file mode 100644 index 000000000..099bea2a6 --- /dev/null +++ b/src/WinRT.Runtime/DelegateExtensions.cs @@ -0,0 +1,110 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.InteropServices; + +namespace WinRT +{ + internal static class DelegateExtensions + { + public static void DynamicInvokeAbi(this Delegate del, object[] invoke_params) + { + Marshal.ThrowExceptionForHR((int)del.DynamicInvoke(invoke_params)); + } + + // These methods below can be used to efficiently change the signature of arbitrary delegate types + // to one that has just 'object' as any of the type arguments, without the need to generate a new + // closure and display class. The C# compiler will lower the method group expression over one of + // the extension methods below into a direct constructor call of the new delegate types, passing + // a function pointer for the target delegate, along with any target, if present. This is more + // compact in binary size (and better in perf) than eg. 'return (object arg) => function(arg)'; + // + // We have both some general purpose generic versions, as well two specialized variants to handle + // the ObjectReferenceValue marshalling with arbitrary inputs, when marshalling reference types. + // Once again, this just allows us to attach additional logic without needing a display class. + + public static Func WithMarshaler2Support(this Func function) + { + return function.InvokeWithMarshaler2Support; + } + + public static Action WithMarshaler2Support(this Action action) + { + return action.InvokeWithMarshaler2Support; + } + + public static Action WithTypedT1(this Action action) + { + return action.InvokeWithTypedT1; + } + + public static Func WithObjectT(this Func function) + { + return function.InvokeWithObjectT; + } + + public static Func WithObjectTResult(this Func function) + { + return function.InvokeWithObjectTResult; + } + + public static Action WithObjectParams(this Action action) + { + return action.InvokeWithObjectParams; + } + + private static object InvokeWithMarshaler2Support(this Func func, object arg) + { + if (arg is ObjectReferenceValue objectReferenceValue) + { + return objectReferenceValue.GetAbi(); + } + + return func.Invoke((IObjectReference)arg); + } + + private static void InvokeWithMarshaler2Support(this Action action, object arg) + { + if (arg is ObjectReferenceValue objectReferenceValue) + { + objectReferenceValue.Dispose(); + } + else + { + action((IObjectReference)arg); + } + } + + private static object InvokeWithObjectTResult(this Func func, T arg) + { + return func.Invoke(arg); + } + + private static TResult InvokeWithObjectT(this Func func, object arg) + { + return func.Invoke((T)arg); + } + + private static void InvokeWithObjectParams(this Action func, object arg) + { + func.Invoke((T)arg); + } + + private static void InvokeWithTypedT1(this Action action, T arg1, IntPtr arg2) + { + action.Invoke(arg1, arg2); + } + + // This extension method allows us to create a new delegate directly pointing to this method, which + // also adds the resulting object to the conditional weak table before returning it to callers. + public static object InvokeWithBoxedValueReferenceCacheInsertion(this Func factory, IInspectable inspectable) + { + object resultingObject = factory(inspectable); + + ComWrappersSupport.BoxedValueReferenceCache.Add(resultingObject, inspectable); + + return resultingObject; + } + } +} diff --git a/src/WinRT.Runtime/Directory.Build.props b/src/WinRT.Runtime/Directory.Build.props index 26add7b49..4c38e4eca 100644 --- a/src/WinRT.Runtime/Directory.Build.props +++ b/src/WinRT.Runtime/Directory.Build.props @@ -3,4 +3,11 @@ + + true + + + True + + diff --git a/src/WinRT.Runtime/EventRegistrationToken.cs b/src/WinRT.Runtime/EventRegistrationToken.cs index 23f495e47..2ba75e305 100644 --- a/src/WinRT.Runtime/EventRegistrationToken.cs +++ b/src/WinRT.Runtime/EventRegistrationToken.cs @@ -4,11 +4,16 @@ using System; namespace WinRT -{ +{ + [global::WinRT.WindowsRuntimeType("Windows.Foundation.FoundationContract")] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.WinRT.EventRegistrationToken))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif #if EMBED internal #else - public + public #endif struct EventRegistrationToken : IEquatable { diff --git a/src/WinRT.Runtime/EventRegistrationTokenTable{T}.cs b/src/WinRT.Runtime/EventRegistrationTokenTable{T}.cs new file mode 100644 index 000000000..4cd3ab6f1 --- /dev/null +++ b/src/WinRT.Runtime/EventRegistrationTokenTable{T}.cs @@ -0,0 +1,201 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; + +#nullable enable + +namespace WinRT +{ + /// + /// An event registration token table stores mappings from delegates to event tokens, in order to support + /// sourcing WinRT style events from managed code. This only supports events for CCW objects. + /// + /// The event handler type to use in the table. +#if EMBED + internal +#else + public +#endif + sealed class EventRegistrationTokenTable + where T : Delegate + { + /// + /// The hashcode of the delegate type, being set in the upper 32 bits of the registration tokens. + /// + private static readonly int TypeOfTHashCode = GetTypeOfTHashCode(); + + private static int GetTypeOfTHashCode() + { + int hashCode = typeof(T).GetHashCode(); + + // There is a minimal but non-zero chance that the hashcode of the T type argument will be 0. + // If that is the case, it means that it is possible for an event registration token to just + // be 0, which will happen when the low 32 bits also wrap around and go through 0. Such a + // registration token is not valid as per the WinRT spec, see: + // https://learn.microsoft.com/uwp/api/windows.foundation.eventregistrationtoken.value. + // To work around this, we just check for this edge case and return a magic constant instead. + if (hashCode == 0) + { + return 0x5FC74196; + } + + return hashCode; + } + + // Note this dictionary is also used as the synchronization object for this table + private readonly Dictionary m_tokens = new(); + + // The current counter used for the low 32 bits of the registration tokens. + // We explicit use [int.MinValue, int.MaxValue] as the range, as this value + // is expected to eventually wrap around, and we don't want to lose the + // additional possible range of negative values (there's no reason for that). + private int m_low32Bits = +#if NET8_0_OR_GREATER + Random.Shared.Next(int.MinValue, int.MaxValue); +#else + new Random().Next(int.MinValue, int.MaxValue); +#endif + /// + /// Adds an event handler to the table and retrieves the value for it. + /// + /// The handler to add to the table. + /// The value for the new handler. + /// + /// Handler can be registered multiple times, and they will use a different token each time. + /// If the input handler is , the resulting token will be 0. + /// + public EventRegistrationToken AddEventHandler(T? handler) + { + // Windows Runtime allows null handlers. Assign those the default token (token value 0) for simplicity + if (handler is null) + { + return default; + } + + lock (m_tokens) + { + // Get a registration token, making sure that we haven't already used the value. This should be quite + // rare, but in the case it does happen, just keep trying until we find one that's unused. Note that + // this mutable part of the token is just 32 bit wide (the lower 32 bits). The upper 32 bits are fixed. + // + // Note that: + // - If there is a handler assigned to the generated initial token value, it is not necessarily + // this handler. + // - If there is no handler assigned to the generated initial token value, the handler may still + // be registered under a different token. + // + // Effectively the only reasonable thing to do with this value is to use it as a good starting point + // for generating a token for handler. + // + // We want to generate a token value that has the following properties: + // 1. Is quickly obtained from the handler instance (in this case, it doesn't depend on it at all). + // 2. Uses bits in the upper 32 bits of the 64 bit value, in order to avoid bugs where code + // may assume the value is really just 32 bits. + // 3. Uses bits in the bottom 32 bits of the 64 bit value, in order to ensure that code doesn't + // take a dependency on them always being 0. + // + // The simple algorithm chosen here is to simply assign the upper 32 bits the metadata token of the + // event handler type, and the lower 32 bits to an incremental counter starting from some arbitrary + // constant. Using the metadata token for the upper 32 bits gives us at least a small chance of being + // able to identify a totally corrupted token if we ever come across one in a minidump or other scenario. + // + // We should feel free to change this algorithm as other requirements / optimizations become available. + // This implementation is sufficiently random that code cannot simply guess the value to take a dependency + // upon it. (Simply applying the hash-value algorithm directly won't work in the case of collisions, + // where we'll use a different token value). + int tokenLow32Bits; + +#if NET8_0_OR_GREATER + do + { + // When on .NET 6+, just iterate on TryAdd, which allows skipping the extra + // lookup on the last iteration (as the handler is added rigth away instead). + // + // We're doing this do-while loop here and incrementing 'm_low32Bits' on every failed insertion to work + // around one possible (theoretical) performance problem. Suppose the candidate token was somehow already + // used (not entirely clear when that would happen in practice). Incrementing only the local value from the + // loop would mean we could "race past" the value in 'm_low32Bits', meaning that all subsequent registrations + // would then also go through unnecessary extra lookups as the value of those lower 32 bits "catches up" to + // the one that ended up being used here. So we can avoid that by simply incrementing both of them every time. + tokenLow32Bits = m_low32Bits++; + } + while (!m_tokens.TryAdd(tokenLow32Bits, handler)); +#else + do + { + tokenLow32Bits = m_low32Bits++; + } + while (m_tokens.ContainsKey(tokenLow32Bits)); + m_tokens[tokenLow32Bits] = handler; +#endif + // The real event registration token is composed this way: + // - The upper 32 bits are the hashcode of the T type argument. + // - The lower 32 bits are the valid token computed above. + return new() { Value = (long)(((ulong)(uint)TypeOfTHashCode << 32) | (uint)tokenLow32Bits) }; + } + } + + /// + /// Removes an event handler from the table and retrieves the delegate associated with the input token, if it exists. + /// + /// The registration token to use to remove the event handler. + /// The resulting delegate associated with , if it exists. + /// Whether or not a registered event handler could be retrieved and removed from the table. + public bool RemoveEventHandler( + EventRegistrationToken token, +#if NET8_0_OR_GREATER + [NotNullWhen(true)] +#endif + out T? handler) + { + // If the token doesn't have the upper 32 bits set to the hashcode of the delegate + // type in use, we know that the token cannot possibly have a registered handler. + // + // Note that both here right after the right shift by 32 bits (since we want to read + // the upper 32 bits to compare against the T hashcode) and below (where we want to + // read the lower 32 bits to use as lookup index into our dictionary), we're just + // casting to int as a simple and efficient way of truncating the input 64 bit value. + // That is, '(int)i64' is the same as '(int)(i64 & 0xFFFFFFFF)', but more readable. + if ((int)((ulong)token.Value >> 32) != TypeOfTHashCode) + { + handler = null; + + return false; + } + + lock (m_tokens) + { +#if NET8_0_OR_GREATER + // On .NET 6 and above, we can use a single lookup to both check whether the token + // exists in the table, remove it, and also retrieve the removed handler to return. + if (m_tokens.Remove((int)token.Value, out object? obj)) + { + handler = Unsafe.As(obj); + + return true; + } +#else + if (m_tokens.TryGetValue((int)token.Value, out object? obj)) + { + m_tokens.Remove((int)token.Value); + + handler = Unsafe.As(obj); + + return true; + } +#endif + } + + handler = null; + + return false; + } + } +} + +// Restore in case this file is merged with others. +#nullable restore \ No newline at end of file diff --git a/src/WinRT.Runtime/ExceptionHelpers.Microsoft.cs b/src/WinRT.Runtime/ExceptionHelpers.Microsoft.cs new file mode 100644 index 000000000..76f32ca36 --- /dev/null +++ b/src/WinRT.Runtime/ExceptionHelpers.Microsoft.cs @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.UI.Xaml +{ + namespace Automation + { +#if EMBED + internal +#else + public +#endif + class ElementNotAvailableException : Exception + { + public ElementNotAvailableException() + : base("The element is not available.") + { + HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTAVAILABLE; + } + + public ElementNotAvailableException(string message) + : base(message) + { + HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTAVAILABLE; + } + + public ElementNotAvailableException(string message, Exception innerException) + : base(message, innerException) + { + HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTAVAILABLE; + } + } + +#if EMBED + internal +#else + public +#endif + class ElementNotEnabledException : Exception + { + public ElementNotEnabledException() + : base("The element is not enabled.") + { + HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTENABLED; + } + + public ElementNotEnabledException(string message) + : base(message) + { + HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTENABLED; + } + + public ElementNotEnabledException(string message, Exception innerException) + : base(message, innerException) + { + HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTENABLED; + } + } + } + namespace Markup + { + +#if EMBED + internal +#else + public +#endif + class XamlParseException : Exception + { + public XamlParseException() + : base("XAML parsing failed.") + { + HResult = WinRT.ExceptionHelpers.E_XAMLPARSEFAILED; + } + + public XamlParseException(string message) + : base(message) + { + HResult = WinRT.ExceptionHelpers.E_XAMLPARSEFAILED; + } + + public XamlParseException(string message, Exception innerException) + : base(message, innerException) + { + HResult = WinRT.ExceptionHelpers.E_XAMLPARSEFAILED; + } + } + } + +#if EMBED + internal +#else + public +#endif + class LayoutCycleException : Exception + { + public LayoutCycleException() + : base("A cycle occurred while laying out the GUI.") + { + HResult = WinRT.ExceptionHelpers.E_LAYOUTCYCLE; + } + + public LayoutCycleException(string message) + : base(message) + { + HResult = WinRT.ExceptionHelpers.E_LAYOUTCYCLE; + } + + public LayoutCycleException(string message, Exception innerException) + : base(message, innerException) + { + HResult = WinRT.ExceptionHelpers.E_LAYOUTCYCLE; + } + } +} diff --git a/src/WinRT.Runtime/ExceptionHelpers.Windows.net5.cs b/src/WinRT.Runtime/ExceptionHelpers.Windows.net5.cs new file mode 100644 index 000000000..852e6e62d --- /dev/null +++ b/src/WinRT.Runtime/ExceptionHelpers.Windows.net5.cs @@ -0,0 +1,118 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Windows.UI.Xaml +{ + namespace Automation + { +#if EMBED + internal +#else + public +#endif + class ElementNotAvailableException : Exception + { + public ElementNotAvailableException() + : base("The element is not available.") + { + HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTAVAILABLE; + } + + public ElementNotAvailableException(string message) + : base(message) + { + HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTAVAILABLE; + } + + public ElementNotAvailableException(string message, Exception innerException) + : base(message, innerException) + { + HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTAVAILABLE; + } + } + +#if EMBED + internal +#else + public +#endif + class ElementNotEnabledException : Exception + { + public ElementNotEnabledException() + : base("The element is not enabled.") + { + HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTENABLED; + } + + public ElementNotEnabledException(string message) + : base(message) + { + HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTENABLED; + } + + public ElementNotEnabledException(string message, Exception innerException) + : base(message, innerException) + { + HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTENABLED; + } + } + } + + namespace Markup + { + +#if EMBED + internal +#else + public +#endif + class XamlParseException : Exception + { + public XamlParseException() + : base("XAML parsing failed.") + { + HResult = WinRT.ExceptionHelpers.E_XAMLPARSEFAILED; + } + + public XamlParseException(string message) + : base(message) + { + HResult = WinRT.ExceptionHelpers.E_XAMLPARSEFAILED; + } + + public XamlParseException(string message, Exception innerException) + : base(message, innerException) + { + HResult = WinRT.ExceptionHelpers.E_XAMLPARSEFAILED; + } + } + } + +#if EMBED + internal +#else + public +#endif + class LayoutCycleException : Exception + { + public LayoutCycleException() + : base("A cycle occurred while laying out the GUI.") + { + HResult = WinRT.ExceptionHelpers.E_LAYOUTCYCLE; + } + + public LayoutCycleException(string message) + : base(message) + { + HResult = WinRT.ExceptionHelpers.E_LAYOUTCYCLE; + } + + public LayoutCycleException(string message, Exception innerException) + : base(message, innerException) + { + HResult = WinRT.ExceptionHelpers.E_LAYOUTCYCLE; + } + } +} diff --git a/src/WinRT.Runtime/ExceptionHelpers.cs b/src/WinRT.Runtime/ExceptionHelpers.cs index 0a345467d..35b1296cf 100644 --- a/src/WinRT.Runtime/ExceptionHelpers.cs +++ b/src/WinRT.Runtime/ExceptionHelpers.cs @@ -1,531 +1,803 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections; -using System.Runtime.ExceptionServices; -using System.Runtime.InteropServices; -using WinRT.Interop; - -namespace WinRT -{ - -#if EMBED - internal -#else - public -#endif - static unsafe class ExceptionHelpers - { - private const int COR_E_OBJECTDISPOSED = unchecked((int)0x80131622); - private const int RO_E_CLOSED = unchecked((int)0x80000013); - internal const int E_BOUNDS = unchecked((int)0x8000000b); - internal const int E_CHANGED_STATE = unchecked((int)0x8000000c); - private const int E_ILLEGAL_STATE_CHANGE = unchecked((int)0x8000000d); - private const int E_ILLEGAL_METHOD_CALL = unchecked((int)0x8000000e); - private const int E_ILLEGAL_DELEGATE_ASSIGNMENT = unchecked((int)0x80000018); - private const int APPMODEL_ERROR_NO_PACKAGE = unchecked((int)0x80073D54); - internal const int E_XAMLPARSEFAILED = unchecked((int)0x802B000A); - internal const int E_LAYOUTCYCLE = unchecked((int)0x802B0014); - internal const int E_ELEMENTNOTENABLED = unchecked((int)0x802B001E); - internal const int E_ELEMENTNOTAVAILABLE = unchecked((int)0x802B001F); - internal const int ERROR_INVALID_WINDOW_HANDLE = unchecked((int)0x80070578); - - [DllImport("oleaut32.dll")] - private static extern int SetErrorInfo(uint dwReserved, IntPtr perrinfo); - - private static delegate* unmanaged[Stdcall] getRestrictedErrorInfo; - private static delegate* unmanaged[Stdcall] setRestrictedErrorInfo; - private static delegate* unmanaged[Stdcall] roOriginateLanguageException; - private static delegate* unmanaged[Stdcall] roReportUnhandledError; - - private static readonly bool initialized = Initialize(); - - private static bool Initialize() - { - IntPtr winRTErrorModule = Platform.LoadLibraryExW("api-ms-win-core-winrt-error-l1-1-1.dll", IntPtr.Zero, (uint)DllImportSearchPath.System32); - if (winRTErrorModule != IntPtr.Zero) - { - roOriginateLanguageException = (delegate* unmanaged[Stdcall])Platform.GetProcAddress(winRTErrorModule, "RoOriginateLanguageException"); - roReportUnhandledError = (delegate* unmanaged[Stdcall])Platform.GetProcAddress(winRTErrorModule, "RoReportUnhandledError"); - } - else - { - winRTErrorModule = Platform.LoadLibraryExW("api-ms-win-core-winrt-error-l1-1-0.dll", IntPtr.Zero, (uint)DllImportSearchPath.System32); - } - - if (winRTErrorModule != IntPtr.Zero) - { - getRestrictedErrorInfo = (delegate* unmanaged[Stdcall])Platform.GetProcAddress(winRTErrorModule, "GetRestrictedErrorInfo"); - setRestrictedErrorInfo = (delegate* unmanaged[Stdcall])Platform.GetProcAddress(winRTErrorModule, "SetRestrictedErrorInfo"); - } - - return true; - } - - public static void ThrowExceptionForHR(int hr) - { - if (hr < 0) - { - Throw(hr); - } - - static void Throw(int hr) - { - Exception ex = GetExceptionForHR(hr, useGlobalErrorState: true, out bool restoredExceptionFromGlobalState); - if (restoredExceptionFromGlobalState) - { - ExceptionDispatchInfo.Capture(ex).Throw(); - } - else - { - throw ex; - } - } - } - - public static Exception GetExceptionForHR(int hr) => hr >= 0 ? null : GetExceptionForHR(hr, true, out _); - - private static Exception GetExceptionForHR(int hr, bool useGlobalErrorState, out bool restoredExceptionFromGlobalState) - { - restoredExceptionFromGlobalState = false; - - ObjectReference iErrorInfo = null; - IObjectReference restrictedErrorInfoToSave = null; - Exception ex; - string description = null; - string restrictedError = null; - string restrictedErrorReference = null; - string restrictedCapabilitySid = null; - bool hasOtherLanguageException = false; - - if (useGlobalErrorState && getRestrictedErrorInfo != null) - { - Marshal.ThrowExceptionForHR(getRestrictedErrorInfo(out IntPtr restrictedErrorInfoPtr)); - - if (restrictedErrorInfoPtr != IntPtr.Zero) - { - IObjectReference restrictedErrorInfoRef = ObjectReference.Attach(ref restrictedErrorInfoPtr); - restrictedErrorInfoToSave = restrictedErrorInfoRef.As(); - - ABI.WinRT.Interop.IRestrictedErrorInfo restrictedErrorInfo = new ABI.WinRT.Interop.IRestrictedErrorInfo(restrictedErrorInfoRef); - restrictedErrorInfo.GetErrorDetails(out description, out int hrLocal, out restrictedError, out restrictedCapabilitySid); - restrictedErrorReference = restrictedErrorInfo.GetReference(); - if (restrictedErrorInfoRef.TryAs(out var languageErrorInfoRef) >= 0) - { - ILanguageExceptionErrorInfo languageErrorInfo = new ABI.WinRT.Interop.ILanguageExceptionErrorInfo(languageErrorInfoRef); - using IObjectReference languageException = languageErrorInfo.GetLanguageException(); - if (languageException is object) - { - if (languageException.IsReferenceToManagedObject) - { - ex = ComWrappersSupport.FindObject(languageException.ThisPtr); - if (GetHRForException(ex) == hr) - { - restoredExceptionFromGlobalState = true; - return ex; - } - } - else - { - hasOtherLanguageException = true; - } - } - } - else - { - if (hr == hrLocal) - { - restrictedErrorInfoRef.TryAs(out iErrorInfo); - } - } - } - } - - using (iErrorInfo) - { - switch (hr) - { - case E_ILLEGAL_STATE_CHANGE: - case E_ILLEGAL_METHOD_CALL: - case E_ILLEGAL_DELEGATE_ASSIGNMENT: - case APPMODEL_ERROR_NO_PACKAGE: - ex = new InvalidOperationException(description); - break; - case E_XAMLPARSEFAILED: - ex = new Microsoft.UI.Xaml.Markup.XamlParseException(); - break; - case E_LAYOUTCYCLE: - ex = new Microsoft.UI.Xaml.LayoutCycleException(); - break; - case E_ELEMENTNOTAVAILABLE: - ex = new Microsoft.UI.Xaml.Automation.ElementNotAvailableException(); - break; - case E_ELEMENTNOTENABLED: - ex = new Microsoft.UI.Xaml.Automation.ElementNotEnabledException(); - break; - case ERROR_INVALID_WINDOW_HANDLE: - ex = new System.Runtime.InteropServices.COMException( -@"Invalid window handle. (0x80070578) -Consider WindowNative, InitializeWithWindow -See https://aka.ms/cswinrt/interop#windows-sdk", - ERROR_INVALID_WINDOW_HANDLE); - break; - default: - ex = Marshal.GetExceptionForHR(hr, iErrorInfo?.ThisPtr ?? (IntPtr)(-1)); - break; - } - } - - ex.AddExceptionDataForRestrictedErrorInfo( - description, - restrictedError, - restrictedErrorReference, - restrictedCapabilitySid, - restrictedErrorInfoToSave, - hasOtherLanguageException); - - return ex; - } - - public static unsafe void SetErrorInfo(Exception ex) - { - if (getRestrictedErrorInfo != null && setRestrictedErrorInfo != null && roOriginateLanguageException != null) - { - // If the exception has information for an IRestrictedErrorInfo, use that - // as our error so as to propagate the error through WinRT end-to-end. - if (ex.TryGetRestrictedLanguageErrorObject(out var restrictedErrorObject)) - { - using (restrictedErrorObject) - { - setRestrictedErrorInfo(restrictedErrorObject.ThisPtr); - } - } - else - { - string message = ex.Message; - if (string.IsNullOrEmpty(message)) - { - message = ex.GetType().FullName; - } - - IntPtr hstring; - - if (Platform.WindowsCreateString(message, message.Length, &hstring) != 0) - { - hstring = IntPtr.Zero; - } - - using var managedExceptionWrapper = ComWrappersSupport.CreateCCWForObject(ex); - roOriginateLanguageException(GetHRForException(ex), hstring, managedExceptionWrapper.ThisPtr); - } - } - else - { - using var iErrorInfo = ComWrappersSupport.CreateCCWForObject(new ManagedExceptionErrorInfo(ex)); - SetErrorInfo(0, iErrorInfo.ThisPtr); - } - } - - public static void ReportUnhandledError(Exception ex) - { - SetErrorInfo(ex); - if (getRestrictedErrorInfo != null && roReportUnhandledError != null) - { - Marshal.ThrowExceptionForHR(getRestrictedErrorInfo(out IntPtr ppRestrictedErrorInfo)); - using var restrictedErrorRef = ObjectReference.Attach(ref ppRestrictedErrorInfo); - roReportUnhandledError(restrictedErrorRef.ThisPtr); - } - } - - public static int GetHRForException(Exception ex) - { - int hr = ex.HResult; - if (ex.TryGetRestrictedLanguageErrorObject(out var restrictedErrorObject)) - { - restrictedErrorObject.AsType().GetErrorDetails(out _, out hr, out _, out _); - } - if (hr == COR_E_OBJECTDISPOSED) - { - return RO_E_CLOSED; - } - return hr; - } - - // - // Exception requires anything to be added into Data dictionary is serializable - // This wrapper is made serializable to satisfy this requirement but does NOT serialize - // the object and simply ignores it during serialization, because we only need - // the exception instance in the app to hold the error object alive. - // - [Serializable] - internal sealed class __RestrictedErrorObject - { - // Hold the error object instance but don't serialize/deserialize it - [NonSerialized] - private readonly IObjectReference _realErrorObject; - - internal __RestrictedErrorObject(IObjectReference errorObject) - { - _realErrorObject = errorObject; - } - - public IObjectReference RealErrorObject - { - get - { - return _realErrorObject; - } - } - } - - internal static void AddExceptionDataForRestrictedErrorInfo( - this Exception ex, - string description, - string restrictedError, - string restrictedErrorReference, - string restrictedCapabilitySid, - IObjectReference restrictedErrorObject, - bool hasRestrictedLanguageErrorObject = false) - { - IDictionary dict = ex.Data; - if (dict != null) - { - dict.Add("Description", description); - dict.Add("RestrictedDescription", restrictedError); - dict.Add("RestrictedErrorReference", restrictedErrorReference); - dict.Add("RestrictedCapabilitySid", restrictedCapabilitySid); - - // Keep the error object alive so that user could retrieve error information - // using Data["RestrictedErrorReference"] - dict.Add("__RestrictedErrorObjectReference", restrictedErrorObject == null ? null : new __RestrictedErrorObject(restrictedErrorObject)); - dict.Add("__HasRestrictedLanguageErrorObject", hasRestrictedLanguageErrorObject); - } - } - - internal static bool TryGetRestrictedLanguageErrorObject( - this Exception ex, - out IObjectReference restrictedErrorObject) - { - restrictedErrorObject = null; - IDictionary dict = ex.Data; - if (dict != null && dict.Contains("__HasRestrictedLanguageErrorObject")) - { - if (dict.Contains("__RestrictedErrorObjectReference")) - { - if (dict["__RestrictedErrorObjectReference"] is __RestrictedErrorObject restrictedObject) - restrictedErrorObject = restrictedObject.RealErrorObject; - } - return (bool)dict["__HasRestrictedLanguageErrorObject"]!; - } - - return false; - } - - public static Exception AttachRestrictedErrorInfo(Exception e) - { - // If there is no exception, then the restricted error info doesn't apply to it - if (e != null) - { - try - { - // Get the restricted error info for this thread and see if it may correlate to the current - // exception object. Note that in general the thread's IRestrictedErrorInfo is not meant for - // exceptions that are marshaled Windows.Foundation.HResults and instead are intended for - // HRESULT ABI return values. However, in many cases async APIs will set the thread's restricted - // error info as a convention in order to provide extended debugging information for the ErrorCode - // property. - Marshal.ThrowExceptionForHR(getRestrictedErrorInfo(out IntPtr restrictedErrorInfoPtr)); - - if (restrictedErrorInfoPtr != IntPtr.Zero) - { - IObjectReference restrictedErrorInfoRef = ObjectReference.Attach(ref restrictedErrorInfoPtr); - - ABI.WinRT.Interop.IRestrictedErrorInfo restrictedErrorInfo = new ABI.WinRT.Interop.IRestrictedErrorInfo(restrictedErrorInfoRef); - - restrictedErrorInfo.GetErrorDetails(out string description, - out int restrictedErrorInfoHResult, - out string restrictedDescription, - out string capabilitySid); - - // Since this is a special case where by convention there may be a correlation, there is not a - // guarantee that the restricted error info does belong to the async error code. In order to - // reduce the risk that we associate incorrect information with the exception object, we need - // to apply a heuristic where we attempt to match the current exception's HRESULT with the - // HRESULT the IRestrictedErrorInfo belongs to. If it is a match we will assume association - // for the IAsyncInfo case. - if (e.HResult == restrictedErrorInfoHResult) - { - e.AddExceptionDataForRestrictedErrorInfo(description, - restrictedDescription, - restrictedErrorInfo.GetReference(), - capabilitySid, - restrictedErrorInfoRef.As()); - } - } - } - catch - { - // If we can't get the restricted error info, then proceed as if it isn't associated with this - // error. - } - } - - return e; - } - } - -#if EMBED - internal -#else - public -#endif - static class ExceptionExtensions - { - public static void SetHResult(this Exception ex, int value) - { - ex.GetType().GetProperty("HResult").SetValue(ex, value); - } - - internal static Exception GetExceptionForHR(this Exception innerException, int hresult, string messageResource) - { - Exception e; - if (innerException != null) - { - string message = innerException.Message ?? messageResource; - e = new Exception(message, innerException); - } - else - { - e = new Exception(messageResource); - } - e.SetHResult(hresult); - return e; - } - } -} - -namespace Microsoft.UI.Xaml -{ - using System.Runtime.Serialization; - namespace Automation - { - [Serializable] -#if EMBED - internal -#else - public -#endif - class ElementNotAvailableException : Exception - { - public ElementNotAvailableException() - : base("The element is not available.") - { - HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTAVAILABLE; - } - - public ElementNotAvailableException(string message) - : base(message) - { - HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTAVAILABLE; - } - - public ElementNotAvailableException(string message, Exception innerException) - : base(message, innerException) - { - HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTAVAILABLE; - } - - protected ElementNotAvailableException(SerializationInfo serializationInfo, StreamingContext streamingContext) - : base(serializationInfo, streamingContext) - { - } - } - -#if EMBED - internal -#else - public -#endif - class ElementNotEnabledException : Exception - { - public ElementNotEnabledException() - : base("The element is not enabled.") - { - HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTENABLED; - } - - public ElementNotEnabledException(string message) - : base(message) - { - HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTENABLED; - } - - public ElementNotEnabledException(string message, Exception innerException) - : base(message, innerException) - { - HResult = WinRT.ExceptionHelpers.E_ELEMENTNOTENABLED; - } - } - } - namespace Markup - { - -#if EMBED - internal -#else - public -#endif - class XamlParseException : Exception - { - public XamlParseException() - : base("XAML parsing failed.") - { - HResult = WinRT.ExceptionHelpers.E_XAMLPARSEFAILED; - } - - public XamlParseException(string message) - : base(message) - { - HResult = WinRT.ExceptionHelpers.E_XAMLPARSEFAILED; - } - - public XamlParseException(string message, Exception innerException) - : base(message, innerException) - { - HResult = WinRT.ExceptionHelpers.E_XAMLPARSEFAILED; - } - } - } - [Serializable] -#if EMBED - internal -#else - public -#endif - class LayoutCycleException : Exception - { - public LayoutCycleException() - : base("A cycle occurred while laying out the GUI.") - { - HResult = WinRT.ExceptionHelpers.E_LAYOUTCYCLE; - } - - public LayoutCycleException(string message) - : base(message) - { - HResult = WinRT.ExceptionHelpers.E_LAYOUTCYCLE; - } - - public LayoutCycleException(string message, Exception innerException) - : base(message, innerException) - { - HResult = WinRT.ExceptionHelpers.E_LAYOUTCYCLE; - } - - protected LayoutCycleException(SerializationInfo serializationInfo, StreamingContext streamingContext) - : base(serializationInfo, streamingContext) - { - } - } +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Runtime.CompilerServices; +using System.Runtime.ExceptionServices; +using System.Runtime.InteropServices; +using System.Text; +using WinRT.Interop; + +namespace WinRT +{ + +#if EMBED + internal +#else + public +#endif + static unsafe class ExceptionHelpers + { + private const int COR_E_OBJECTDISPOSED = unchecked((int)0x80131622); + private const int COR_E_OPERATIONCANCELED = unchecked((int)0x8013153b); + private const int COR_E_ARGUMENTOUTOFRANGE = unchecked((int)0x80131502); + private const int COR_E_INDEXOUTOFRANGE = unchecked((int)0x80131508); + private const int COR_E_TIMEOUT = unchecked((int)0x80131505); + private const int COR_E_INVALIDOPERATION = unchecked((int)0x80131509); + private const int RO_E_CLOSED = unchecked((int)0x80000013); + internal const int E_BOUNDS = unchecked((int)0x8000000b); + internal const int E_CHANGED_STATE = unchecked((int)0x8000000c); + private const int E_ILLEGAL_STATE_CHANGE = unchecked((int)0x8000000d); + private const int E_ILLEGAL_METHOD_CALL = unchecked((int)0x8000000e); + private const int E_ILLEGAL_DELEGATE_ASSIGNMENT = unchecked((int)0x80000018); + private const int APPMODEL_ERROR_NO_PACKAGE = unchecked((int)0x80073D54); + internal const int E_XAMLPARSEFAILED = unchecked((int)0x802B000A); + internal const int E_LAYOUTCYCLE = unchecked((int)0x802B0014); + internal const int E_ELEMENTNOTENABLED = unchecked((int)0x802B001E); + internal const int E_ELEMENTNOTAVAILABLE = unchecked((int)0x802B001F); + internal const int ERROR_INVALID_WINDOW_HANDLE = unchecked((int)0x80070578); + private const int E_POINTER = unchecked((int)0x80004003); + private const int E_NOTIMPL = unchecked((int)0x80004001); + private const int E_ACCESSDENIED = unchecked((int)0x80070005); + internal const int E_INVALIDARG = unchecked((int)0x80070057); + internal const int E_NOINTERFACE = unchecked((int)0x80004002); + private const int E_OUTOFMEMORY = unchecked((int)0x8007000e); + private const int E_NOTSUPPORTED = unchecked((int)0x80070032); + private const int ERROR_ARITHMETIC_OVERFLOW = unchecked((int)0x80070216); + private const int ERROR_FILENAME_EXCED_RANGE = unchecked((int)0x800700ce); + private const int ERROR_FILE_NOT_FOUND = unchecked((int)0x80070002); + private const int ERROR_HANDLE_EOF = unchecked((int)0x80070026); + private const int ERROR_PATH_NOT_FOUND = unchecked((int)0x80070003); + private const int ERROR_STACK_OVERFLOW = unchecked((int)0x800703e9); + private const int ERROR_BAD_FORMAT = unchecked((int)0x8007000b); + private const int ERROR_CANCELLED = unchecked((int)0x800704c7); + private const int ERROR_TIMEOUT = unchecked((int)0x800705b4); + internal const int REGDB_E_CLASSNOTREG = unchecked((int)0x80040154); + + private static delegate* unmanaged[Stdcall] getRestrictedErrorInfo; + private static delegate* unmanaged[Stdcall] setRestrictedErrorInfo; + private static delegate* unmanaged[Stdcall] roOriginateLanguageException; + private static delegate* unmanaged[Stdcall] roReportUnhandledError; + + private static readonly bool initialized = Initialize(); + + private static bool Initialize() + { + IntPtr winRTErrorModule = Platform.LoadLibraryExW("api-ms-win-core-winrt-error-l1-1-1.dll", IntPtr.Zero, (uint)DllImportSearchPath.System32); + if (winRTErrorModule != IntPtr.Zero) + { +#if NET7_0_OR_GREATER || CsWinRT_LANG_11_FEATURES + ReadOnlySpan langExceptionString = "RoOriginateLanguageException"u8; + ReadOnlySpan reportUnhandledErrorString = "RoReportUnhandledError"u8; +#else + ReadOnlySpan langExceptionString = Encoding.ASCII.GetBytes("RoOriginateLanguageException"); + ReadOnlySpan reportUnhandledErrorString = Encoding.ASCII.GetBytes("RoReportUnhandledError"); +#endif + + roOriginateLanguageException = (delegate* unmanaged[Stdcall])Platform.GetProcAddress(winRTErrorModule, langExceptionString); + roReportUnhandledError = (delegate* unmanaged[Stdcall])Platform.GetProcAddress(winRTErrorModule, reportUnhandledErrorString); + } + else + { + winRTErrorModule = Platform.LoadLibraryExW("api-ms-win-core-winrt-error-l1-1-0.dll", IntPtr.Zero, (uint)DllImportSearchPath.System32); + } + + if (winRTErrorModule != IntPtr.Zero) + { +#if NET7_0_OR_GREATER || CsWinRT_LANG_11_FEATURES + ReadOnlySpan getRestrictedErrorInfoFuncName = "GetRestrictedErrorInfo"u8; + ReadOnlySpan setRestrictedErrorInfoFuncName = "SetRestrictedErrorInfo"u8; +#else + ReadOnlySpan getRestrictedErrorInfoFuncName = Encoding.ASCII.GetBytes("GetRestrictedErrorInfo"); + ReadOnlySpan setRestrictedErrorInfoFuncName = Encoding.ASCII.GetBytes("SetRestrictedErrorInfo"); +#endif + getRestrictedErrorInfo = (delegate* unmanaged[Stdcall])Platform.GetProcAddress(winRTErrorModule, getRestrictedErrorInfoFuncName); + setRestrictedErrorInfo = (delegate* unmanaged[Stdcall])Platform.GetProcAddress(winRTErrorModule, setRestrictedErrorInfoFuncName); + } + + return true; + } + + public static void ThrowExceptionForHR(int hr) + { + if (hr < 0) + { + Throw(hr); + } + + [MethodImpl(MethodImplOptions.NoInlining)] + static void Throw(int hr) + { + Exception ex = GetExceptionForHR(hr, useGlobalErrorState: true, true, out bool restoredExceptionFromGlobalState); + if (restoredExceptionFromGlobalState) + { + ExceptionDispatchInfo.Capture(ex).Throw(); + } + else + { + throw ex; + } + } + } + + // Retrieve restricted error info from thread without removing it, for propagation and debugging (watch, locals, etc) + private static ObjectReferenceValue BorrowRestrictedErrorInfo() + { + if (getRestrictedErrorInfo == null) + return default; + + IntPtr restrictedErrorInfoPtr = IntPtr.Zero; + Marshal.ThrowExceptionForHR(getRestrictedErrorInfo(&restrictedErrorInfoPtr)); + if (restrictedErrorInfoPtr == IntPtr.Zero) + return default; + + if (setRestrictedErrorInfo != null) + { + setRestrictedErrorInfo(restrictedErrorInfoPtr); + } + + return ObjectReferenceValue.Attach(ref restrictedErrorInfoPtr); + } + + public static Exception GetExceptionForHR(int hr) => hr >= 0 ? null : GetExceptionForHR(hr, true, false, out _); + + private static Exception GetExceptionForHR(int hr, bool useGlobalErrorState, bool associateErrorInfo, out bool restoredExceptionFromGlobalState) + { + restoredExceptionFromGlobalState = false; + + ObjectReference restrictedErrorInfoToSave = null; + Exception ex; + string description = null; + string restrictedError = null; + string restrictedErrorReference = null; + string restrictedCapabilitySid = null; + string errorMessage = string.Empty; + Exception internalGetGlobalErrorStateException = null; + + if (useGlobalErrorState) + { + ObjectReferenceValue restrictedErrorInfoValue = default; + try + { + restrictedErrorInfoValue = BorrowRestrictedErrorInfo(); + var restrictedErrorInfoValuePtr = restrictedErrorInfoValue.GetAbi(); + if (restrictedErrorInfoValuePtr != default) + { + if (Marshal.QueryInterface(restrictedErrorInfoValuePtr, ref Unsafe.AsRef(in IID.IID_ILanguageExceptionErrorInfo), out var languageErrorInfoPtr) >= 0) + { + try + { + ex = GetLanguageException(languageErrorInfoPtr, hr); + if (ex is not null) + { + restoredExceptionFromGlobalState = true; + if (associateErrorInfo) + { + ex.AddExceptionDataForRestrictedErrorInfo(ObjectReference.FromAbi(restrictedErrorInfoValuePtr, IID.IID_IRestrictedErrorInfo), true); + } + return ex; + } + } + finally + { + Marshal.Release(languageErrorInfoPtr); + } + } + + restrictedErrorInfoToSave = ObjectReference.FromAbi(restrictedErrorInfoValuePtr, IID.IID_IRestrictedErrorInfo); + ABI.WinRT.Interop.IRestrictedErrorInfoMethods.GetErrorDetails(restrictedErrorInfoValuePtr, out description, out int hrLocal, out restrictedError, out restrictedCapabilitySid); + restrictedErrorReference = ABI.WinRT.Interop.IRestrictedErrorInfoMethods.GetReference(restrictedErrorInfoValuePtr); + if (hr == hrLocal) + { + // For cross language WinRT exceptions, general information will be available in the description, + // which is populated from IRestrictedErrorInfo::GetErrorDetails and more specific information will be available + // in the restrictedError which also comes from IRestrictedErrorInfo::GetErrorDetails. If both are available, we + + // need to concatinate them to produce the final exception message. + if (!string.IsNullOrEmpty(description)) + { + errorMessage += description; + + if (!string.IsNullOrEmpty(restrictedError)) + { + errorMessage += Environment.NewLine; + } + } + + if (!string.IsNullOrEmpty(restrictedError)) + { + errorMessage += restrictedError; + } + } + } + } + catch (Exception e) + { + // If we fail to get the error info or the exception from it, + // we fallback to using the hresult to create the exception. + // But we do store it in the exception data for debugging purposes. + Debug.Assert(false, e.Message, e.StackTrace); + internalGetGlobalErrorStateException = e; + } + finally + { + restrictedErrorInfoValue.Dispose(); + } + } + + if (string.IsNullOrWhiteSpace(errorMessage)) + { + char* message = default; + if (Platform.FormatMessageW(0x13FF /* FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK */, + null, + (uint)hr, + 0, + &message, + 0, + null) > 0) + { + errorMessage = $"{new string(message)}(0x{hr:X8})"; + + // LocalHandle isn't needed since FormatMessage uses LMEM_FIXED, + // and while we can use Marshal.FreeHGlobal since it uses LocalFree internally, + // it's not guranteed that this behavior stays the same in the future, + // especially considering the method's name, so it's safer to use LocalFree directly. + Platform.LocalFree(message); + } + } + + switch (hr) + { + case E_CHANGED_STATE: + case E_ILLEGAL_STATE_CHANGE: + case E_ILLEGAL_METHOD_CALL: + case E_ILLEGAL_DELEGATE_ASSIGNMENT: + case APPMODEL_ERROR_NO_PACKAGE: + case COR_E_INVALIDOPERATION: + ex = !string.IsNullOrEmpty(errorMessage) ? new InvalidOperationException(errorMessage) : new InvalidOperationException(); + break; + case E_XAMLPARSEFAILED: +#if NET + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + ex = !string.IsNullOrEmpty(errorMessage) ? new Windows.UI.Xaml.Markup.XamlParseException(errorMessage) : new Windows.UI.Xaml.Markup.XamlParseException(); + } + else +#endif + { + ex = !string.IsNullOrEmpty(errorMessage) ? new Microsoft.UI.Xaml.Markup.XamlParseException(errorMessage) : new Microsoft.UI.Xaml.Markup.XamlParseException(); + } + break; + case E_LAYOUTCYCLE: +#if NET + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + ex = !string.IsNullOrEmpty(errorMessage) ? new Windows.UI.Xaml.LayoutCycleException(errorMessage) : new Windows.UI.Xaml.LayoutCycleException(); + } + else +#endif + { + ex = !string.IsNullOrEmpty(errorMessage) ? new Microsoft.UI.Xaml.LayoutCycleException(errorMessage) : new Microsoft.UI.Xaml.LayoutCycleException(); + } + break; + case E_ELEMENTNOTAVAILABLE: +#if NET + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + ex = !string.IsNullOrEmpty(errorMessage) ? new Windows.UI.Xaml.Automation.ElementNotAvailableException(errorMessage) : new Windows.UI.Xaml.Automation.ElementNotAvailableException(); + } + else +#endif + { + ex = !string.IsNullOrEmpty(errorMessage) ? new Microsoft.UI.Xaml.Automation.ElementNotAvailableException(errorMessage) : new Microsoft.UI.Xaml.Automation.ElementNotAvailableException(); + } + break; + case E_ELEMENTNOTENABLED: +#if NET + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + ex = !string.IsNullOrEmpty(errorMessage) ? new Windows.UI.Xaml.Automation.ElementNotEnabledException(errorMessage) : new Windows.UI.Xaml.Automation.ElementNotEnabledException(); + } + else +#endif + { + ex = !string.IsNullOrEmpty(errorMessage) ? new Microsoft.UI.Xaml.Automation.ElementNotEnabledException(errorMessage) : new Microsoft.UI.Xaml.Automation.ElementNotEnabledException(); + } + break; + case ERROR_INVALID_WINDOW_HANDLE: + ex = new COMException( +@"Invalid window handle. (0x80070578) +Consider WindowNative, InitializeWithWindow +See https://aka.ms/cswinrt/interop#windows-sdk", + ERROR_INVALID_WINDOW_HANDLE); + break; + case RO_E_CLOSED: + ex = !string.IsNullOrEmpty(errorMessage) ? new ObjectDisposedException(string.Empty, errorMessage) : new ObjectDisposedException(string.Empty); + break; + case E_POINTER: + ex = !string.IsNullOrEmpty(errorMessage) ? new NullReferenceException(errorMessage) : new NullReferenceException(); + break; + case E_NOTIMPL: + ex = !string.IsNullOrEmpty(errorMessage) ? new NotImplementedException(errorMessage) : new NotImplementedException(); + break; + case E_ACCESSDENIED: + ex = !string.IsNullOrEmpty(errorMessage) ? new UnauthorizedAccessException(errorMessage) : new UnauthorizedAccessException(); + break; + case E_INVALIDARG: + ex = !string.IsNullOrEmpty(errorMessage) ? new ArgumentException(errorMessage) : new ArgumentException(); + break; + case E_NOINTERFACE: + ex = !string.IsNullOrEmpty(errorMessage) ? new InvalidCastException(errorMessage) : new InvalidCastException(); + break; + case E_OUTOFMEMORY: + ex = !string.IsNullOrEmpty(errorMessage) ? new OutOfMemoryException(errorMessage) : new OutOfMemoryException(); + break; + case E_BOUNDS: + ex = !string.IsNullOrEmpty(errorMessage) ? new ArgumentOutOfRangeException(errorMessage) : new ArgumentOutOfRangeException(); + break; + case E_NOTSUPPORTED: + ex = !string.IsNullOrEmpty(errorMessage) ? new NotSupportedException(errorMessage) : new NotSupportedException(); + break; + case ERROR_ARITHMETIC_OVERFLOW: + ex = !string.IsNullOrEmpty(errorMessage) ? new ArithmeticException(errorMessage) : new ArithmeticException(); + break; + case ERROR_FILENAME_EXCED_RANGE: + ex = !string.IsNullOrEmpty(errorMessage) ? new PathTooLongException(errorMessage) : new PathTooLongException(); + break; + case ERROR_FILE_NOT_FOUND: + ex = !string.IsNullOrEmpty(errorMessage) ? new FileNotFoundException(errorMessage) : new FileNotFoundException(); + break; + case ERROR_HANDLE_EOF: + ex = !string.IsNullOrEmpty(errorMessage) ? new EndOfStreamException(errorMessage) : new EndOfStreamException(); + break; + case ERROR_PATH_NOT_FOUND: + ex = !string.IsNullOrEmpty(errorMessage) ? new DirectoryNotFoundException(errorMessage) : new DirectoryNotFoundException(); + break; + case ERROR_STACK_OVERFLOW: + ex = !string.IsNullOrEmpty(errorMessage) ? new StackOverflowException(errorMessage) : new StackOverflowException(); + break; + case ERROR_BAD_FORMAT: + ex = !string.IsNullOrEmpty(errorMessage) ? new BadImageFormatException(errorMessage) : new BadImageFormatException(); + break; + case ERROR_CANCELLED: + ex = !string.IsNullOrEmpty(errorMessage) ? new OperationCanceledException(errorMessage) : new OperationCanceledException(); + break; + case ERROR_TIMEOUT: + ex = !string.IsNullOrEmpty(errorMessage) ? new TimeoutException(errorMessage) : new TimeoutException(); + break; + + default: + ex = !string.IsNullOrEmpty(errorMessage) ? new COMException(errorMessage, hr) : new COMException($"0x{hr:X8}", hr); + break; + } + + // Ensure HResult matches. + ex.SetHResult(hr); + + if (useGlobalErrorState) + { + ex.AddExceptionDataForRestrictedErrorInfo( + description, + restrictedError, + restrictedErrorReference, + restrictedCapabilitySid, + restrictedErrorInfoToSave, + false, + internalGetGlobalErrorStateException); + } + + return ex; + } + + // This is a helper method specifically to be used by exception propagation scenarios where we carefully + // manage the lifetime of the CCW for the exception object to avoid cycles and thereby leaking it. + private static Exception GetLanguageException(IntPtr languageErrorInfoPtr, int hr) + { + // Check the error info first for the language exception. + var exception = GetLanguageExceptionInternal(languageErrorInfoPtr, hr); + if (exception is not null) + { + return exception; + } + + // If propagated exceptions are supported, traverse it and check if any one of those is our exception to reuse. + if (Marshal.QueryInterface(languageErrorInfoPtr, ref Unsafe.AsRef(in IID.IID_ILanguageExceptionErrorInfo2), out var languageErrorInfo2Ptr) >= 0) + { + IntPtr currentLanguageExceptionErrorInfo2Ptr = default; + try + { + currentLanguageExceptionErrorInfo2Ptr + = global::ABI.WinRT.Interop.ILanguageExceptionErrorInfo2Methods.GetPropagationContextHead(languageErrorInfo2Ptr); + while (currentLanguageExceptionErrorInfo2Ptr != default) + { + var propagatedException = GetLanguageExceptionInternal(currentLanguageExceptionErrorInfo2Ptr, hr); + if (propagatedException is not null) + { + return propagatedException; + } + + var previousLanguageExceptionErrorInfo2Ptr = currentLanguageExceptionErrorInfo2Ptr; + currentLanguageExceptionErrorInfo2Ptr = global::ABI.WinRT.Interop.ILanguageExceptionErrorInfo2Methods.GetPreviousLanguageExceptionErrorInfo(currentLanguageExceptionErrorInfo2Ptr); + Marshal.Release(previousLanguageExceptionErrorInfo2Ptr); + } + } + finally + { + MarshalExtensions.ReleaseIfNotNull(currentLanguageExceptionErrorInfo2Ptr); + Marshal.Release(languageErrorInfo2Ptr); + } + } + + return null; + + static Exception GetLanguageExceptionInternal(IntPtr languageErrorInfoPtr, int hr) + { + var languageExceptionPtr = ABI.WinRT.Interop.ILanguageExceptionErrorInfoMethods.GetLanguageException(languageErrorInfoPtr); + if (languageExceptionPtr != default) + { + try + { + if (IUnknownVftbl.IsReferenceToManagedObject(languageExceptionPtr)) + { + var ex = ComWrappersSupport.FindObject(languageExceptionPtr); + if (GetHRForException(ex) == hr) + { + return ex; + } + } + } + finally + { + Marshal.Release(languageExceptionPtr); + } + } + + return null; + } + } + + public static unsafe void SetErrorInfo(Exception ex) + { + try + { + if (getRestrictedErrorInfo != null && setRestrictedErrorInfo != null && roOriginateLanguageException != null) + { + // If the exception has an IRestrictedErrorInfo, use that as our error info + // to allow to propagate the original error through WinRT with the end to end information + // rather than losing that context. + if (ex.TryGetRestrictedLanguageErrorInfo(out var restrictedErrorObject, out var isLanguageException)) + { + // Capture the C# language exception if it hasn't already been captured previously either during the throw or during a propagation. + // Given the C# exception itself captures propagation context on rethrow, we don't do it each time. + if (!isLanguageException && + Marshal.QueryInterface(restrictedErrorObject.ThisPtr, ref Unsafe.AsRef(in IID.IID_ILanguageExceptionErrorInfo2), out var languageErrorInfo2Ptr) >= 0) + { + try + { + global::ABI.WinRT.Interop.ILanguageExceptionErrorInfo2Methods.CapturePropagationContext(languageErrorInfo2Ptr, ex); + } + finally + { + Marshal.Release(languageErrorInfo2Ptr); + } + } + else if (isLanguageException) + { + // Remove object reference to avoid cycles between error info holding exception + // and exception holding error info. We currently can't avoid this cycle + // when the C# exception is caught on the C# side. + ex.Data.Remove("__RestrictedErrorObjectReference"); + ex.Data.Remove("__HasRestrictedLanguageErrorObject"); + } + + setRestrictedErrorInfo(restrictedErrorObject.ThisPtr); + + GC.KeepAlive(restrictedErrorObject); + } + else + { + string message = ex.Message; + if (string.IsNullOrEmpty(message)) + { + message = ex.GetType().FullName; + } + + MarshalString.HSTRING_HEADER header = default; + IntPtr hstring = default; + + fixed (char* lpMessage = message) + { + if (Platform.WindowsCreateStringReference( + sourceString: (ushort*)lpMessage, + length: message.Length, + hstring_header: (IntPtr*)&header, + hstring: &hstring) != 0) + { + hstring = IntPtr.Zero; + } + +#if NET + IntPtr managedExceptionWrapper = ComWrappersSupport.CreateCCWForObjectUnsafe(ex); + + try + { + roOriginateLanguageException(GetHRForException(ex), hstring, managedExceptionWrapper); + } + finally + { + Marshal.Release(managedExceptionWrapper); + } +#else + using var managedExceptionWrapper = ComWrappersSupport.CreateCCWForObject(ex); + roOriginateLanguageException(GetHRForException(ex), hstring, managedExceptionWrapper.ThisPtr); +#endif + } + } + } + else + { + using var iErrorInfo = ComWrappersSupport.CreateCCWForObject(new ManagedExceptionErrorInfo(ex)); + Platform.SetErrorInfo(0, iErrorInfo.ThisPtr); + } + } + catch (Exception e) + { + // If we fail to set the error info, we continue on reporting the original exception. + Debug.Assert(false, e.Message, e.StackTrace); + } + } + + public static void ReportUnhandledError(Exception ex) + { + SetErrorInfo(ex); + if (roReportUnhandledError != null) + { + var restrictedErrorInfoValue = BorrowRestrictedErrorInfo(); + var restrictedErrorInfoValuePtr = restrictedErrorInfoValue.GetAbi(); + if (restrictedErrorInfoValuePtr != default) + { + roReportUnhandledError(restrictedErrorInfoValuePtr); + restrictedErrorInfoValue.Dispose(); + } + } + } + + public static int GetHRForException(Exception ex) + { + int hr = ex.HResult; + try + { + if (ex.TryGetRestrictedLanguageErrorInfo(out var restrictedErrorObject, out var _)) + { + ABI.WinRT.Interop.IRestrictedErrorInfoMethods.GetErrorDetails(restrictedErrorObject.ThisPtr, out hr); + GC.KeepAlive(restrictedErrorObject); + } + } + catch (Exception e) + { + // If we fail to get the hresult from the error info, we fallback to the exception hresult. + Debug.Assert(false, e.Message, e.StackTrace); + } + + switch (hr) + { + case COR_E_OBJECTDISPOSED: + return RO_E_CLOSED; + case COR_E_OPERATIONCANCELED: + return ERROR_CANCELLED; + case COR_E_ARGUMENTOUTOFRANGE: + case COR_E_INDEXOUTOFRANGE: + return E_BOUNDS; + case COR_E_TIMEOUT: + return ERROR_TIMEOUT; + + default: + return hr; + } + } + +#if !NET + // + // Exception requires anything to be added into Data dictionary is serializable + // This wrapper is made serializable to satisfy this requirement but does NOT serialize + // the object and simply ignores it during serialization, because we only need + // the exception instance in the app to hold the error object alive. + // + [Serializable] + internal sealed class __RestrictedErrorObject + { + // Hold the error object instance but don't serialize/deserialize it + [NonSerialized] + private readonly ObjectReference _realErrorObject; + internal __RestrictedErrorObject(ObjectReference errorObject) + { + _realErrorObject = errorObject; + } + public ObjectReference RealErrorObject + { + get + { + return _realErrorObject; + } + } + } +#endif + + internal static void AddExceptionDataForRestrictedErrorInfo( + this Exception ex, + string description, + string restrictedError, + string restrictedErrorReference, + string restrictedCapabilitySid, + ObjectReference restrictedErrorObject, + bool hasRestrictedLanguageErrorObject = false, + Exception internalGetGlobalErrorStateException = null) + { + IDictionary dict = ex.Data; + if (dict != null) + { + dict["Description"] = description; + dict["RestrictedDescription"] = restrictedError; + dict["RestrictedErrorReference"] = restrictedErrorReference; + dict["RestrictedCapabilitySid"] = restrictedCapabilitySid; + + // Keep the error object alive so that user could retrieve error information + // using Data["RestrictedErrorReference"] +#if NET + dict["__RestrictedErrorObjectReference"] = restrictedErrorObject; +#else + dict["__RestrictedErrorObjectReference"] = restrictedErrorObject == null ? null : new __RestrictedErrorObject(restrictedErrorObject); +#endif + dict["__HasRestrictedLanguageErrorObject"] = hasRestrictedLanguageErrorObject; + + if (internalGetGlobalErrorStateException != null) + { + dict["_InternalCsWinRTException"] = internalGetGlobalErrorStateException; + } + } + } + + internal static void AddExceptionDataForRestrictedErrorInfo( + this Exception ex, + ObjectReference restrictedErrorObject, + bool hasRestrictedLanguageErrorObject) + { + IDictionary dict = ex.Data; + if (dict != null) + { + // Keep the error object alive so that user could retrieve error information + // using Data["RestrictedErrorReference"] +#if NET + dict["__RestrictedErrorObjectReference"] = restrictedErrorObject; +#else + dict["__RestrictedErrorObjectReference"] = restrictedErrorObject == null ? null : new __RestrictedErrorObject(restrictedErrorObject); +#endif + dict["__HasRestrictedLanguageErrorObject"] = hasRestrictedLanguageErrorObject; + } + } + + internal static bool TryGetRestrictedLanguageErrorInfo( + this Exception ex, + out ObjectReference restrictedErrorObject, + out bool isLanguageException) + { + restrictedErrorObject = null; + isLanguageException = false; + + IDictionary dict = ex.Data; + if (dict != null ) + { + if (dict.Contains("__RestrictedErrorObjectReference")) + { +#if NET + restrictedErrorObject = (ObjectReference)dict["__RestrictedErrorObjectReference"]; +#else + restrictedErrorObject = ((__RestrictedErrorObject)dict["__RestrictedErrorObjectReference"])?.RealErrorObject; +#endif + } + + if (dict.Contains("__HasRestrictedLanguageErrorObject")) + { + isLanguageException = (bool)dict["__HasRestrictedLanguageErrorObject"]!; + } + + return restrictedErrorObject is not null; + } + + return false; + } + + public static Exception AttachRestrictedErrorInfo(Exception e) + { + // If there is no exception, then the restricted error info doesn't apply to it + if (e != null) + { + IntPtr restrictedErrorInfoPtr = IntPtr.Zero; + + try + { + // Get the restricted error info for this thread and see if it may correlate to the current + // exception object. Note that in general the thread's IRestrictedErrorInfo is not meant for + // exceptions that are marshaled Windows.Foundation.HResults and instead are intended for + // HRESULT ABI return values. However, in many cases async APIs will set the thread's restricted + // error info as a convention in order to provide extended debugging information for the ErrorCode + // property. + Marshal.ThrowExceptionForHR(getRestrictedErrorInfo(&restrictedErrorInfoPtr)); + + if (restrictedErrorInfoPtr != IntPtr.Zero) + { + ABI.WinRT.Interop.IRestrictedErrorInfoMethods.GetErrorDetails( + restrictedErrorInfoPtr, + out string description, + out int restrictedErrorInfoHResult, + out string restrictedDescription, + out string capabilitySid); + + // Since this is a special case where by convention there may be a correlation, there is not a + // guarantee that the restricted error info does belong to the async error code. In order to + // reduce the risk that we associate incorrect information with the exception object, we need + // to apply a heuristic where we attempt to match the current exception's HRESULT with the + // HRESULT the IRestrictedErrorInfo belongs to. If it is a match we will assume association + // for the IAsyncInfo case. + if (e.HResult == restrictedErrorInfoHResult) + { + string reference = ABI.WinRT.Interop.IRestrictedErrorInfoMethods.GetReference(restrictedErrorInfoPtr); + e.AddExceptionDataForRestrictedErrorInfo(description, + restrictedDescription, + reference, + capabilitySid, + ObjectReference.Attach(ref restrictedErrorInfoPtr, IID.IID_IRestrictedErrorInfo)); + } + } + } + catch + { + // If we can't get the restricted error info, then proceed as if it isn't associated with this + // error. + } + finally + { + MarshalExtensions.ReleaseIfNotNull(restrictedErrorInfoPtr); + } + } + + return e; + } + } + +#if EMBED + internal +#else + public +#endif + static class ExceptionExtensions + { + public static void SetHResult(this Exception ex, int value) + { +#if !NET + ex.GetType().GetProperty("HResult").SetValue(ex, value); +#else + ex.HResult = value; +#endif + } + + internal static Exception GetExceptionForHR(this Exception innerException, int hresult, string messageResource) + { + Exception e; + if (innerException != null) + { + string message = innerException.Message ?? messageResource; + e = new Exception(message, innerException); + } + else + { + e = new Exception(messageResource); + } + e.SetHResult(hresult); + return e; + } + } } \ No newline at end of file diff --git a/src/WinRT.Runtime/FundamentalMarshalers.cs b/src/WinRT.Runtime/FundamentalMarshalers.cs index c173d55b9..5aecd7780 100644 --- a/src/WinRT.Runtime/FundamentalMarshalers.cs +++ b/src/WinRT.Runtime/FundamentalMarshalers.cs @@ -2,33 +2,75 @@ // Licensed under the MIT License. using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; namespace ABI.System { + /// + /// Non-generic marshalling stubs used from . + /// This avoids the generic instantiations for all additional stubs for these ABI types. + /// + internal static class NonBlittableMarshallingStubs + { + // This can be shared for all DisposeMarshaler and DisposeAbi methods for these ABI types. + // None of these has any special logic that needs to run when those two APIs are invoked. + public static readonly Action NoOpFunc = NoOp; + + public static object Boolean_CreateMarshaler(bool value) => Boolean.CreateMarshaler(value); + public static object Boolean_GetAbi(object value) => Boolean.GetAbi((bool)value); + public static bool Boolean_FromAbi(object abi) => Boolean.FromAbi((byte)abi); + public static void Boolean_CopyAbi(object value, IntPtr dest) => Boolean.CopyAbi((bool)value, dest); + public static object Boolean_FromManaged(bool value) => Boolean.FromManaged(value); + + public static object Char_CreateMarshaler(char value) => Char.CreateMarshaler(value); + public static object Char_GetAbi(object value) => Char.GetAbi((char)value); + public static char Char_FromAbi(object abi) => Char.FromAbi((ushort)abi); + public static void Char_CopyAbi(object value, IntPtr dest) => Char.CopyAbi((char)value, dest); + public static object Char_FromManaged(char value) => Char.FromManaged(value); + + public static object TimeSpan_CreateMarshaler(global::System.TimeSpan value) => TimeSpan.CreateMarshaler(value); + public static object TimeSpan_GetAbi(object value) => TimeSpan.GetAbi((TimeSpan.Marshaler)value); + public static global::System.TimeSpan TimeSpan_FromAbi(object abi) => TimeSpan.FromAbi((TimeSpan)abi); + public static void TimeSpan_CopyAbi(object value, IntPtr dest) => TimeSpan.CopyAbi((TimeSpan.Marshaler)value, dest); + public static object TimeSpan_FromManaged(global::System.TimeSpan value) => TimeSpan.FromManaged(value); + + public static object DateTimeOffset_CreateMarshaler(global::System.DateTimeOffset value) => DateTimeOffset.CreateMarshaler(value); + public static object DateTimeOffset_GetAbi(object value) => DateTimeOffset.GetAbi((DateTimeOffset.Marshaler)value); + public static global::System.DateTimeOffset DateTimeOffset_FromAbi(object abi) => DateTimeOffset.FromAbi((DateTimeOffset)abi); + public static unsafe void DateTimeOffset_CopyAbi(object value, IntPtr dest) => DateTimeOffset.CopyAbi((DateTimeOffset.Marshaler)value, dest); + public static object DateTimeOffset_FromManaged(global::System.DateTimeOffset value) => DateTimeOffset.FromManaged(value); + + public static object Type_CreateMarshalerArray(global::System.Type[] value) => Type.CreateMarshalerArray(value); + public static object Exception_CreateMarshalerArray(global::System.Exception[] value) => global::WinRT.MarshalNonBlittable.CreateMarshalerArray(value); + + private static void NoOp(object obj) + { + } + } + internal struct Boolean { - byte value; public static bool CreateMarshaler(bool value) => value; - public static Boolean GetAbi(bool value) => new Boolean() { value = (byte)(value ? 1 : 0) }; - public static bool FromAbi(Boolean abi) => abi.value != 0; - public static unsafe void CopyAbi(bool value, IntPtr dest) => *(byte*)dest.ToPointer() = GetAbi(value).value; - public static Boolean FromManaged(bool value) => GetAbi(value); - public static unsafe void CopyManaged(bool arg, IntPtr dest) => *(byte*)dest.ToPointer() = FromManaged(arg).value; + public static byte GetAbi(bool value) => (byte)(value ? 1 : 0); + public static bool FromAbi(byte abi) => abi != 0; + public static unsafe void CopyAbi(bool value, IntPtr dest) => *(byte*)dest.ToPointer() = GetAbi(value); + public static byte FromManaged(bool value) => GetAbi(value); + public static unsafe void CopyManaged(bool arg, IntPtr dest) => *(byte*)dest.ToPointer() = FromManaged(arg); public static void DisposeMarshaler(bool m) { } - public static void DisposeAbi(Boolean abi) { } + public static void DisposeAbi(byte abi) { } } internal struct Char { - ushort value; public static char CreateMarshaler(char value) => value; - public static Char GetAbi(char value) => new Char() { value = (ushort)value }; - public static char FromAbi(Char abi) => (char)abi.value; - public static unsafe void CopyAbi(char value, IntPtr dest) => *(ushort*)dest.ToPointer() = GetAbi(value).value; - public static Char FromManaged(char value) => GetAbi(value); - public static unsafe void CopyManaged(char arg, IntPtr dest) => *(ushort*)dest.ToPointer() = FromManaged(arg).value; + public static ushort GetAbi(char value) => (ushort)value; + public static char FromAbi(ushort abi) => (char)abi; + public static unsafe void CopyAbi(char value, IntPtr dest) => *(ushort*)dest.ToPointer() = GetAbi(value); + public static ushort FromManaged(char value) => GetAbi(value); + public static unsafe void CopyManaged(char arg, IntPtr dest) => *(ushort*)dest.ToPointer() = FromManaged(arg); public static void DisposeMarshaler(char m) { } - public static void DisposeAbi(Char abi) { } + public static void DisposeAbi(ushort abi) { } } } diff --git a/src/WinRT.Runtime/GuidGenerator.cs b/src/WinRT.Runtime/GuidGenerator.cs index 2028abe82..3397a6e70 100644 --- a/src/WinRT.Runtime/GuidGenerator.cs +++ b/src/WinRT.Runtime/GuidGenerator.cs @@ -1,12 +1,14 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; +using System.Buffers; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Reflection; +using System.Runtime.CompilerServices; using System.Security.Cryptography; using System.Text; +using WinRT.Interop; namespace WinRT { @@ -17,18 +19,43 @@ namespace WinRT #endif static class GuidGenerator { +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2067", Justification = "This method only accesses 'Type.GUID', no fields are ever needed.")] +#endif public static Guid GetGUID(Type type) { - return type.GetGuidType().GUID; + type = type.GetGuidType(); + + // Only check the WUX/MUX types if the feature switch is set, to avoid introducing + // performance regressions in the standard case where MUX is targeted (default). + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + if (TryGetWindowsUIXamlIID(type, out Guid iid)) + { + return iid; + } + } + + return type.GUID; } - public static Guid GetIID( + public static Guid GetIID( #if NET [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] #endif Type type) { type = type.GetGuidType(); + + // Same optional check as above + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + if (TryGetWindowsUIXamlIID(type, out Guid iid)) + { + return iid; + } + } + if (!type.IsGenericType) { return type.GUID; @@ -36,21 +63,95 @@ public static Guid GetIID( return (Guid)type.GetField("PIID").GetValue(null); } - public static string GetSignature( + internal static bool TryGetWindowsUIXamlIID(Type type, out Guid iid) + { + if (type == typeof(global::ABI.System.Collections.Specialized.INotifyCollectionChanged)) + { + iid = IID.IID_WUX_INotifyCollectionChanged; + + return true; + } + + if (type == typeof(global::ABI.System.ComponentModel.INotifyPropertyChanged)) + { + iid = IID.IID_WUX_INotifyPropertyChanged; + + return true; + } + + if (type == typeof(global::ABI.System.Collections.Specialized.NotifyCollectionChangedEventArgs)) + { + iid = IID.IID_WUX_INotifyCollectionChangedEventArgs; + + return true; + } + + if (type == typeof(global::ABI.System.Collections.Specialized.NotifyCollectionChangedEventHandler)) + { + iid = IID.IID_WUX_NotifyCollectionChangedEventHandler; + + return true; + } + + if (type == typeof(global::ABI.System.ComponentModel.PropertyChangedEventHandler)) + { + iid = IID.IID_WUX_PropertyChangedEventHandler; + + return true; + } + + iid = default; + + return false; + } + + public static string GetSignature( #if NET + // This '[DynamicallyAccessedMembers]' annotation is here just for backwards-compatibility with old projections. Those are + // not trim-safe, but we didn't want to break existing consumers that had code that happened to still work in this case. + // The only case where 'GetSignature' actually uses reflection is in that scenario, but when using updated projections, the + // generated attributes are always used instead, which are trim-safe. Therefore, it's safe to call this method suppressing + // the trim warning for the 'type' parameter if legacy projections are not a concern, or for a "best effort" support there. [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] #endif Type type) - { + { if (type == typeof(object)) { return "cinterface(IInspectable)"; - } - + } + if (type == typeof(string)) { return "string"; - } + } + + if (type == typeof(Type)) + { + return ABI.System.Type.GetGuidSignature(); + } + + if (type.IsGenericType) + { +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2062", Justification = "Fallback path for old projections, not trim-safe by design.")] +#endif + static string[] SelectSignaturesForTypes(Type[] types) + { + string[] signatures = new string[types.Length]; + + for (int i = 0; i < types.Length; i++) + { + signatures[i] = GetSignature(types[i]); + } + + return signatures; + } + + var args = SelectSignaturesForTypes(type.GetGenericArguments()); + var genericHelperType = type.GetGenericTypeDefinition().FindHelperType() ?? type; + return "pinterface({" + genericHelperType.GUID + "};" + string.Join(";", args) + ")"; + } var helperType = type.FindHelperType(); if (helperType != null) @@ -86,33 +187,86 @@ public static string GetSignature( var isFlags = type.IsDefined(typeof(FlagsAttribute)); return "enum(" + type.FullName + ";" + (isFlags ? "u4" : "i4") + ")"; } + if (!type.IsPrimitive) { - var args = type.GetFields(BindingFlags.Instance | BindingFlags.Public).Select(fi => GetSignature(fi.FieldType)); - return "struct(" + type.FullName + ";" + String.Join(";", args) + ")"; + var winrtTypeAttribute = type.GetCustomAttribute(); + if (winrtTypeAttribute != null && !string.IsNullOrEmpty(winrtTypeAttribute.GuidSignature)) + { + return winrtTypeAttribute.GuidSignature; + } + + if (winrtTypeAttribute == null && + (winrtTypeAttribute = type.GetAuthoringMetadataType()?.GetCustomAttribute()) != null && + !string.IsNullOrEmpty(winrtTypeAttribute.GuidSignature)) + { + return winrtTypeAttribute.GuidSignature; + } + +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new InvalidOperationException( + $"Cannot compute signature for type '{type}', as doing so requires a fallback path that is not trim/AOT " + + $"compatible. Using AOT requires all referenced projections to be up to date. Make sure to only reference " + + $"WinRT projections that have been generated by a version of CsWinRT supported for AOT."); + } + + [UnconditionalSuppressMessage("Trimming", "IL2072", Justification = "Fallback path for old projections, not trim-safe by design.")] +#endif + static string[] SelectSignaturesForFields(FieldInfo[] fields) + { + string[] signatures = new string[fields.Length]; + + for (int i = 0; i < fields.Length; i++) + { + signatures[i] = GetSignature(fields[i].FieldType); + } + + return signatures; + } + + var args = SelectSignaturesForFields(type.GetFields(BindingFlags.Instance | BindingFlags.Public)); + return "struct(" + type.FullName + ";" + string.Join(";", args) + ")"; } - throw new InvalidOperationException("unsupported value type"); + + throw new InvalidOperationException("Unsupported value type."); } } } - - type = type.IsInterface ? (type.GetAuthoringMetadataType() ?? type) : type; - if (type.IsGenericType) - { - var args = type.GetGenericArguments().Select(t => GetSignature(t)); - return "pinterface({" + GetGUID(type) + "};" + String.Join(";", args) + ")"; - } - + // For authoring interfaces, we can use the metadata type or the helper type to get the guid. + // For built-in system interfaces that are custom type mapped, we use the helper type to get the guid. + // For others, either the type itself or the helper type has the same guid and can be used. + type = type.IsInterface ? (helperType ?? type) : type; + if (type.IsDelegate()) { return "delegate({" + GetGUID(type) + "})"; } - if (type.IsClass && Projections.TryGetDefaultInterfaceTypeForRuntimeClassType(type, out Type iface)) +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2067", Justification = "'GetSignature' will only actually use reflection when using old projections.")] +#endif + static bool TryGetSignatureFromDefaultInterfaceTypeForRuntimeClassType(Type type, out string signature) + { + if (type.IsClass && Projections.TryGetDefaultInterfaceTypeForRuntimeClassType(type, out Type iface)) + { + signature = "rc(" + type.FullName + ";" + GetSignature(iface) + ")"; + + return true; + } + + signature = null; + + return false; + } + + if (TryGetSignatureFromDefaultInterfaceTypeForRuntimeClassType(type, out string signature)) { - return "rc(" + type.FullName + ";" + GetSignature(iface) + ")"; + return signature; } + return "{" + type.GUID.ToString() + "}"; } @@ -146,30 +300,86 @@ private static Guid encode_guid(Span data) #endif } - private readonly static Guid wrt_pinterface_namespace = new(0xd57af411, 0x737b, 0xc042, 0xab, 0xae, 0x87, 0x8b, 0x1e, 0x16, 0xad, 0xee); + private static readonly Guid wrt_pinterface_namespace = new(0xd57af411, 0x737b, 0xc042, 0xab, 0xae, 0x87, 0x8b, 0x1e, 0x16, 0xad, 0xee); - public static Guid CreateIID(Type type) + public static Guid CreateIID( +#if NET + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] +#endif + Type type) { var sig = GetSignature(type); if (!type.IsGenericType) { return new Guid(sig); } -#if !NET - var data = wrt_pinterface_namespace.ToByteArray().Concat(UTF8Encoding.UTF8.GetBytes(sig)).ToArray(); -#else - var maxBytes = UTF8Encoding.UTF8.GetMaxByteCount(sig.Length); + else + { + return CreateIIDForGenericType(sig); + } + } - var data = new byte[16 /* Number of bytes in a GUID */ + maxBytes]; - Span dataSpan = data; - wrt_pinterface_namespace.TryWriteBytes(dataSpan); - var numBytes = UTF8Encoding.UTF8.GetBytes(sig, dataSpan[16..]); - data = data[..(16 + numBytes)]; + /// + /// Gets the IID of a given type, just like , but without rooting reflection metadata + /// for all public fields of that type. It can be used internally where we know that extra info is not actually needed. + /// + /// The type to get the IID for. + /// The IID for . +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2067", Justification = "This method is only used for types (eg. generics) where fields aren't needed.")] +#endif + internal static Guid CreateIIDUnsafe(Type type) + { + return CreateIID(type); + } + +#if NET8_0_OR_GREATER + [SkipLocalsInit] #endif + internal static Guid CreateIIDForGenericType(string signature) + { +#if !NET + var data = System.Linq.Enumerable.ToArray(System.Linq.Enumerable.Concat(wrt_pinterface_namespace.ToByteArray(), Encoding.UTF8.GetBytes(signature))); + + // CodeQL [SM02196] WinRT uses UUID v5 SHA1 to generate Guids for parameterized types. Not used for authentication and must not change. using (SHA1 sha = new SHA1CryptoServiceProvider()) { return encode_guid(sha.ComputeHash(data)); - } + } +#else +#nullable enable + // Get the maximum UTF8 byte size and allocate a buffer for the encoding. + // If the minimum buffer is small enough, we can stack-allocate it. + // The 512 threshold is the smallest one that will contain generally all + // enum types names. Eg. we'd get signature strings like this: + // 'pinterface({61c17706-2d65-11e0-9ae8-d48564015472};enum(Windows.UI.Xaml.Visibility;u4))'. + // Which would result in a number of bytes ~280, including the GUID size. + int maxUtf8ByteCount = Encoding.UTF8.GetMaxByteCount(signature.Length); + int minimumPooledLength = 16 /* Number of bytes in a GUID */ + maxUtf8ByteCount; + byte[]? utf8BytesFromPool = null; + Span utf8Bytes = minimumPooledLength <= 512 + ? stackalloc byte[512] + : (utf8BytesFromPool = ArrayPool.Shared.Rent(minimumPooledLength)); + + wrt_pinterface_namespace.TryWriteBytes(utf8Bytes); + + int encodedUtf8BytesWritten = Encoding.UTF8.GetBytes(signature, utf8Bytes[16..]); + Span sha1Bytes = stackalloc byte[20]; // 'SHA1.HashSizeInBytes' does not exist on .NET 6, update this when that's dropped + + // Hash the encoded signature (the bytes written are guaranteed to always match the span) + _ = SHA1.HashData(utf8Bytes[..(16 + encodedUtf8BytesWritten)], sha1Bytes); + + Guid iid = encode_guid(sha1Bytes); + + // Before exiting, make sure to return the array from the pool, if we rented one + if (utf8BytesFromPool is not null) + { + ArrayPool.Shared.Return(utf8BytesFromPool); + } + + return iid; +#nullable restore +#endif } } } diff --git a/src/WinRT.Runtime/IInspectable.cs b/src/WinRT.Runtime/IInspectable.cs index 8a80f39a2..498a94dc3 100644 --- a/src/WinRT.Runtime/IInspectable.cs +++ b/src/WinRT.Runtime/IInspectable.cs @@ -2,6 +2,8 @@ // Licensed under the MIT License. using System; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; using WinRT.Interop; @@ -20,7 +22,9 @@ enum TrustLevel } // IInspectable - [ObjectReferenceWrapper(nameof(_obj))] +#if !NET + [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] +#endif [Guid("AF86E2E0-B12D-4c6a-9C5A-D7AA65101E90")] #if EMBED internal @@ -67,8 +71,8 @@ static Vftbl() _GetTrustLevel = (void*)(delegate* unmanaged)&Do_Abi_GetTrustLevel #endif }; - AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false); + AbiToProjectionVftablePtr = Marshal.AllocHGlobal(sizeof(Vftbl)); + *(Vftbl*)AbiToProjectionVftablePtr = AbiToProjectionVftable; } #if NET @@ -118,16 +122,32 @@ private static int Do_Abi_GetTrustLevel(IntPtr pThis, TrustLevel* trustLevel) } public static IInspectable FromAbi(IntPtr thisPtr) => - new IInspectable(ObjectReference.FromAbi(thisPtr)); + new IInspectable(ObjectReference.FromAbi(thisPtr, IID.IID_IInspectable)); - private readonly ObjectReference _obj; + private readonly ObjectReference _obj; public IntPtr ThisPtr => _obj.ThisPtr; - public static implicit operator IInspectable(IObjectReference obj) => obj.As(); +#if !NET + public static implicit operator IInspectable(IObjectReference obj) => obj.As(IID.IID_IInspectable); public static implicit operator IInspectable(ObjectReference obj) => new IInspectable(obj); +#endif + +#if NET + [RequiresUnreferencedCode(AttributeMessages.GenericRequiresUnreferencedCodeMessage)] + [Obsolete(AttributeMessages.GenericDeprecatedMessage)] + [EditorBrowsable(EditorBrowsableState.Never)] +#endif public ObjectReference As() => _obj.As(); public IObjectReference ObjRef { get => _obj; } - public IInspectable(IObjectReference obj) : this(obj.As()) { } - public IInspectable(ObjectReference obj) + public IInspectable(IObjectReference obj) : this(obj.As(IID.IID_IInspectable)) { } + +#if NET + [Obsolete(AttributeMessages.GenericDeprecatedMessage)] + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + public IInspectable(ObjectReference obj) : this(obj.As(IID.IID_IInspectable)) { } + + // Note: callers have to ensure to perform QI for 'IInspectable' when using this constructor + internal IInspectable(ObjectReference obj) { _obj = obj; } @@ -137,7 +157,9 @@ public unsafe string GetRuntimeClassName(bool noThrow = false) IntPtr __retval = default; try { - var hr = _obj.Vftbl.GetRuntimeClassName(ThisPtr, &__retval); + IntPtr thisPtr = ThisPtr; + var hr = ((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[4])(thisPtr, &__retval); + GC.KeepAlive(_obj); if (hr != 0) { if (noThrow) diff --git a/src/WinRT.Runtime/IWinRTObject.net5.cs b/src/WinRT.Runtime/IWinRTObject.net5.cs index 3507d04f5..8aa763b34 100644 --- a/src/WinRT.Runtime/IWinRTObject.net5.cs +++ b/src/WinRT.Runtime/IWinRTObject.net5.cs @@ -1,194 +1,348 @@ // Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Runtime.InteropServices; -using WinRT.Interop; - -namespace WinRT +// Licensed under the MIT License. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +#if NET8_0_OR_GREATER +using System.Runtime.InteropServices.Marshalling; +#endif +using WinRT.Interop; + +namespace WinRT { -#if EMBED - internal -#else - public -#endif - interface IWinRTObject : IDynamicInterfaceCastable - { - bool IDynamicInterfaceCastable.IsInterfaceImplemented(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented) - { - return IsInterfaceImplementedFallback(interfaceType, throwIfNotImplemented); - } - - bool IsInterfaceImplementedFallback(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented) - { - if (QueryInterfaceCache.ContainsKey(interfaceType)) - { - return true; - } - Type type = Type.GetTypeFromHandle(interfaceType); - - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IReadOnlyCollection<>)) - { - Type itemType = type.GetGenericArguments()[0]; - if (itemType.IsGenericType && itemType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) - { - Type iReadOnlyDictionary = typeof(IReadOnlyDictionary<,>).MakeGenericType(itemType.GetGenericArguments()); - if (IsInterfaceImplemented(iReadOnlyDictionary.TypeHandle, false)) - { - if (QueryInterfaceCache.TryGetValue(iReadOnlyDictionary.TypeHandle, out var typedObjRef) && !QueryInterfaceCache.TryAdd(interfaceType, typedObjRef)) - { - typedObjRef.Dispose(); - } - return true; - } - } - Type iReadOnlyList = typeof(IReadOnlyList<>).MakeGenericType(new[] { itemType }); - if (IsInterfaceImplemented(iReadOnlyList.TypeHandle, throwIfNotImplemented)) - { - if (QueryInterfaceCache.TryGetValue(iReadOnlyList.TypeHandle, out var typedObjRef) && !QueryInterfaceCache.TryAdd(interfaceType, typedObjRef)) - { - typedObjRef.Dispose(); - } - return true; - } - - return false; - } - else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.ICollection<>)) - { - Type itemType = type.GetGenericArguments()[0]; - if (itemType.IsGenericType && itemType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) - { - Type iDictionary = typeof(IDictionary<,>).MakeGenericType(itemType.GetGenericArguments()); - if (IsInterfaceImplemented(iDictionary.TypeHandle, false)) - { - if (QueryInterfaceCache.TryGetValue(iDictionary.TypeHandle, out var typedObjRef) && !QueryInterfaceCache.TryAdd(interfaceType, typedObjRef)) - { - typedObjRef.Dispose(); - } - return true; - } - } - Type iList = typeof(IList<>).MakeGenericType(new[] { itemType }); - if (IsInterfaceImplemented(iList.TypeHandle, throwIfNotImplemented)) - { - if (QueryInterfaceCache.TryGetValue(iList.TypeHandle, out var typedObjRef) && !QueryInterfaceCache.TryAdd(interfaceType, typedObjRef)) - { - typedObjRef.Dispose(); - } - return true; - } - - return false; - } - else if (type == typeof(System.Collections.IEnumerable)) - { - Type iEnum = typeof(System.Collections.Generic.IEnumerable); - if (IsInterfaceImplemented(iEnum.TypeHandle, false)) - { - if (QueryInterfaceCache.TryGetValue(iEnum.TypeHandle, out var typedObjRef) && !QueryInterfaceCache.TryAdd(interfaceType, typedObjRef)) - { - typedObjRef.Dispose(); - } - return true; - } - } - - Type helperType = type.FindHelperType(); - if (helperType is null || !helperType.IsInterface) - { - return false; - } - int hr = NativeObject.TryAs(GuidGenerator.GetIID(helperType), out var objRef); - if (hr < 0) - { - if (throwIfNotImplemented) - { - ExceptionHelpers.ThrowExceptionForHR(hr); - } - return false; - } - - if (typeof(System.Collections.IEnumerable).IsAssignableFrom(type)) - { - RuntimeTypeHandle projectIEnum = typeof(System.Collections.IEnumerable).TypeHandle; - AdditionalTypeData.GetOrAdd(projectIEnum, (_) => new ABI.System.Collections.IEnumerable.AdaptiveFromAbiHelper(type, this)); - } - - var vftblType = helperType.FindVftblType(); - using (objRef) - { - if (vftblType is null) - { - var qiObjRef = objRef.As(GuidGenerator.GetIID(helperType)); - if (!QueryInterfaceCache.TryAdd(interfaceType, qiObjRef)) - { - qiObjRef.Dispose(); - } - return true; - } - IObjectReference typedObjRef = (IObjectReference)typeof(IObjectReference).GetMethod("As", Type.EmptyTypes).MakeGenericMethod(vftblType).Invoke(objRef, null); - if (!QueryInterfaceCache.TryAdd(interfaceType, typedObjRef)) - { - typedObjRef.Dispose(); - } - return true; - } - } - - RuntimeTypeHandle IDynamicInterfaceCastable.GetInterfaceImplementation(RuntimeTypeHandle interfaceType) - { - var type = Type.GetTypeFromHandle(interfaceType); - var helperType = type.GetHelperType(); - if (helperType.IsInterface) - return helperType.TypeHandle; - return default; - } - - IObjectReference NativeObject { get; } - bool HasUnwrappableNativeObject { get; } - - protected ConcurrentDictionary QueryInterfaceCache { get; } - - IObjectReference GetObjectReferenceForType(RuntimeTypeHandle type) - { - return GetObjectReferenceForTypeFallback(type); - } - - IObjectReference GetObjectReferenceForTypeFallback(RuntimeTypeHandle type) - { - if (IsInterfaceImplemented(type, true)) - { - return QueryInterfaceCache[type]; - } - throw new Exception("Interface " + Type.GetTypeFromHandle(type) +" is not implemented."); - } - - ConcurrentDictionary AdditionalTypeData { get; } - - object GetOrCreateTypeHelperData(RuntimeTypeHandle type, Func helperDataFactory) - { - return AdditionalTypeData.GetOrAdd(type, (type) => helperDataFactory()); - } - - internal void Resurrect() +#if EMBED + internal +#else + public +#endif + interface IWinRTObject : IDynamicInterfaceCastable +#if NET8_0_OR_GREATER + , IUnmanagedVirtualMethodTableProvider +#endif + { + bool IDynamicInterfaceCastable.IsInterfaceImplemented(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented) { - if (NativeObject.Resurrect()) - { - foreach (var cached in QueryInterfaceCache) - { - cached.Value.Resurrect(); - } - - // Delegates store their agile reference as an additional type data. - // These should be recreated when instances are resurrect. - if (AdditionalTypeData.TryGetValue(typeof(AgileReference).TypeHandle, out var agileObj)) - { - AdditionalTypeData.TryUpdate(typeof(AgileReference).TypeHandle, new AgileReference(NativeObject), agileObj); - } - } - } - } + if (!FeatureSwitches.EnableIDynamicInterfaceCastableSupport) + { + return false; + } + + return IsInterfaceImplementedFallback(interfaceType, throwIfNotImplemented); + } + + internal sealed bool IsInterfaceImplementedFallback(RuntimeTypeHandle interfaceType, bool throwIfNotImplemented) + { + if (!FeatureSwitches.EnableIDynamicInterfaceCastableSupport) + { + return throwIfNotImplemented ? + throw new NotSupportedException($"Support for 'IDynamicInterfaceCastable' is disabled (make sure that the 'CsWinRTEnableIDynamicInterfaceCastableSupport' property is not set to 'false').") : false; + } + + if (QueryInterfaceCache.ContainsKey(interfaceType)) + { + return true; + } + +#if NET8_0_OR_GREATER + bool vtableLookup = LookupGeneratedVTableInfo(interfaceType, out _, out int qiResult); + if (vtableLookup) + { + return true; + } + else if (qiResult < 0 && throwIfNotImplemented) + { + // A qiResult of less than zero means the call to QueryInterface has failed. + ExceptionHelpers.ThrowExceptionForHR(qiResult); + } +#endif + + Type type = Type.GetTypeFromHandle(interfaceType); + + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.IReadOnlyCollection<>)) + { +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return throwIfNotImplemented ? throw new NotSupportedException($"'IDynamicInterfaceCastable' is not supported for generic type '{type}'.") : false; + } +#endif + +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + Type itemType = type.GetGenericArguments()[0]; + if (itemType.IsGenericType && itemType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) + { + Type iReadOnlyDictionary = typeof(IReadOnlyDictionary<,>).MakeGenericType(itemType.GetGenericArguments()); + if (IsInterfaceImplemented(iReadOnlyDictionary.TypeHandle, false)) + { + if (QueryInterfaceCache.TryGetValue(iReadOnlyDictionary.TypeHandle, out var typedObjRef) && !QueryInterfaceCache.TryAdd(interfaceType, typedObjRef)) + { + typedObjRef.Dispose(); + } + return true; + } + } + Type iReadOnlyList = typeof(IReadOnlyList<>).MakeGenericType(new[] { itemType }); +#pragma warning restore IL3050 + if (IsInterfaceImplemented(iReadOnlyList.TypeHandle, throwIfNotImplemented)) + { + if (QueryInterfaceCache.TryGetValue(iReadOnlyList.TypeHandle, out var typedObjRef) && !QueryInterfaceCache.TryAdd(interfaceType, typedObjRef)) + { + typedObjRef.Dispose(); + } + return true; + } + + return false; + } + else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.ICollection<>)) + { +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return throwIfNotImplemented ? throw new NotSupportedException($"'IDynamicInterfaceCastable' is not supported for generic type '{type}'.") : false; + } +#endif + +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + Type itemType = type.GetGenericArguments()[0]; + if (itemType.IsGenericType && itemType.GetGenericTypeDefinition() == typeof(KeyValuePair<,>)) + { + Type iDictionary = typeof(IDictionary<,>).MakeGenericType(itemType.GetGenericArguments()); + if (IsInterfaceImplemented(iDictionary.TypeHandle, false)) + { + if (QueryInterfaceCache.TryGetValue(iDictionary.TypeHandle, out var typedObjRef) && !QueryInterfaceCache.TryAdd(interfaceType, typedObjRef)) + { + typedObjRef.Dispose(); + } + return true; + } + } + Type iList = typeof(IList<>).MakeGenericType(new[] { itemType }); +#pragma warning restore IL3050 + if (IsInterfaceImplemented(iList.TypeHandle, throwIfNotImplemented)) + { + if (QueryInterfaceCache.TryGetValue(iList.TypeHandle, out var typedObjRef) && !QueryInterfaceCache.TryAdd(interfaceType, typedObjRef)) + { + typedObjRef.Dispose(); + } + return true; + } + + return false; + } + else if (type == typeof(System.Collections.IEnumerable)) + { + Type iEnum = typeof(System.Collections.Generic.IEnumerable); + if (IsInterfaceImplemented(iEnum.TypeHandle, false)) + { + if (QueryInterfaceCache.TryGetValue(iEnum.TypeHandle, out var typedObjRef) && !QueryInterfaceCache.TryAdd(interfaceType, typedObjRef)) + { + typedObjRef.Dispose(); + } + return true; + } + } + else if (type == typeof(System.Collections.ICollection)) + { + Type iList = typeof(global::System.Collections.IList); + if (IsInterfaceImplemented(iList.TypeHandle, false)) + { + if (QueryInterfaceCache.TryGetValue(iList.TypeHandle, out var typedObjRef) && !QueryInterfaceCache.TryAdd(interfaceType, typedObjRef)) + { + typedObjRef.Dispose(); + } + return true; + } + } + + // Make sure we are going to do a QI on a WinRT interface, + // otherwise we can get a helper type for a non WinRT type. + if (!Projections.IsTypeWindowsRuntimeType(type)) + { + return false; + } + + Type helperType = type.FindHelperType(throwIfNotImplemented); + if (helperType is null || !helperType.IsInterface) + { + return false; + } + int hr = NativeObject.TryAs(GuidGenerator.GetIID(helperType), out var objRef); + if (hr < 0) + { + if (throwIfNotImplemented) + { + ExceptionHelpers.ThrowExceptionForHR(hr); + } + return false; + } + + if (typeof(System.Collections.IEnumerable).IsAssignableFrom(type)) + { + RuntimeTypeHandle projectIEnum = typeof(System.Collections.IEnumerable).TypeHandle; + AdditionalTypeData.GetOrAdd(projectIEnum, static (_, info) => new ABI.System.Collections.IEnumerable.AdaptiveFromAbiHelper(info.Type, info.This), new AdditionalTypeDataParams { Type = type, This = this }); + } + + bool hasMovedObjRefOwnership = false; + + try + { + var vftblType = helperType.FindVftblType(); + + // If there is no nested vftbl type, we want to add the object reference with the IID from the helper type + // to the cache. Rather than doing 'QueryInterface' again with the same IID, on the object reference we + // already have, we can just store that same instance in the cache, and suppress the 'objRef.Dispose()' + // call for it. This avoids that extra call, plus the overhead of allocating a new object reference. + // For all other cases, we dispose the object reference as usual at the end of this scope. + if (vftblType is null) + { + hasMovedObjRefOwnership = QueryInterfaceCache.TryAdd(interfaceType, objRef); + + return true; + } + +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return throwIfNotImplemented ? throw new NotSupportedException($"Cannot construct an object reference for vtable type '{vftblType}'.") : false; + } +#endif + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "If the 'Vftbl' type is kept, we can assume all its metadata will also have been rooted.")] + [MethodImpl(MethodImplOptions.NoInlining)] + static IObjectReference GetObjectReferenceViaVftbl(IObjectReference objRef, Type vftblType) + { + return (IObjectReference)typeof(IObjectReference).GetMethod("As", Type.EmptyTypes).MakeGenericMethod(vftblType).Invoke(objRef, null); + } + +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + IObjectReference typedObjRef = GetObjectReferenceViaVftbl(objRef, vftblType); +#pragma warning restore IL3050 + + if (!QueryInterfaceCache.TryAdd(interfaceType, typedObjRef)) + { + typedObjRef.Dispose(); + } + + return true; + } + finally + { + if (!hasMovedObjRefOwnership) + { + objRef.Dispose(); + } + } + } + +#if NET8_0_OR_GREATER + internal sealed unsafe bool LookupGeneratedVTableInfo(RuntimeTypeHandle interfaceType, [NotNullWhen(true)] out IIUnknownCacheStrategy.TableInfo? result, out int qiResult) + { + result = null; + qiResult = 0; + if (AdditionalTypeData.TryGetValue(interfaceType, out object value)) + { + if (value is IIUnknownCacheStrategy.TableInfo tableInfo) + { + result = tableInfo; + return true; + } + return false; + } + + if (StrategyBasedComWrappers.DefaultIUnknownInterfaceDetailsStrategy.GetIUnknownDerivedDetails(interfaceType) is IIUnknownDerivedDetails details) + { + qiResult = NativeObject.TryAs(details.Iid, out ObjectReference objRef); + if (qiResult < 0) + return false; + var obj = (void***)objRef.ThisPtr; + result = new IIUnknownCacheStrategy.TableInfo() + { + ThisPtr = obj, + Table = *obj, + ManagedType = details.Implementation.TypeHandle + }; + + if (!AdditionalTypeData.TryAdd(interfaceType, result)) + { + bool found = AdditionalTypeData.TryGetValue(interfaceType, out object newInfo); + System.Diagnostics.Debug.Assert(found); + result = (IIUnknownCacheStrategy.TableInfo)newInfo; + objRef.Dispose(); + } + else + { + QueryInterfaceCache.TryAdd(interfaceType, objRef); + } + + return true; + } + return false; + } +#endif + + RuntimeTypeHandle IDynamicInterfaceCastable.GetInterfaceImplementation(RuntimeTypeHandle interfaceType) + { +#if NET8_0_OR_GREATER + if (AdditionalTypeData.TryGetValue(interfaceType, out object value) && value is IIUnknownCacheStrategy.TableInfo tableInfo) + { + return tableInfo.ManagedType; + } +#endif + var type = Type.GetTypeFromHandle(interfaceType); + var helperType = type.GetHelperType(); + if (helperType.IsInterface) + return helperType.TypeHandle; + return default; + } + +#if NET8_0_OR_GREATER + unsafe VirtualMethodTableInfo IUnmanagedVirtualMethodTableProvider.GetVirtualMethodTableInfoForKey(Type type) + { + if (!LookupGeneratedVTableInfo(type.TypeHandle, out IIUnknownCacheStrategy.TableInfo? result, out int qiHResult)) + { + Marshal.ThrowExceptionForHR(qiHResult); + } + + return new(result.Value.ThisPtr, result.Value.Table); + } +#endif + + IObjectReference NativeObject { get; } + bool HasUnwrappableNativeObject { get; } + + protected ConcurrentDictionary QueryInterfaceCache { get; } + + IObjectReference GetObjectReferenceForType(RuntimeTypeHandle type) + { + return GetObjectReferenceForTypeFallback(type); + } + + internal sealed IObjectReference GetObjectReferenceForTypeFallback(RuntimeTypeHandle type) + { + if (IsInterfaceImplemented(type, true)) + { + return QueryInterfaceCache[type]; + } + + throw new Exception("Interface '" + Type.GetTypeFromHandle(type) + "' is not implemented."); + } + + ConcurrentDictionary AdditionalTypeData { get; } + } + + // Custom type to avoid rooting value tuple metadata (saves a few KBs of size) + internal struct AdditionalTypeDataParams + { + public Type Type; + public IWinRTObject This; + } } \ No newline at end of file diff --git a/src/WinRT.Runtime/Interop/EventHandlerEventSource.cs b/src/WinRT.Runtime/Interop/EventHandlerEventSource.cs new file mode 100644 index 000000000..d36a220c1 --- /dev/null +++ b/src/WinRT.Runtime/Interop/EventHandlerEventSource.cs @@ -0,0 +1,65 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using WinRT; +using WinRT.Interop; + +namespace ABI.WinRT.Interop +{ + using EventRegistrationToken = global::WinRT.EventRegistrationToken; + + /// + /// An implementation for . + /// + /// This type is only meant to be used by generated projections. + [EditorBrowsable(EditorBrowsableState.Never)] +#if EMBED + internal +#else + public +#endif + sealed unsafe class EventHandlerEventSource : EventSource + { + public EventHandlerEventSource( + IObjectReference objectReference, +#if NET + delegate* unmanaged[Stdcall] addHandler, +#else + delegate* unmanaged[Stdcall] addHandler, +#endif + delegate* unmanaged[Stdcall] removeHandler) + : base(objectReference, addHandler, removeHandler) + { + } + + public EventHandlerEventSource( + IObjectReference objectReference, + int vtableIndexForAddHandler) + : base(objectReference, vtableIndexForAddHandler) + { + } + + /// + protected override ObjectReferenceValue CreateMarshaler(EventHandler del) => + ABI.System.EventHandler.CreateMarshaler2(del); + + /// + protected override EventSourceState CreateEventSourceState() => + new EventState(ObjectReference.ThisPtr, Index); + + private sealed class EventState : EventSourceState + { + public EventState(IntPtr obj, int index) + : base(obj, index) + { + } + + protected override EventHandler GetEventInvoke() + { + return (obj, e) => targetDelegate?.Invoke(obj, e); + } + } + } +} diff --git a/src/WinRT.Runtime/Interop/EventHandlerEventSource{T}.cs b/src/WinRT.Runtime/Interop/EventHandlerEventSource{T}.cs new file mode 100644 index 000000000..e203d4fe9 --- /dev/null +++ b/src/WinRT.Runtime/Interop/EventHandlerEventSource{T}.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using WinRT; +using WinRT.Interop; + +namespace ABI.WinRT.Interop +{ + using EventRegistrationToken = global::WinRT.EventRegistrationToken; + + /// + /// An implementation for . + /// + /// The type of the event data generated by the event. + /// This type is only meant to be used by generated projections. + [EditorBrowsable(EditorBrowsableState.Never)] +#if EMBED + internal +#else + public +#endif + sealed unsafe class EventHandlerEventSource : EventSource> + { + public EventHandlerEventSource( + IObjectReference objectReference, +#if NET + delegate* unmanaged[Stdcall] addHandler, +#else + delegate* unmanaged[Stdcall] addHandler, +#endif + delegate* unmanaged[Stdcall] removeHandler, + int index) : base(objectReference, addHandler, removeHandler, index) + { + } + + public EventHandlerEventSource(IObjectReference objectReference, int vtableIndexForAddHandler) + : base(objectReference, vtableIndexForAddHandler) + { + } + + /// + protected override ObjectReferenceValue CreateMarshaler(EventHandler del) => + ABI.System.EventHandler.CreateMarshaler2(del); + + /// + protected override EventSourceState> CreateEventSourceState() => + new EventState(ObjectReference.ThisPtr, Index); + + private sealed class EventState : EventSourceState> + { + public EventState(IntPtr obj, int index) + : base(obj, index) + { + } + + protected override EventHandler GetEventInvoke() + { + return (obj, e) => targetDelegate?.Invoke(obj, e); + } + } + } +} diff --git a/src/WinRT.Runtime/Interop/EventSourceCache.cs b/src/WinRT.Runtime/Interop/EventSourceCache.cs new file mode 100644 index 000000000..87f61e817 --- /dev/null +++ b/src/WinRT.Runtime/Interop/EventSourceCache.cs @@ -0,0 +1,182 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Threading; +using WinRT; +using WinRT.Interop; + +namespace ABI.WinRT.Interop +{ + internal sealed class EventSourceCache + { + private static readonly ReaderWriterLockSlim cachesLock = new(); + private static readonly ConcurrentDictionary caches = new(); + + private readonly ConcurrentDictionary> states = new(); + private global::WinRT.Interop.IWeakReference target; + + private EventSourceCache(global::WinRT.Interop.IWeakReference target, int index, global::System.WeakReference state) + { + this.target = target; + + SetState(index, state); + } + + private EventSourceCache Update(global::WinRT.Interop.IWeakReference target, int index, global::System.WeakReference state) + { + // If target no longer exists, destroy cache + lock (this) + { + using var resolved = this.target.Resolve(IID.IID_IUnknown); + if (resolved == null) + { + this.target = target; + states.Clear(); + } + } + SetState(index, state); + return this; + } + + private global::System.WeakReference GetState(int index) + { + // If target no longer exists, destroy cache + lock (this) + { + using var resolved = this.target.Resolve(IID.IID_IUnknown); + if (resolved == null) + { + return null; + } + } + + if (states.TryGetValue(index, out var weakState)) + { + return weakState; + } + return null; + } + + private void SetState(int index, global::System.WeakReference state) + { + states[index] = state; + } + + public static void Create(IObjectReference obj, int index, global::System.WeakReference state) + { + // If event source implements weak reference support, track event registrations so that + // unsubscribes will work across garbage collections. Note that most static/factory classes + // do not implement IWeakReferenceSource, so static codegen caching approach is also used. + global::WinRT.Interop.IWeakReference target = null; +#if !NET + try + { + var weakRefSource = (global::WinRT.Interop.IWeakReferenceSource)typeof(global::WinRT.Interop.IWeakReferenceSource).GetHelperType().GetConstructor(new[] { typeof(IObjectReference) }).Invoke(new object[] { obj }); + if (weakRefSource == null) + { + return; + } + target = weakRefSource.GetWeakReference(); + } + catch (Exception) + { + return; + } +#else + int hr = obj.TryAs(IID.IID_IWeakReferenceSource, out var weakRefSource); + if (hr != 0) + { + return; + } + + target = ABI.WinRT.Interop.IWeakReferenceSourceMethods.GetWeakReference(weakRefSource); +#endif + + cachesLock.EnterReadLock(); + try + { +#if NET + caches.AddOrUpdate( + key: obj.ThisPtr, + addValueFactory: static (IntPtr ThisPtr, CachesFactoryArgs args) => new EventSourceCache(args.Target, args.Index, args.State), + updateValueFactory: static (IntPtr ThisPtr, EventSourceCache cache, CachesFactoryArgs args) => cache.Update(args.Target, args.Index, args.State), + factoryArgument: new CachesFactoryArgs(target, index, state)); +#else + caches.AddOrUpdate(obj.ThisPtr, + (IntPtr ThisPtr) => new EventSourceCache(target, index, state), + (IntPtr ThisPtr, EventSourceCache cache) => cache.Update(target, index, state)); +#endif + } + finally + { + cachesLock.ExitReadLock(); + } + } + + public static global::System.WeakReference GetState(IObjectReference obj, int index) + { + if (caches.TryGetValue(obj.ThisPtr, out var cache)) + { + return cache.GetState(index); + } + + return null; + } + + public static void Remove(IntPtr thisPtr, int index, global::System.WeakReference state) + { + if (caches.TryGetValue(thisPtr, out var cache)) + { +#if !NET + // https://devblogs.microsoft.com/pfxteam/little-known-gems-atomic-conditional-removals-from-concurrentdictionary/ + ((ICollection>>)cache.states).Remove( + new KeyValuePair>(index, state)); +#else + // If we failed to remove the entry, we can stop here without checking the actual state. Even if there + // was a value when we were called, we might've raced against another thread, which removed the item + // first. That is still fine: this thread can stop here, and the one that won the race will do the + // check below and cleanup the event cache instance in case that was the last remaining cache entry. + if (!cache.states.TryRemove(new KeyValuePair>(index, state))) + { + return; + } +#endif + // using double-checked lock idiom + if (cache.states.IsEmpty) + { + cachesLock.EnterWriteLock(); + try + { + if (cache.states.IsEmpty) + { + caches.TryRemove(thisPtr, out var _); + } + } + finally + { + cachesLock.ExitWriteLock(); + } + } + } + } + +#if NET + // We're intentionally using a separate type and not a value tuple, because creating generic + // type instantiations with this type when delegates are involved results in additional + // metadata being preserved after trimming. This can save a few KBs in binary size on Native AOT. + // See: https://github.com/dotnet/runtime/pull/111204#issuecomment-2599397292. + private readonly struct CachesFactoryArgs( + global::WinRT.Interop.IWeakReference target, + int index, + global::System.WeakReference state) + { + public readonly global::WinRT.Interop.IWeakReference Target = target; + public readonly int Index = index; + public readonly global::System.WeakReference State = state; + } +#endif + } +} diff --git a/src/WinRT.Runtime/Interop/EventSourceState{TDelegate}.cs b/src/WinRT.Runtime/Interop/EventSourceState{TDelegate}.cs new file mode 100644 index 000000000..b9ba55da4 --- /dev/null +++ b/src/WinRT.Runtime/Interop/EventSourceState{TDelegate}.cs @@ -0,0 +1,167 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using WinRT.Interop; + +#nullable enable + +namespace ABI.WinRT.Interop +{ + using EventRegistrationToken = global::WinRT.EventRegistrationToken; + + /// + /// A type representing all associated state for a given instance. + /// + /// The type of delegate being managed from the associated event. + /// This type is only meant to be used by generated projections. + [EditorBrowsable(EditorBrowsableState.Never)] +#if EMBED + internal +#else + public +#endif + abstract class EventSourceState : IDisposable + where TDelegate : class, MulticastDelegate + { + // Registration state and delegate cached separately to survive EventSource garbage collection + // and to prevent the generated event delegate from impacting the lifetime of the event source. + + private bool disposedValue; + private readonly IntPtr obj; + private readonly int index; + private readonly global::System.WeakReference cacheEntry; + private IntPtr eventInvokePtr; + private IntPtr referenceTrackerTargetPtr; + internal EventRegistrationToken token; + internal TDelegate? targetDelegate; + internal TDelegate eventInvoke; + + /// + /// Creates a new instance with the specified parameters. + /// + /// The pointer to the target object owning the associated event. + /// The index of the event the state is associated to. + protected EventSourceState(IntPtr thisPtr, int index) + { + this.obj = thisPtr; + this.index = index; + eventInvoke = GetEventInvoke(); + cacheEntry = new global::System.WeakReference(this); + } + + /// + /// Finalizes the current instance. + /// + ~EventSourceState() + { + // The lifetime of this object is managed by the delegate / eventInvoke + // through its target reference to it. Once the delegate no longer has + // any references, this object will also no longer have any references. + Dispose(false); + } + + /// + /// Gets the current value with the active subscriptions for the target event. + /// + protected TDelegate? TargetDelegate => targetDelegate; + + /// + /// Gets a instance responsible for actually raising the + /// event, if any targets are currently available, or for doing nothing if the current + /// target handler is currently . + /// + /// The resulting instance to raise the event. + /// + /// The returned delegate must capture to ensure the associated state is kept alive. + /// + protected abstract TDelegate GetEventInvoke(); + + /// + /// Disposes the current instance. + /// + /// Indicates whether the method was called by . + protected virtual void Dispose(bool disposing) + { + // Uses the dispose pattern to ensure we only remove + // from the cache once: either via unsubscribe or via + // the finalizer. + if (!disposedValue) + { + EventSourceCache.Remove(obj, index, cacheEntry); + disposedValue = true; + } + } + + /// + public void Dispose() + { + GC.SuppressFinalize(this); + + Dispose(true); + } + + // Allows to retrieve a singleton like weak reference to use + // with the cache to allow for proper removal with comparision. + internal global::System.WeakReference GetWeakReferenceForCache() + { + return cacheEntry; + } + + internal void InitializeReferenceTracking(IntPtr ptr) + { + eventInvokePtr = ptr; + int hr = Marshal.QueryInterface(ptr, ref Unsafe.AsRef(in IID.IID_IReferenceTrackerTarget), out referenceTrackerTargetPtr); + if (hr != 0) + { + referenceTrackerTargetPtr = default; + } + else + { + // We don't want to keep ourselves alive and as long as this object + // is alive, the CCW still exists. + Marshal.Release(referenceTrackerTargetPtr); + } + } + + internal unsafe bool HasComReferences() + { + if (eventInvokePtr != default) + { + IUnknownVftbl vftblIUnknown = **(IUnknownVftbl**)eventInvokePtr; + vftblIUnknown.AddRef(eventInvokePtr); + uint comRefCount = vftblIUnknown.Release(eventInvokePtr); + if (comRefCount != 0) + { + return true; + } + } + + if (referenceTrackerTargetPtr != default) + { + void** vftblReferenceTracker = *(void***)referenceTrackerTargetPtr; + + // AddRefFromReferenceTracker + _ = ((delegate* unmanaged[Stdcall])(vftblReferenceTracker[3]))(referenceTrackerTargetPtr); + + // ReleaseFromReferenceTracker + uint refTrackerCount = ((delegate* unmanaged[Stdcall])(vftblReferenceTracker[4]))(referenceTrackerTargetPtr); + + if (refTrackerCount != 0) + { + // Note we can't tell if the reference tracker ref is pegged or not, so this is best effort where if there + // are any reference tracker references, we assume the event has references. + return true; + } + } + + return false; + } + } +} + +// Restore in case this file is merged with others. +#nullable restore \ No newline at end of file diff --git a/src/WinRT.Runtime/Interop/EventSource{TDelegate}.cs b/src/WinRT.Runtime/Interop/EventSource{TDelegate}.cs new file mode 100644 index 000000000..af5897c0f --- /dev/null +++ b/src/WinRT.Runtime/Interop/EventSource{TDelegate}.cs @@ -0,0 +1,265 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using WinRT; + +#nullable enable + +namespace ABI.WinRT.Interop +{ + using EventRegistrationToken = global::WinRT.EventRegistrationToken; + + /// + /// A managed wrapper for an event to expose to a native WinRT consumer. + /// + /// The type of delegate being managed. + /// This type is only meant to be used by generated projections. + [EditorBrowsable(EditorBrowsableState.Never)] + #if EMBED + internal +#else + public +#endif + abstract unsafe class EventSource + where TDelegate : class, MulticastDelegate + { + private readonly IObjectReference _objectReference; + private readonly int _index; + private readonly long _vtableIndexForAddHandler; +#if NET + private readonly delegate* unmanaged[Stdcall] _addHandler; +#else + private readonly delegate* unmanaged[Stdcall] _addHandler; +#endif + private readonly delegate* unmanaged[Stdcall] _removeHandler; + private global::System.WeakReference? _state; + + // The add / remove handlers given to us can be for an object which is not agile meaning we may have + // a proxy which we need to call through. Due to this, we store the offset of the add handler + // we are given rather than directly caching it. Then we use that offset, to determine the add and + // remove handler to call based on the pointer from the current context. +#if NET + private delegate* unmanaged[Stdcall] AddHandler +#else + private delegate* unmanaged[Stdcall] AddHandler +#endif + { + get + { + if (_addHandler is not null) + { + return _addHandler; + } + + var thisPtr = _objectReference.ThisPtr; +#if NET + return (*(delegate* unmanaged[Stdcall]**)thisPtr)[_vtableIndexForAddHandler]; +#else + return (*(delegate* unmanaged[Stdcall]**)thisPtr)[_vtableIndexForAddHandler]; +#endif + } + } + + private delegate* unmanaged[Stdcall] RemoveHandler + { + get + { + if (_removeHandler is not null) + { + return _removeHandler; + } + + var thisPtr = _objectReference.ThisPtr; + // Add 1 to the offset to get remove handler from add handler offset. + return (*(delegate* unmanaged[Stdcall]**)thisPtr)[_vtableIndexForAddHandler + 1]; + } + } + + /// + /// Creates a new instance with the specified parameters. + /// + /// The instance holding the event. + /// The native function pointer for the AddHandler method on the target object. + /// The native function pointer for the RemoveHandler method on the target object. + /// The index of the event being managed. + protected EventSource( + IObjectReference objectReference, +#if NET + delegate* unmanaged[Stdcall] addHandler, +#else + delegate* unmanaged[Stdcall] addHandler, +#endif + delegate* unmanaged[Stdcall] removeHandler, + int index = 0) + { + _objectReference = objectReference; + _index = index; + _state = EventSourceCache.GetState(objectReference, index); + + // If this isn't a free threaded object, we can't cache the handlers due to we + // might be accessing it from a different context which would have its own add handler address + // for the proxy. So caching it would end up calling the wrong vtable. + // We instead use it to calculate the vtable offset for the add handler to use later. + if (objectReference is IObjectReferenceWithContext) + { + int vtableIndexForAddHandler = 0; + while ((*(void***)objectReference.ThisPtr)[vtableIndexForAddHandler] != addHandler) + { + vtableIndexForAddHandler++; + } + _vtableIndexForAddHandler = vtableIndexForAddHandler; + } + else + { + _addHandler = addHandler; + _removeHandler = removeHandler; + } + } + + /// + /// Creates a new instance with the specified parameters. + /// + /// The instance holding the event. + /// The vtable index for the add handler of the event being managed. + protected EventSource( + IObjectReference objectReference, + int vtableIndexForAddHandler) + { + _objectReference = objectReference; + _index = vtableIndexForAddHandler; + _state = EventSourceCache.GetState(objectReference, vtableIndexForAddHandler); + _vtableIndexForAddHandler = vtableIndexForAddHandler; + } + + /// + /// Gets the instance holding the event. + /// + protected IObjectReference ObjectReference => _objectReference; + + /// + /// Gets the index of the event being managed. + /// + protected int Index => _index; + + /// + /// Gets an instance to marshal a instance. + /// + /// The input handler to create the marshaller for. + /// An instance to marshal a instance. + protected abstract ObjectReferenceValue CreateMarshaler(TDelegate handler); + + /// + /// Creates the instance for the current event source. + /// + /// The instance for the current event source. + protected abstract EventSourceState CreateEventSourceState(); + + /// + /// Subscribes a given handler to the target event. + /// + /// The handler to subscribe to the target event. + public void Subscribe(TDelegate handler) + { + lock (this) + { + EventSourceState? state = null; + bool registerHandler = + !TryGetStateUnsafe(out state) || + // We have a wrapper delegate, but no longer has any references from any event source. + !state!.HasComReferences(); + if (registerHandler) + { + state = CreateEventSourceState(); + _state = state.GetWeakReferenceForCache(); + EventSourceCache.Create(_objectReference, _index, _state); + } + + state!.targetDelegate = (TDelegate)Delegate.Combine(state.targetDelegate, handler); + if (registerHandler) + { + var eventInvoke = state.eventInvoke; + var marshaler = CreateMarshaler(eventInvoke); + try + { + var nativeDelegate = marshaler.GetAbi(); + state.InitializeReferenceTracking(nativeDelegate); + + EventRegistrationToken token; +#if NET + ExceptionHelpers.ThrowExceptionForHR(AddHandler(_objectReference.ThisPtr, nativeDelegate, &token)); +#else + ExceptionHelpers.ThrowExceptionForHR(AddHandler(_objectReference.ThisPtr, nativeDelegate, out token)); +#endif + global::System.GC.KeepAlive(_objectReference); + state.token = token; + } + finally + { + // Dispose our managed reference to the delegate's CCW. + // Either the native event holds a reference now or the _addHandler call failed. + marshaler.Dispose(); + } + } + } + } + + /// + /// Removes a given handler from the target event. + /// + /// The handler to remove from the target event. + public void Unsubscribe(TDelegate handler) + { + if (_state is null || !TryGetStateUnsafe(out var state)) + { + return; + } + + lock (this) + { + var oldEvent = state!.targetDelegate; + state.targetDelegate = (TDelegate?)Delegate.Remove(state.targetDelegate, handler); + if (oldEvent is object && state.targetDelegate is null) + { + UnsubscribeFromNative(state); + } + } + } + + private void UnsubscribeFromNative(EventSourceState state) + { + ExceptionHelpers.ThrowExceptionForHR(RemoveHandler(_objectReference.ThisPtr, state.token)); + global::System.GC.KeepAlive(_objectReference); + state.Dispose(); + _state = null; + } + +#if NET + [MemberNotNullWhen(true, nameof(_state))] +#endif + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool TryGetStateUnsafe( +#if NET + [NotNullWhen(true)] +#endif + out EventSourceState? state) + { + if (_state is not null && _state.TryGetTarget(out object? stateObj)) + { + state = Unsafe.As>(stateObj); + + return true; + } + + state = null; + + return false; + } + } +} + +// Restore in case this file is merged with others. +#nullable restore \ No newline at end of file diff --git a/src/WinRT.Runtime/Interop/ExceptionErrorInfo.cs b/src/WinRT.Runtime/Interop/ExceptionErrorInfo.cs index 775c62b73..acf71b77d 100644 --- a/src/WinRT.Runtime/Interop/ExceptionErrorInfo.cs +++ b/src/WinRT.Runtime/Interop/ExceptionErrorInfo.cs @@ -1,506 +1,152 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -// This file was generated by cswinrt.exe -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Numerics; -using System.Security.Cryptography; -using System.Text; -using System.Threading; -using System.Linq.Expressions; - -namespace WinRT.Interop -{ - [Guid("1CF2B120-547D-101B-8E65-08002B2BD119")] - internal interface IErrorInfo - { - Guid GetGuid(); - string GetSource(); - string GetDescription(); - string GetHelpFile(); - string GetHelpFileContent(); - } - - [Guid("DF0B3D60-548F-101B-8E65-08002B2BD119")] - internal interface ISupportErrorInfo - { - bool InterfaceSupportsErrorInfo(Guid riid); - } - - [Guid("04a2dbf3-df83-116c-0946-0812abf6e07d")] - internal interface ILanguageExceptionErrorInfo - { - IObjectReference GetLanguageException(); - } - - [Guid("82BA7092-4C88-427D-A7BC-16DD93FEB67E")] - internal interface IRestrictedErrorInfo - { - void GetErrorDetails( - out string description, - out int error, - out string restrictedDescription, - out string capabilitySid); - - string GetReference(); - } - - internal sealed class ManagedExceptionErrorInfo : IErrorInfo, ISupportErrorInfo - { - private readonly Exception _exception; - - public ManagedExceptionErrorInfo(Exception ex) - { - _exception = ex; - } - - public bool InterfaceSupportsErrorInfo(Guid riid) => true; - - public Guid GetGuid() => default; - - public string GetSource() => _exception.Source; - - public string GetDescription() - { - string desc = _exception.Message; - if (string.IsNullOrEmpty(desc)) - { - desc = _exception.GetType().FullName; - } - return desc; - } - - public string GetHelpFile() => _exception.HelpLink; - - public string GetHelpFileContent() => string.Empty; - } -} - -#pragma warning disable CS0649 - -namespace ABI.WinRT.Interop -{ - using global::WinRT; - using WinRT.Interop; - - [Guid("1CF2B120-547D-101B-8E65-08002B2BD119")] - internal unsafe class IErrorInfo : global::WinRT.Interop.IErrorInfo - { - [Guid("1CF2B120-547D-101B-8E65-08002B2BD119")] - public struct Vftbl - { - internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; - private void* _GetGuid_0; - public delegate* unmanaged[Stdcall] GetGuid_0 { get => (delegate* unmanaged[Stdcall])_GetGuid_0; set => _GetGuid_0 = value; } - private void* _GetSource_1; - public delegate* unmanaged[Stdcall] GetSource_1 { get => (delegate* unmanaged[Stdcall])_GetSource_1; set => _GetSource_1 = value; } - private void* _GetDescription_2; - public delegate* unmanaged[Stdcall] GetDescription_2 { get => (delegate* unmanaged[Stdcall])_GetDescription_2; set => _GetDescription_2 = value; } - private void* _GetHelpFile_3; - public delegate* unmanaged[Stdcall] GetHelpFile_3 { get => (delegate* unmanaged[Stdcall])_GetHelpFile_3; set => _GetHelpFile_3 = value; } - private void* _GetHelpFileContent_4; - public delegate* unmanaged[Stdcall] GetHelpFileContent_4 { get => (delegate* unmanaged[Stdcall])_GetHelpFileContent_4; set => _GetHelpFileContent_4 = value; } - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - public delegate int GetGuidDelegate(IntPtr thisPtr, Guid* guid); - public delegate int GetBstrDelegate(IntPtr thisPtr, IntPtr* bstr); - private static readonly Delegate[] DelegateCache = new Delegate[5]; -#endif - - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, -#if !NET - _GetGuid_0 = Marshal.GetFunctionPointerForDelegate(DelegateCache[0] = new GetGuidDelegate(Do_Abi_GetGuid_0)).ToPointer(), - _GetSource_1 = Marshal.GetFunctionPointerForDelegate(DelegateCache[1] = new GetBstrDelegate(Do_Abi_GetSource_1)).ToPointer(), - _GetDescription_2 = Marshal.GetFunctionPointerForDelegate(DelegateCache[2] = new GetBstrDelegate(Do_Abi_GetDescription_2)).ToPointer(), - _GetHelpFile_3 = Marshal.GetFunctionPointerForDelegate(DelegateCache[3] = new GetBstrDelegate(Do_Abi_GetHelpFile_3)).ToPointer(), - _GetHelpFileContent_4 = Marshal.GetFunctionPointerForDelegate(DelegateCache[4] = new GetBstrDelegate(Do_Abi_GetHelpFileContent_4)).ToPointer(), -#else - _GetGuid_0 = (delegate* unmanaged)&Do_Abi_GetGuid_0, - _GetSource_1 = (delegate* unmanaged)&Do_Abi_GetSource_1, - _GetDescription_2 = (delegate* unmanaged)&Do_Abi_GetDescription_2, - _GetHelpFile_3 = (delegate* unmanaged)&Do_Abi_GetHelpFile_3, - _GetHelpFileContent_4 = (delegate* unmanaged)&Do_Abi_GetHelpFileContent_4 -#endif - }; - var nativeVftbl = (IntPtr*)Marshal.AllocCoTaskMem(Marshal.SizeOf()); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static int Do_Abi_GetGuid_0(IntPtr thisPtr, Guid* guid) - { - try - { - *guid = ComWrappersSupport.FindObject(thisPtr).GetGuid(); - } - catch (Exception ex) - { - ExceptionHelpers.SetErrorInfo(ex); - return ExceptionHelpers.GetHRForException(ex); - } - return 0; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static int Do_Abi_GetSource_1(IntPtr thisPtr, IntPtr* source) - { - *source = IntPtr.Zero; - string _source; - try - { - _source = ComWrappersSupport.FindObject(thisPtr).GetSource(); - *source = Marshal.StringToBSTR(_source); - } - catch (Exception ex) - { - Marshal.FreeBSTR(*source); - ExceptionHelpers.SetErrorInfo(ex); - return ExceptionHelpers.GetHRForException(ex); - } - return 0; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static int Do_Abi_GetDescription_2(IntPtr thisPtr, IntPtr* description) - { - *description = IntPtr.Zero; - string _description; - try - { - _description = ComWrappersSupport.FindObject(thisPtr).GetDescription(); - *description = Marshal.StringToBSTR(_description); - } - catch (Exception ex) - { - Marshal.FreeBSTR(*description); - ExceptionHelpers.SetErrorInfo(ex); - return ExceptionHelpers.GetHRForException(ex); - } - return 0; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static int Do_Abi_GetHelpFile_3(IntPtr thisPtr, IntPtr* helpFile) - { - *helpFile = IntPtr.Zero; - string _helpFile; - try - { - _helpFile = ComWrappersSupport.FindObject(thisPtr).GetHelpFile(); - *helpFile = Marshal.StringToBSTR(_helpFile); - } - catch (Exception ex) - { - Marshal.FreeBSTR(*helpFile); - ExceptionHelpers.SetErrorInfo(ex); - return ExceptionHelpers.GetHRForException(ex); - } - return 0; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static int Do_Abi_GetHelpFileContent_4(IntPtr thisPtr, IntPtr* helpFileContent) - { - *helpFileContent = IntPtr.Zero; - string _helpFileContent; - try - { - _helpFileContent = ComWrappersSupport.FindObject(thisPtr).GetHelpFileContent(); - *helpFileContent = Marshal.StringToBSTR(_helpFileContent); - } - catch (Exception ex) - { - Marshal.FreeBSTR(*helpFileContent); - ExceptionHelpers.SetErrorInfo(ex); - return ExceptionHelpers.GetHRForException(ex); - } - return 0; - } - } - - public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator IErrorInfo(IObjectReference obj) => (obj != null) ? new IErrorInfo(obj) : null; - public static implicit operator IErrorInfo(ObjectReference obj) => (obj != null) ? new IErrorInfo(obj) : null; - protected readonly ObjectReference _obj; - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public IErrorInfo(IObjectReference obj) : this(obj.As()) { } - public IErrorInfo(ObjectReference obj) - { - _obj = obj; - } - - public Guid GetGuid() - { - Guid __return_value__; - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetGuid_0(ThisPtr, &__return_value__)); - return __return_value__; - } - - public string GetSource() - { - IntPtr __retval = default; - try - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetSource_1(ThisPtr, &__retval)); - return __retval != IntPtr.Zero ? Marshal.PtrToStringBSTR(__retval) : string.Empty; - } - finally - { - Marshal.FreeBSTR(__retval); - } - } - - public string GetDescription() - { - IntPtr __retval = default; - try - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetDescription_2(ThisPtr, &__retval)); - return __retval != IntPtr.Zero ? Marshal.PtrToStringBSTR(__retval) : string.Empty; - } - finally - { - Marshal.FreeBSTR(__retval); - } - } - - public string GetHelpFile() - { - IntPtr __retval = default; - try - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetHelpFile_3(ThisPtr, &__retval)); - return __retval != IntPtr.Zero ? Marshal.PtrToStringBSTR(__retval) : string.Empty; - } - finally - { - Marshal.FreeBSTR(__retval); - } - } - - public string GetHelpFileContent() - { - IntPtr __retval = default; - try - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetHelpFileContent_4(ThisPtr, &__retval)); - return __retval != IntPtr.Zero ? Marshal.PtrToStringBSTR(__retval) : string.Empty; - } - finally - { - Marshal.FreeBSTR(__retval); - } - } - } - - [Guid("04a2dbf3-df83-116c-0946-0812abf6e07d")] - internal unsafe class ILanguageExceptionErrorInfo : global::WinRT.Interop.ILanguageExceptionErrorInfo - { - [Guid("04a2dbf3-df83-116c-0946-0812abf6e07d")] - public struct Vftbl - { - internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; - private void* _GetLanguageException_0; - public delegate* unmanaged[Stdcall] GetLanguageException_0 => (delegate* unmanaged[Stdcall])_GetLanguageException_0; - } - - public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator ILanguageExceptionErrorInfo(IObjectReference obj) => (obj != null) ? new ILanguageExceptionErrorInfo(obj) : null; - public static implicit operator ILanguageExceptionErrorInfo(ObjectReference obj) => (obj != null) ? new ILanguageExceptionErrorInfo(obj) : null; - protected readonly ObjectReference _obj; - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public ILanguageExceptionErrorInfo(IObjectReference obj) : this(obj.As()) { } - public ILanguageExceptionErrorInfo(ObjectReference obj) - { - _obj = obj; - } - - public IObjectReference GetLanguageException() - { - IntPtr __return_value__ = IntPtr.Zero; - - try - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetLanguageException_0(ThisPtr, &__return_value__)); - return ObjectReference.Attach(ref __return_value__); - } - finally - { - if (__return_value__ != IntPtr.Zero) - { - (*(global::WinRT.Interop.IUnknownVftbl**)__return_value__)->Release(__return_value__); - } - } - } - } - - [Guid("DF0B3D60-548F-101B-8E65-08002B2BD119")] - internal unsafe class ISupportErrorInfo : global::WinRT.Interop.ISupportErrorInfo - { - [Guid("DF0B3D60-548F-101B-8E65-08002B2BD119")] - public struct Vftbl - { - internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; - private void* _InterfaceSupportsErrorInfo_0; - public delegate* unmanaged[Stdcall] InterfaceSupportsErrorInfo_0 { get => (delegate* unmanaged[Stdcall])_InterfaceSupportsErrorInfo_0; set => _InterfaceSupportsErrorInfo_0 = value; } - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; -#if !NET - public delegate int _InterfaceSupportsErrorInfo(IntPtr thisPtr, Guid* riid); - private static readonly _InterfaceSupportsErrorInfo DelegateCache; -#endif - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, -#if !NET - _InterfaceSupportsErrorInfo_0 = Marshal.GetFunctionPointerForDelegate(DelegateCache = Do_Abi_InterfaceSupportsErrorInfo_0).ToPointer() -#else - _InterfaceSupportsErrorInfo_0 = (delegate* unmanaged)&Do_Abi_InterfaceSupportsErrorInfo_0 -#endif - }; - var nativeVftbl = (IntPtr*)Marshal.AllocCoTaskMem(Marshal.SizeOf()); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } -#if NET - [UnmanagedCallersOnly] -#endif - private static int Do_Abi_InterfaceSupportsErrorInfo_0(IntPtr thisPtr, Guid* guid) - { - try - { - return global::WinRT.ComWrappersSupport.FindObject(thisPtr).InterfaceSupportsErrorInfo(*guid) ? 0 : 1; - } - catch (Exception ex) - { - ExceptionHelpers.SetErrorInfo(ex); - return ExceptionHelpers.GetHRForException(ex); - } - } - } - - public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator ISupportErrorInfo(IObjectReference obj) => (obj != null) ? new ISupportErrorInfo(obj) : null; - public static implicit operator ISupportErrorInfo(ObjectReference obj) => (obj != null) ? new ISupportErrorInfo(obj) : null; - protected readonly ObjectReference _obj; - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public ISupportErrorInfo(IObjectReference obj) : this(obj.As()) { } - public ISupportErrorInfo(ObjectReference obj) - { - _obj = obj; - } - - public bool InterfaceSupportsErrorInfo(Guid riid) - { - return _obj.Vftbl.InterfaceSupportsErrorInfo_0(ThisPtr, &riid) == 0; - } - } - - [Guid("82BA7092-4C88-427D-A7BC-16DD93FEB67E")] - internal unsafe class IRestrictedErrorInfo : global::WinRT.Interop.IRestrictedErrorInfo - { - [Guid("82BA7092-4C88-427D-A7BC-16DD93FEB67E")] - public struct Vftbl - { - public delegate int _GetErrorDetails(IntPtr thisPtr, out IntPtr description, out int error, out IntPtr restrictedDescription, out IntPtr capabilitySid); - public delegate int _GetReference(IntPtr thisPtr, out IntPtr reference); - - internal global::WinRT.Interop.IUnknownVftbl unknownVftbl; - private void* _GetErrorDetails_0; - public delegate* unmanaged[Stdcall] GetErrorDetails_0 => (delegate* unmanaged[Stdcall])_GetErrorDetails_0; - private void* _GetReference_1; - public delegate* unmanaged[Stdcall] GetReference_1 => (delegate* unmanaged[Stdcall])_GetReference_1; - } - - public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator IRestrictedErrorInfo(IObjectReference obj) => (obj != null) ? new IRestrictedErrorInfo(obj) : null; - public static implicit operator IRestrictedErrorInfo(ObjectReference obj) => (obj != null) ? new IRestrictedErrorInfo(obj) : null; - protected readonly ObjectReference _obj; - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public IRestrictedErrorInfo(IObjectReference obj) : this(obj.As()) { } - public IRestrictedErrorInfo(ObjectReference obj) - { - _obj = obj; - } - - public void GetErrorDetails( - out string description, - out int error, - out string restrictedDescription, - out string capabilitySid) - { - IntPtr _description = IntPtr.Zero; - IntPtr _restrictedDescription = IntPtr.Zero; - IntPtr _capabilitySid = IntPtr.Zero; - try - { - fixed (int* pError = &error) - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetErrorDetails_0(ThisPtr, &_description, pError, &_restrictedDescription, &_capabilitySid)); - } - description = _description != IntPtr.Zero ? Marshal.PtrToStringBSTR(_description) : string.Empty; - restrictedDescription = _restrictedDescription != IntPtr.Zero ? Marshal.PtrToStringBSTR(_restrictedDescription) : string.Empty; - capabilitySid = _capabilitySid != IntPtr.Zero ? Marshal.PtrToStringBSTR(_capabilitySid) : string.Empty; - } - finally - { - Marshal.FreeBSTR(_description); - Marshal.FreeBSTR(_restrictedDescription); - Marshal.FreeBSTR(_capabilitySid); - } - } - - public string GetReference() - { - IntPtr __retval = default; - try - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetReference_1(ThisPtr, &__retval)); - return __retval != IntPtr.Zero ? Marshal.PtrToStringBSTR(__retval) : string.Empty; - } - finally - { - Marshal.FreeBSTR(__retval); - } - } - } -} +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.InteropServices; +using WinRT; +using WinRT.Interop; + +namespace ABI.WinRT.Interop +{ + internal static class IRestrictedErrorInfoMethods + { + public static unsafe void GetErrorDetails( + IntPtr thisPtr, + out string description, + out int error, + out string restrictedDescription, + out string capabilitySid) + { + IntPtr _description = IntPtr.Zero; + IntPtr _restrictedDescription = IntPtr.Zero; + IntPtr _capabilitySid = IntPtr.Zero; + + try + { + fixed (int* pError = &error) + { + // GetErrorDetails + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[3])( + thisPtr, + &_description, + pError, + &_restrictedDescription, + &_capabilitySid)); + } + + description = _description != IntPtr.Zero ? Marshal.PtrToStringBSTR(_description) : string.Empty; + restrictedDescription = _restrictedDescription != IntPtr.Zero ? Marshal.PtrToStringBSTR(_restrictedDescription) : string.Empty; + capabilitySid = _capabilitySid != IntPtr.Zero ? Marshal.PtrToStringBSTR(_capabilitySid) : string.Empty; + } + finally + { + Marshal.FreeBSTR(_description); + Marshal.FreeBSTR(_restrictedDescription); + Marshal.FreeBSTR(_capabilitySid); + } + } + + public static unsafe void GetErrorDetails( + IntPtr thisPtr, + out int error) + { + IntPtr _description = IntPtr.Zero; + IntPtr _restrictedDescription = IntPtr.Zero; + IntPtr _capabilitySid = IntPtr.Zero; + + try + { + fixed (int* pError = &error) + { + // GetErrorDetails + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[3])( + thisPtr, + &_description, + pError, + &_restrictedDescription, + &_capabilitySid)); + } + } + finally + { + Marshal.FreeBSTR(_description); + Marshal.FreeBSTR(_restrictedDescription); + Marshal.FreeBSTR(_capabilitySid); + } + } + + public static unsafe string GetReference(IntPtr thisPtr) + { + IntPtr __retval = default; + + try + { + // GetReference + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[4])( + thisPtr, + &__retval)); + return __retval != IntPtr.Zero ? Marshal.PtrToStringBSTR(__retval) : string.Empty; + } + finally + { + Marshal.FreeBSTR(__retval); + } + } + } + + internal static class ILanguageExceptionErrorInfoMethods + { + public static unsafe IntPtr GetLanguageException(IntPtr thisPtr) + { + IntPtr __return_value__ = IntPtr.Zero; + + // GetLanguageException + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[3])(thisPtr, &__return_value__)); + return __return_value__; + } + } + + internal static class ILanguageExceptionErrorInfo2Methods + { + public static unsafe IntPtr GetPreviousLanguageExceptionErrorInfo(IntPtr thisPtr) + { + IntPtr __retval = default; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[4])( + thisPtr, + &__retval)); + return __retval; + } + + public static unsafe void CapturePropagationContext(IntPtr thisPtr, Exception ex) + { +#if NET + IntPtr managedExceptionWrapper = ComWrappersSupport.CreateCCWForObjectUnsafe(ex); + + try + { + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[5])( + thisPtr, + managedExceptionWrapper)); + } + finally + { + Marshal.Release(managedExceptionWrapper); + } +#else + using var managedExceptionWrapper = ComWrappersSupport.CreateCCWForObject(ex); + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[5])( + thisPtr, + managedExceptionWrapper.ThisPtr)); +#endif + } + + public static unsafe IntPtr GetPropagationContextHead(IntPtr thisPtr) + { + IntPtr __retval = default; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[6])( + thisPtr, + &__retval)); + return __retval; + } + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime/Interop/ExceptionErrorInfo.net5.cs b/src/WinRT.Runtime/Interop/ExceptionErrorInfo.net5.cs new file mode 100644 index 000000000..008698e53 --- /dev/null +++ b/src/WinRT.Runtime/Interop/ExceptionErrorInfo.net5.cs @@ -0,0 +1,229 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace WinRT.Interop +{ + [WinRTExposedType(typeof(ManagedExceptionErrorInfoTypeDetails))] + internal sealed class ManagedExceptionErrorInfo + { + private readonly Exception _exception; + + public ManagedExceptionErrorInfo(Exception ex) + { + _exception = ex; + } + + public bool InterfaceSupportsErrorInfo(Guid riid) => true; + + public Guid GetGuid() => default; + + public string GetSource() => _exception.Source; + + public string GetDescription() + { + string desc = _exception.Message; + if (string.IsNullOrEmpty(desc)) + { + desc = _exception.GetType().FullName; + } + return desc; + } + + public string GetHelpFile() => _exception.HelpLink; + + public string GetHelpFileContent() => string.Empty; + } + + internal sealed class ManagedExceptionErrorInfoTypeDetails : IWinRTExposedTypeDetails + { + public ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + { + return new ComWrappers.ComInterfaceEntry[] + { + new ComWrappers.ComInterfaceEntry + { + IID = IID.IID_IErrorInfo, + Vtable = ABI.WinRT.Interop.IErrorInfoVftbl.AbiToProjectionVftablePtr + }, + new ComWrappers.ComInterfaceEntry + { + IID = IID.IID_ISupportErrorInfo, + Vtable = ABI.WinRT.Interop.ISupportErrorInfoVftbl.AbiToProjectionVftablePtr + } + }; + } + } +} + +#pragma warning disable CS0649 + +namespace ABI.WinRT.Interop +{ + using global::WinRT; + using global::WinRT.Interop; + + internal unsafe struct IErrorInfoVftbl + { + public IUnknownVftbl IUnknownVftbl; + public delegate* unmanaged[Stdcall] GetGuid_0; + public delegate* unmanaged[Stdcall] GetSource_1; + public delegate* unmanaged[Stdcall] GetDescription_2; + public delegate* unmanaged[Stdcall] GetHelpFile_3; + public delegate* unmanaged[Stdcall] GetHelpFileContent_4; + + public static readonly IErrorInfoVftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + + static IErrorInfoVftbl() + { + AbiToProjectionVftable = new IErrorInfoVftbl + { + IUnknownVftbl = IUnknownVftbl.AbiToProjectionVftbl, + GetGuid_0 = &Do_Abi_GetGuid_0, + GetSource_1 = &Do_Abi_GetSource_1, + GetDescription_2 = &Do_Abi_GetDescription_2, + GetHelpFile_3 = &Do_Abi_GetHelpFile_3, + GetHelpFileContent_4 = &Do_Abi_GetHelpFileContent_4 + }; + + var nativeVftbl = (IntPtr*)Marshal.AllocCoTaskMem(sizeof(IErrorInfoVftbl)); + + *(IErrorInfoVftbl*)nativeVftbl = AbiToProjectionVftable; + + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static int Do_Abi_GetGuid_0(IntPtr thisPtr, Guid* guid) + { + try + { + *guid = ComWrappersSupport.FindObject(thisPtr).GetGuid(); + } + catch (Exception ex) + { + ExceptionHelpers.SetErrorInfo(ex); + return ExceptionHelpers.GetHRForException(ex); + } + return 0; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static int Do_Abi_GetSource_1(IntPtr thisPtr, IntPtr* source) + { + *source = IntPtr.Zero; + string _source; + try + { + _source = ComWrappersSupport.FindObject(thisPtr).GetSource(); + *source = Marshal.StringToBSTR(_source); + } + catch (Exception ex) + { + Marshal.FreeBSTR(*source); + ExceptionHelpers.SetErrorInfo(ex); + return ExceptionHelpers.GetHRForException(ex); + } + return 0; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static int Do_Abi_GetDescription_2(IntPtr thisPtr, IntPtr* description) + { + *description = IntPtr.Zero; + string _description; + try + { + _description = ComWrappersSupport.FindObject(thisPtr).GetDescription(); + *description = Marshal.StringToBSTR(_description); + } + catch (Exception ex) + { + Marshal.FreeBSTR(*description); + ExceptionHelpers.SetErrorInfo(ex); + return ExceptionHelpers.GetHRForException(ex); + } + return 0; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static int Do_Abi_GetHelpFile_3(IntPtr thisPtr, IntPtr* helpFile) + { + *helpFile = IntPtr.Zero; + string _helpFile; + try + { + _helpFile = ComWrappersSupport.FindObject(thisPtr).GetHelpFile(); + *helpFile = Marshal.StringToBSTR(_helpFile); + } + catch (Exception ex) + { + Marshal.FreeBSTR(*helpFile); + ExceptionHelpers.SetErrorInfo(ex); + return ExceptionHelpers.GetHRForException(ex); + } + return 0; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static int Do_Abi_GetHelpFileContent_4(IntPtr thisPtr, IntPtr* helpFileContent) + { + *helpFileContent = IntPtr.Zero; + string _helpFileContent; + try + { + _helpFileContent = ComWrappersSupport.FindObject(thisPtr).GetHelpFileContent(); + *helpFileContent = Marshal.StringToBSTR(_helpFileContent); + } + catch (Exception ex) + { + Marshal.FreeBSTR(*helpFileContent); + ExceptionHelpers.SetErrorInfo(ex); + return ExceptionHelpers.GetHRForException(ex); + } + return 0; + } + } + + internal unsafe struct ISupportErrorInfoVftbl + { + public IUnknownVftbl IUnknownVftbl; + public delegate* unmanaged[Stdcall] InterfaceSupportsErrorInfo_0; + + public static readonly ISupportErrorInfoVftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + + static ISupportErrorInfoVftbl() + { + AbiToProjectionVftable = new ISupportErrorInfoVftbl + { + IUnknownVftbl = IUnknownVftbl.AbiToProjectionVftbl, + InterfaceSupportsErrorInfo_0 = &Do_Abi_InterfaceSupportsErrorInfo_0 + }; + + var nativeVftbl = (IntPtr*)Marshal.AllocCoTaskMem(sizeof(ISupportErrorInfoVftbl)); + + *(ISupportErrorInfoVftbl*)nativeVftbl = AbiToProjectionVftable; + + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static int Do_Abi_InterfaceSupportsErrorInfo_0(IntPtr thisPtr, Guid* guid) + { + try + { + return global::WinRT.ComWrappersSupport.FindObject(thisPtr).InterfaceSupportsErrorInfo(*guid) ? 0 : 1; + } + catch (Exception ex) + { + ExceptionHelpers.SetErrorInfo(ex); + return ExceptionHelpers.GetHRForException(ex); + } + } + } +} diff --git a/src/WinRT.Runtime/Interop/ExceptionErrorInfo.netstandard2.0.cs b/src/WinRT.Runtime/Interop/ExceptionErrorInfo.netstandard2.0.cs new file mode 100644 index 000000000..f4a3e5ebd --- /dev/null +++ b/src/WinRT.Runtime/Interop/ExceptionErrorInfo.netstandard2.0.cs @@ -0,0 +1,333 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +// This file was generated by cswinrt.exe +using System; +using System.Runtime.InteropServices; + +namespace WinRT.Interop +{ + [Guid("1CF2B120-547D-101B-8E65-08002B2BD119")] + internal interface IErrorInfo + { + Guid GetGuid(); + string GetSource(); + string GetDescription(); + string GetHelpFile(); + string GetHelpFileContent(); + } + + [Guid("DF0B3D60-548F-101B-8E65-08002B2BD119")] + internal interface ISupportErrorInfo + { + bool InterfaceSupportsErrorInfo(Guid riid); + } + + internal sealed class ManagedExceptionErrorInfo : IErrorInfo, ISupportErrorInfo + { + private readonly Exception _exception; + + public ManagedExceptionErrorInfo(Exception ex) + { + _exception = ex; + } + + public bool InterfaceSupportsErrorInfo(Guid riid) => true; + + public Guid GetGuid() => default; + + public string GetSource() => _exception.Source; + + public string GetDescription() + { + string desc = _exception.Message; + if (string.IsNullOrEmpty(desc)) + { + desc = _exception.GetType().FullName; + } + return desc; + } + + public string GetHelpFile() => _exception.HelpLink; + + public string GetHelpFileContent() => string.Empty; + } +} + +#pragma warning disable CS0649 + +namespace ABI.WinRT.Interop +{ + using global::WinRT; + + [Guid("1CF2B120-547D-101B-8E65-08002B2BD119")] + internal unsafe class IErrorInfo : global::WinRT.Interop.IErrorInfo + { + internal static readonly Guid IID = new(0x1CF2B120, 0x547D, 0x101B, 0x8E, 0x65, 0x08, 0x00, 0x2B, 0x2B, 0xD1, 0x19); + + [Guid("1CF2B120-547D-101B-8E65-08002B2BD119")] + public struct Vftbl + { + internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; + private void* _GetGuid_0; + public delegate* unmanaged[Stdcall] GetGuid_0 { get => (delegate* unmanaged[Stdcall])_GetGuid_0; set => _GetGuid_0 = value; } + private void* _GetSource_1; + public delegate* unmanaged[Stdcall] GetSource_1 { get => (delegate* unmanaged[Stdcall])_GetSource_1; set => _GetSource_1 = value; } + private void* _GetDescription_2; + public delegate* unmanaged[Stdcall] GetDescription_2 { get => (delegate* unmanaged[Stdcall])_GetDescription_2; set => _GetDescription_2 = value; } + private void* _GetHelpFile_3; + public delegate* unmanaged[Stdcall] GetHelpFile_3 { get => (delegate* unmanaged[Stdcall])_GetHelpFile_3; set => _GetHelpFile_3 = value; } + private void* _GetHelpFileContent_4; + public delegate* unmanaged[Stdcall] GetHelpFileContent_4 { get => (delegate* unmanaged[Stdcall])_GetHelpFileContent_4; set => _GetHelpFileContent_4 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + + public delegate int GetGuidDelegate(IntPtr thisPtr, Guid* guid); + public delegate int GetBstrDelegate(IntPtr thisPtr, IntPtr* bstr); + private static readonly Delegate[] DelegateCache = new Delegate[5]; + + static unsafe Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, + _GetGuid_0 = Marshal.GetFunctionPointerForDelegate(DelegateCache[0] = new GetGuidDelegate(Do_Abi_GetGuid_0)).ToPointer(), + _GetSource_1 = Marshal.GetFunctionPointerForDelegate(DelegateCache[1] = new GetBstrDelegate(Do_Abi_GetSource_1)).ToPointer(), + _GetDescription_2 = Marshal.GetFunctionPointerForDelegate(DelegateCache[2] = new GetBstrDelegate(Do_Abi_GetDescription_2)).ToPointer(), + _GetHelpFile_3 = Marshal.GetFunctionPointerForDelegate(DelegateCache[3] = new GetBstrDelegate(Do_Abi_GetHelpFile_3)).ToPointer(), + _GetHelpFileContent_4 = Marshal.GetFunctionPointerForDelegate(DelegateCache[4] = new GetBstrDelegate(Do_Abi_GetHelpFileContent_4)).ToPointer() + }; + var nativeVftbl = (IntPtr*)Marshal.AllocCoTaskMem(sizeof(Vftbl)); + Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + + private static int Do_Abi_GetGuid_0(IntPtr thisPtr, Guid* guid) + { + try + { + *guid = ComWrappersSupport.FindObject(thisPtr).GetGuid(); + } + catch (Exception ex) + { + ExceptionHelpers.SetErrorInfo(ex); + return ExceptionHelpers.GetHRForException(ex); + } + return 0; + } + + private static int Do_Abi_GetSource_1(IntPtr thisPtr, IntPtr* source) + { + *source = IntPtr.Zero; + string _source; + try + { + _source = ComWrappersSupport.FindObject(thisPtr).GetSource(); + *source = Marshal.StringToBSTR(_source); + } + catch (Exception ex) + { + Marshal.FreeBSTR(*source); + ExceptionHelpers.SetErrorInfo(ex); + return ExceptionHelpers.GetHRForException(ex); + } + return 0; + } + + private static int Do_Abi_GetDescription_2(IntPtr thisPtr, IntPtr* description) + { + *description = IntPtr.Zero; + string _description; + try + { + _description = ComWrappersSupport.FindObject(thisPtr).GetDescription(); + *description = Marshal.StringToBSTR(_description); + } + catch (Exception ex) + { + Marshal.FreeBSTR(*description); + ExceptionHelpers.SetErrorInfo(ex); + return ExceptionHelpers.GetHRForException(ex); + } + return 0; + } + + private static int Do_Abi_GetHelpFile_3(IntPtr thisPtr, IntPtr* helpFile) + { + *helpFile = IntPtr.Zero; + string _helpFile; + try + { + _helpFile = ComWrappersSupport.FindObject(thisPtr).GetHelpFile(); + *helpFile = Marshal.StringToBSTR(_helpFile); + } + catch (Exception ex) + { + Marshal.FreeBSTR(*helpFile); + ExceptionHelpers.SetErrorInfo(ex); + return ExceptionHelpers.GetHRForException(ex); + } + return 0; + } + + private static int Do_Abi_GetHelpFileContent_4(IntPtr thisPtr, IntPtr* helpFileContent) + { + *helpFileContent = IntPtr.Zero; + string _helpFileContent; + try + { + _helpFileContent = ComWrappersSupport.FindObject(thisPtr).GetHelpFileContent(); + *helpFileContent = Marshal.StringToBSTR(_helpFileContent); + } + catch (Exception ex) + { + Marshal.FreeBSTR(*helpFileContent); + ExceptionHelpers.SetErrorInfo(ex); + return ExceptionHelpers.GetHRForException(ex); + } + return 0; + } + } + + public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); + + public static implicit operator IErrorInfo(IObjectReference obj) => (obj != null) ? new IErrorInfo(obj) : null; + public static implicit operator IErrorInfo(ObjectReference obj) => (obj != null) ? new IErrorInfo(obj) : null; + protected readonly ObjectReference _obj; + public IntPtr ThisPtr => _obj.ThisPtr; + public ObjectReference AsInterface() => _obj.As(); + public A As() => _obj.AsType(); + public IErrorInfo(IObjectReference obj) : this(obj.As()) { } + public IErrorInfo(ObjectReference obj) + { + _obj = obj; + } + + public Guid GetGuid() + { + Guid __return_value__; + Marshal.ThrowExceptionForHR(_obj.Vftbl.GetGuid_0(ThisPtr, &__return_value__)); + return __return_value__; + } + + public string GetSource() + { + IntPtr __retval = default; + try + { + Marshal.ThrowExceptionForHR(_obj.Vftbl.GetSource_1(ThisPtr, &__retval)); + return __retval != IntPtr.Zero ? Marshal.PtrToStringBSTR(__retval) : string.Empty; + } + finally + { + Marshal.FreeBSTR(__retval); + } + } + + public string GetDescription() + { + IntPtr __retval = default; + try + { + Marshal.ThrowExceptionForHR(_obj.Vftbl.GetDescription_2(ThisPtr, &__retval)); + return __retval != IntPtr.Zero ? Marshal.PtrToStringBSTR(__retval) : string.Empty; + } + finally + { + Marshal.FreeBSTR(__retval); + } + } + + public string GetHelpFile() + { + IntPtr __retval = default; + try + { + Marshal.ThrowExceptionForHR(_obj.Vftbl.GetHelpFile_3(ThisPtr, &__retval)); + return __retval != IntPtr.Zero ? Marshal.PtrToStringBSTR(__retval) : string.Empty; + } + finally + { + Marshal.FreeBSTR(__retval); + } + } + + public string GetHelpFileContent() + { + IntPtr __retval = default; + try + { + Marshal.ThrowExceptionForHR(_obj.Vftbl.GetHelpFileContent_4(ThisPtr, &__retval)); + return __retval != IntPtr.Zero ? Marshal.PtrToStringBSTR(__retval) : string.Empty; + } + finally + { + Marshal.FreeBSTR(__retval); + } + } + } + + [Guid("DF0B3D60-548F-101B-8E65-08002B2BD119")] + internal unsafe class ISupportErrorInfo : global::WinRT.Interop.ISupportErrorInfo + { + internal static readonly Guid IID = new(0xDF0B3D60, 0x548F, 0x101B, 0x8E, 0x65, 0x08, 0x00, 0x2B, 0x2B, 0xD1, 0x19); + + [Guid("DF0B3D60-548F-101B-8E65-08002B2BD119")] + public struct Vftbl + { + internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; + private void* _InterfaceSupportsErrorInfo_0; + public delegate* unmanaged[Stdcall] InterfaceSupportsErrorInfo_0 { get => (delegate* unmanaged[Stdcall])_InterfaceSupportsErrorInfo_0; set => _InterfaceSupportsErrorInfo_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + + public delegate int _InterfaceSupportsErrorInfo(IntPtr thisPtr, Guid* riid); + private static readonly _InterfaceSupportsErrorInfo DelegateCache; + static unsafe Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, + _InterfaceSupportsErrorInfo_0 = Marshal.GetFunctionPointerForDelegate(DelegateCache = Do_Abi_InterfaceSupportsErrorInfo_0).ToPointer() + }; + var nativeVftbl = (IntPtr*)Marshal.AllocCoTaskMem(sizeof(Vftbl)); + Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + + private static int Do_Abi_InterfaceSupportsErrorInfo_0(IntPtr thisPtr, Guid* guid) + { + try + { + return global::WinRT.ComWrappersSupport.FindObject(thisPtr).InterfaceSupportsErrorInfo(*guid) ? 0 : 1; + } + catch (Exception ex) + { + ExceptionHelpers.SetErrorInfo(ex); + return ExceptionHelpers.GetHRForException(ex); + } + } + } + + public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); + + public static implicit operator ISupportErrorInfo(IObjectReference obj) => (obj != null) ? new ISupportErrorInfo(obj) : null; + public static implicit operator ISupportErrorInfo(ObjectReference obj) => (obj != null) ? new ISupportErrorInfo(obj) : null; + protected readonly ObjectReference _obj; + public IntPtr ThisPtr => _obj.ThisPtr; + public ObjectReference AsInterface() => _obj.As(); + public A As() => _obj.AsType(); + public ISupportErrorInfo(IObjectReference obj) : this(obj.As()) { } + public ISupportErrorInfo(ObjectReference obj) + { + _obj = obj; + } + + public bool InterfaceSupportsErrorInfo(Guid riid) + { + return _obj.Vftbl.InterfaceSupportsErrorInfo_0(ThisPtr, &riid) == 0; + } + } +} diff --git a/src/WinRT.Runtime/Interop/IActivationFactory.cs b/src/WinRT.Runtime/Interop/IActivationFactory.cs index 1ba4f4614..32d5cd8c1 100644 --- a/src/WinRT.Runtime/Interop/IActivationFactory.cs +++ b/src/WinRT.Runtime/Interop/IActivationFactory.cs @@ -2,14 +2,20 @@ // Licensed under the MIT License. using System; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using WinRT; +using WinRT.Interop; namespace WinRT.Interop { + /// + /// An interface for managed objects representing activation factories for WinRT types. + /// [WindowsRuntimeType] - [Guid("00000035-0000-0000-C000-000000000046")] + [Guid("00000035-0000-0000-C000-000000000046")] [WindowsRuntimeHelperType(typeof(global::ABI.WinRT.Interop.IActivationFactory))] #if EMBED internal @@ -18,15 +24,99 @@ namespace WinRT.Interop #endif interface IActivationFactory { + /// + /// Activates an instance of the target WinRT type. + /// + /// The resulting instance. + /// + /// Thrown if the operation is not available on the activation factory type in use. For instance, + /// that is the case if the associated type is static or does not have a default constructor. + /// IntPtr ActivateInstance(); } } namespace ABI.WinRT.Interop { +#if EMBED + internal +#else + public +#endif + static class IActivationFactoryMethods + { + public static global::System.Guid IID => global::WinRT.Interop.IID.IID_IActivationFactory; + + public static IntPtr AbiToProjectionVftablePtr => IActivationFactory.Vftbl.AbiToProjectionVftablePtr; + +#if !NET + /// + /// Activates an instance from a given activation factory instance. + /// + /// The input activation factory instance to use. + /// The resulting instance created from . + /// + /// This method assumes is wrapping an IActivationFactory instance (with no validation). + /// This method is only meant to be used by the generated projections, and not by consumers of CsWinRT directly. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static unsafe IObjectReference ActivateInstanceUnsafe(IObjectReference objectReference) + { + IntPtr thisPtr = objectReference.ThisPtr; + IntPtr instancePtr; + + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[6](thisPtr, &instancePtr)); + + GC.KeepAlive(objectReference); + + try + { + return ComWrappersSupport.GetObjectReferenceForInterface(instancePtr, global::WinRT.Interop.IID.IID_IInspectable, requireQI: false); + } + finally + { + MarshalInspectable.DisposeAbi(instancePtr); + } + } +#else + /// + /// Activates an instance from a given activation factory instance with the result + /// pointing to the interface for the given iid. + /// + /// The input activation factory instance to use. + /// The iid for the interface which the resulting will be for. + /// The resulting instance created from and QI to the given . + /// + /// This method assumes is wrapping an IActivationFactory instance (with no validation). + /// This method is only meant to be used by the generated projections, and not by consumers of CsWinRT directly. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public static unsafe IObjectReference ActivateInstanceUnsafe(IObjectReference objectReference, Guid iid) + { + IntPtr thisPtr = objectReference.ThisPtr; + IntPtr instancePtr; + + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[6](thisPtr, &instancePtr)); + + GC.KeepAlive(objectReference); + + try + { + return ComWrappersSupport.GetObjectReferenceForInterface(instancePtr, iid, requireQI: true); + } + finally + { + MarshalInspectable.DisposeAbi(instancePtr); + } + } +#endif + } + +#if !NET [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] +#endif [Guid("00000035-0000-0000-C000-000000000046")] -#if EMBED +#if EMBED internal #else public @@ -51,7 +141,7 @@ public struct Vftbl #endif static unsafe Vftbl() { - AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); (*(Vftbl*)AbiToProjectionVftablePtr) = new Vftbl { IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, @@ -85,18 +175,25 @@ private static unsafe int Do_Abi_ActivateInstance_0(IntPtr thisPtr, IntPtr* resu return 0; } } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); +#if !NET public static implicit operator IActivationFactory(IObjectReference obj) => (obj != null) ? new IActivationFactory(obj) : null; +#endif +#if NET + protected readonly ObjectReference _obj; +#else protected readonly ObjectReference _obj; +#endif public IObjectReference ObjRef { get => _obj; } public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public IActivationFactory(IObjectReference obj) : this(obj.As()) { } - internal IActivationFactory(ObjectReference obj) + + public IActivationFactory(IObjectReference obj) { - _obj = obj; +#if NET + _obj = obj.As(IID.IID_IActivationFactory); +#else + _obj = obj.As(IID.IID_IActivationFactory); +#endif } public unsafe IntPtr ActivateInstance() @@ -104,7 +201,12 @@ public unsafe IntPtr ActivateInstance() IntPtr __retval = default; try { +#if NET + IntPtr thisPtr = ThisPtr; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[6])(thisPtr, &__retval)); +#else global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.ActivateInstance_0(ThisPtr, &__retval)); +#endif return __retval; } finally diff --git a/src/WinRT.Runtime/Interop/IAgileReference.net5.cs b/src/WinRT.Runtime/Interop/IAgileReference.net5.cs index 78ff5392b..9607e539a 100644 --- a/src/WinRT.Runtime/Interop/IAgileReference.net5.cs +++ b/src/WinRT.Runtime/Interop/IAgileReference.net5.cs @@ -24,17 +24,7 @@ internal interface IAgileReference #endif interface IAgileObject { - public static readonly Guid IID = new(0x94ea2b94, 0xe9cc, 0x49e0, 0xc0, 0xff, 0xee, 0x64, 0xca, 0x8f, 0x5b, 0x90); - } - - [WindowsRuntimeType] - [Guid("00000146-0000-0000-C000-000000000046")] - [WindowsRuntimeHelperType(typeof(global::ABI.WinRT.Interop.IGlobalInterfaceTable))] - internal interface IGlobalInterfaceTable - { - IntPtr RegisterInterfaceInGlobal(IntPtr ptr, Guid riid); - void RevokeInterfaceFromGlobal(IntPtr cookie); - IObjectReference GetInterfaceFromGlobal(IntPtr cookie, Guid riid); + public static readonly Guid IID = global::WinRT.Interop.IID.IID_IAgileObject; } } @@ -51,10 +41,11 @@ public static unsafe IObjectReference Resolve(IObjectReference _obj, Guid riid) var ThisPtr = _obj.ThisPtr; IntPtr ptr = IntPtr.Zero; ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3]( - ThisPtr, &riid, &ptr)); + ThisPtr, &riid, &ptr)); + global::System.GC.KeepAlive(_obj); try { - return ComWrappersSupport.GetObjectReferenceForInterface(ptr); + return ComWrappersSupport.GetObjectReferenceForInterface(ptr, riid, requireQI: false); } finally { @@ -70,9 +61,10 @@ public static unsafe ObjectReference Resolve(IObjectReference _obj, Guid r IntPtr ptr = IntPtr.Zero; ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3]( ThisPtr, &riid, &ptr)); + global::System.GC.KeepAlive(_obj); try { - return ComWrappersSupport.GetObjectReferenceForInterface(ptr); + return ComWrappersSupport.GetObjectReferenceForInterface(ptr, riid, requireQI: false); } finally { @@ -132,56 +124,55 @@ static unsafe IAgileObject() } [Guid("00000146-0000-0000-C000-000000000046")] - internal sealed unsafe class IGlobalInterfaceTable : global::WinRT.Interop.IGlobalInterfaceTable - { - internal static readonly Guid IID = new(0x00000146, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46); - - [Guid("00000146-0000-0000-C000-000000000046")] - [StructLayout(LayoutKind.Sequential)] - public struct Vftbl - { - public global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; - private void* _RegisterInterfaceInGlobal; - public delegate* unmanaged[Stdcall] RegisterInterfaceInGlobal => (delegate* unmanaged[Stdcall])_RegisterInterfaceInGlobal; - private void* _RevokeInterfaceFromGlobal; - public delegate* unmanaged[Stdcall] RevokeInterfaceFromGlobal => (delegate* unmanaged[Stdcall])_RevokeInterfaceFromGlobal; - private void* _GetInterfaceFromGlobal; - public delegate* unmanaged[Stdcall] GetInterfaceFromGlobal => (delegate* unmanaged[Stdcall])_GetInterfaceFromGlobal; - } - - public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); + internal sealed unsafe class IGlobalInterfaceTable + { + public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr, global::WinRT.Interop.IID.IID_IGlobalInterfaceTable); - public static implicit operator IGlobalInterfaceTable(IObjectReference obj) => (obj != null) ? new IGlobalInterfaceTable(obj) : null; - public static implicit operator IGlobalInterfaceTable(ObjectReference obj) => (obj != null) ? new IGlobalInterfaceTable(obj) : null; - private readonly ObjectReference _obj; + private readonly ObjectReference _obj; public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public IGlobalInterfaceTable(IObjectReference obj) : this(obj.As()) { } - public IGlobalInterfaceTable(ObjectReference obj) + public IGlobalInterfaceTable(IntPtr thisPtr) { - _obj = obj; + _obj = ObjectReference.FromAbi(thisPtr, global::WinRT.Interop.IID.IID_IGlobalInterfaceTable); } public IntPtr RegisterInterfaceInGlobal(IntPtr ptr, Guid riid) { - ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RegisterInterfaceInGlobal(ThisPtr, ptr, ref riid, out IntPtr cookie)); + IntPtr thisPtr = ThisPtr; + IntPtr cookie; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[3])(thisPtr, ptr, &riid, &cookie)); + global::System.GC.KeepAlive(_obj); return cookie; } - public void RevokeInterfaceFromGlobal(IntPtr cookie) + public void TryRevokeInterfaceFromGlobal(IntPtr cookie) { - ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RevokeInterfaceFromGlobal(ThisPtr, cookie)); + IntPtr thisPtr = ThisPtr; + int hresult = ((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[4])(thisPtr, cookie); + global::System.GC.KeepAlive(_obj); + + if (hresult == ExceptionHelpers.E_INVALIDARG) + { + // Revoking cookie from GIT table may fail if apartment is gone. + } + else + { + // Only throw for other errors (this should technically never happen) + Marshal.ThrowExceptionForHR(hresult); + } } public IObjectReference GetInterfaceFromGlobal(IntPtr cookie, Guid riid) { - ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInterfaceFromGlobal(ThisPtr, cookie, ref riid, out IntPtr ptr)); + IntPtr thisPtr = ThisPtr; + IntPtr ptr; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[5])(thisPtr, cookie, &riid, &ptr)); + global::System.GC.KeepAlive(_obj); + try { - return ComWrappersSupport.GetObjectReferenceForInterface(ptr); + return ComWrappersSupport.GetObjectReferenceForInterface(ptr, riid, requireQI: false); } finally { diff --git a/src/WinRT.Runtime/Interop/IAgileReference.netstandard2.0.cs b/src/WinRT.Runtime/Interop/IAgileReference.netstandard2.0.cs index edbb4f13c..687420625 100644 --- a/src/WinRT.Runtime/Interop/IAgileReference.netstandard2.0.cs +++ b/src/WinRT.Runtime/Interop/IAgileReference.netstandard2.0.cs @@ -50,10 +50,12 @@ public static unsafe IObjectReference Resolve(IObjectReference _obj, Guid riid) var ThisPtr = _obj.ThisPtr; IntPtr ptr = IntPtr.Zero; ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3]( - ThisPtr, &riid, &ptr)); + ThisPtr, &riid, &ptr)); + global::System.GC.KeepAlive(_obj); + try { - return ComWrappersSupport.GetObjectReferenceForInterface(ptr); + return ComWrappersSupport.GetObjectReferenceForInterface(ptr, riid, requireQI: false); } finally { @@ -68,10 +70,12 @@ public static unsafe ObjectReference Resolve(IObjectReference _obj, Guid r var ThisPtr = _obj.ThisPtr; IntPtr ptr = IntPtr.Zero; ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3]( - ThisPtr, &riid, &ptr)); + ThisPtr, &riid, &ptr)); + global::System.GC.KeepAlive(_obj); + try { - return ComWrappersSupport.GetObjectReferenceForInterface(ptr); + return ComWrappersSupport.GetObjectReferenceForInterface(ptr, riid, requireQI: false); } finally { @@ -230,22 +234,25 @@ public IGlobalInterfaceTable(ObjectReference obj) public IntPtr RegisterInterfaceInGlobal(IntPtr ptr, Guid riid) { - ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RegisterInterfaceInGlobal(ThisPtr, ptr, ref riid, out IntPtr cookie)); + ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RegisterInterfaceInGlobal(ThisPtr, ptr, ref riid, out IntPtr cookie)); + global::System.GC.KeepAlive(_obj); return cookie; } public void RevokeInterfaceFromGlobal(IntPtr cookie) { - ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RevokeInterfaceFromGlobal(ThisPtr, cookie)); + ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RevokeInterfaceFromGlobal(ThisPtr, cookie)); + global::System.GC.KeepAlive(_obj); } public IObjectReference GetInterfaceFromGlobal(IntPtr cookie, Guid riid) { - ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInterfaceFromGlobal(ThisPtr, cookie, ref riid, out IntPtr ptr)); + ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInterfaceFromGlobal(ThisPtr, cookie, ref riid, out IntPtr ptr)); + global::System.GC.KeepAlive(_obj); try { - return ComWrappersSupport.GetObjectReferenceForInterface(ptr); + return ComWrappersSupport.GetObjectReferenceForInterface(ptr, riid, requireQI: false); } finally { diff --git a/src/WinRT.Runtime/Interop/IContextCallback.cs b/src/WinRT.Runtime/Interop/IContextCallback.cs index 3e24df1fa..9a87c29fa 100644 --- a/src/WinRT.Runtime/Interop/IContextCallback.cs +++ b/src/WinRT.Runtime/Interop/IContextCallback.cs @@ -6,40 +6,93 @@ using WinRT; using WinRT.Interop; -#pragma warning disable 0169 // warning CS0169: The field '...' is never used -#pragma warning disable 0649 // warning CS0169: Field '...' is never assigned to +#pragma warning disable CS8500, CS0169 -namespace WinRT.Interop +namespace ABI.WinRT.Interop { - struct ComCallData + internal struct ComCallData { public int dwDispid; public int dwReserved; public IntPtr pUserDefined; - } + } + +#if NET && CsWinRT_LANG_11_FEATURES + internal unsafe struct CallbackData + { + public delegate* Callback; + public object State; + } +#endif + +#if NET && CsWinRT_LANG_11_FEATURES + internal unsafe struct IContextCallbackVftbl + { +#pragma warning disable CS0649 // Native layout + private global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; + private delegate* unmanaged[Stdcall] ContextCallback_4; +#pragma warning restore CS0649 + + public static void ContextCallback(IntPtr contextCallbackPtr, delegate* callback, delegate* onFailCallback, object state) + { + ComCallData comCallData; + comCallData.dwDispid = 0; + comCallData.dwReserved = 0; - unsafe delegate int PFNCONTEXTCALL(ComCallData* data); + CallbackData callbackData; + callbackData.Callback = callback; + callbackData.State = state; - [Guid("000001da-0000-0000-C000-000000000046")] - unsafe interface IContextCallback - { - // The pUnk parameter is intentionally excluded here - // since it is required to always be null. - void ContextCallback( - PFNCONTEXTCALL pfnCallback, - ComCallData* pParam, - Guid riid, - int iMethod); - } -} + // We can just store a pointer to the callback to invoke in the context, + // so we don't need to allocate another closure or anything. The callback + // will be kept alive automatically, because 'comCallData' is address exposed. + // We only do this if we can use C# 11, and if we're on modern .NET, to be safe. + // In the callback below, we can then just retrieve the Action again to invoke it. + comCallData.pUserDefined = (IntPtr)(void*)&callbackData; + + [UnmanagedCallersOnly] + static int InvokeCallback(ComCallData* comCallData) + { + try + { + CallbackData* callbackData = (CallbackData*)comCallData->pUserDefined; + + callbackData->Callback(callbackData->State); + + return 0; // S_OK + } + catch (Exception e) + { + return e.HResult; + } + } + + Guid iid = IID.IID_ICallbackWithNoReentrancyToApplicationSTA; + int hresult = (*(IContextCallbackVftbl**)contextCallbackPtr)->ContextCallback_4( + contextCallbackPtr, + (IntPtr)(delegate* unmanaged)&InvokeCallback, + &comCallData, + &iid, + /* iMethod */ 5, + IntPtr.Zero); + + if (hresult < 0) + { + if (onFailCallback is not null) + { + onFailCallback(state); + } + } + } + } +#else + internal unsafe delegate int PFNCONTEXTCALL(ComCallData* data); -namespace ABI.WinRT.Interop -{ [Guid("000001da-0000-0000-C000-000000000046")] - internal sealed unsafe class IContextCallback : global::WinRT.Interop.IContextCallback + internal sealed unsafe class IContextCallback { - internal static readonly Guid IID = new(0x000001da, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46); + internal static readonly Guid IID = global::WinRT.Interop.IID.IID_IContextCallback; [Guid("000001da-0000-0000-C000-000000000046")] public struct Vftbl @@ -66,18 +119,14 @@ public IContextCallback(ObjectReference obj) _obj = obj; } - private unsafe struct ContextCallData - { - public IntPtr delegateHandle; - public ComCallData* userData; - } - - public unsafe void ContextCallback(global::WinRT.Interop.PFNCONTEXTCALL pfnCallback, ComCallData* pParam, Guid riid, int iMethod) + public unsafe void ContextCallback(PFNCONTEXTCALL pfnCallback, ComCallData* pParam, Guid riid, int iMethod) { var callback = Marshal.GetFunctionPointerForDelegate(pfnCallback); - var result = _obj.Vftbl.ContextCallback_4(ThisPtr, callback, pParam, &riid, iMethod, IntPtr.Zero); + var result = _obj.Vftbl.ContextCallback_4(ThisPtr, callback, pParam, &riid, iMethod, IntPtr.Zero); + GC.KeepAlive(_obj); GC.KeepAlive(pfnCallback); Marshal.ThrowExceptionForHR(result); } } +#endif } \ No newline at end of file diff --git a/src/WinRT.Runtime/Interop/IDelegateVftbl.cs b/src/WinRT.Runtime/Interop/IDelegateVftbl.cs new file mode 100644 index 000000000..cf249cc57 --- /dev/null +++ b/src/WinRT.Runtime/Interop/IDelegateVftbl.cs @@ -0,0 +1,21 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace WinRT.Interop +{ + // This is internal both in the embedded case, but also if we're on modern .NET, + // because projections only use this vftbl type downlevel, and running older + // projections against a newer version of CsWinRT is not supported. +#if EMBED || NET + internal +#else + public +#endif + struct IDelegateVftbl + { + public IUnknownVftbl IUnknownVftbl; + public IntPtr Invoke; + } +} diff --git a/src/WinRT.Runtime/Interop/IID.g.cs b/src/WinRT.Runtime/Interop/IID.g.cs new file mode 100644 index 000000000..d8bb7826a --- /dev/null +++ b/src/WinRT.Runtime/Interop/IID.g.cs @@ -0,0 +1,2793 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace WinRT.Interop +{ + /// IIDs for common COM/WinRT interfaces. +#if EMBED + internal +#else + public +#endif + static class IID + { + /// The IID for IUnknown (00000000-0000-0000-C000-000000000046). + public static ref readonly Guid IID_IUnknown + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0xC0, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x46 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IInspectable (AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90). + public static ref readonly Guid IID_IInspectable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xE0, 0xE2, 0x86, 0xAF, + 0x2D, 0xB1, + 0x6A, 0x4C, + 0x9C, + 0x5A, + 0xD7, + 0xAA, + 0x65, + 0x10, + 0x1E, + 0x90 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IWeakReference (00000037-0000-0000-C000-000000000046). + internal static ref readonly Guid IID_IWeakReference + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x37, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0xC0, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x46 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IWeakReferenceSource (00000038-0000-0000-C000-000000000046). + public static ref readonly Guid IID_IWeakReferenceSource + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x38, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0xC0, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x46 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceTracker (11D3B13A-180E-4789-A8BE-7712882893E6). + internal static ref readonly Guid IID_IReferenceTracker + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x3A, 0xB1, 0xD3, 0x11, + 0x0E, 0x18, + 0x89, 0x47, + 0xA8, + 0xBE, + 0x77, + 0x12, + 0x88, + 0x28, + 0x93, + 0xE6 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceTrackerTarget (64BD43F8-BFEE-4EC4-B7EB-2935158DAE21). + internal static ref readonly Guid IID_IReferenceTrackerTarget + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xF8, 0x43, 0xBD, 0x64, + 0xEE, 0xBF, + 0xC4, 0x4E, + 0xB7, + 0xEB, + 0x29, + 0x35, + 0x15, + 0x8D, + 0xAE, + 0x21 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IActivationFactory (00000035-0000-0000-C000-000000000046). + public static ref readonly Guid IID_IActivationFactory + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x35, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0xC0, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x46 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IAgileObject (94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90). + public static ref readonly Guid IID_IAgileObject + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x94, 0x2B, 0xEA, 0x94, + 0xCC, 0xE9, + 0xE0, 0x49, + 0xC0, + 0xFF, + 0xEE, + 0x64, + 0xCA, + 0x8F, + 0x5B, + 0x90 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IMarshal (00000003-0000-0000-C000-000000000046). + public static ref readonly Guid IID_IMarshal + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x03, 0x00, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0xC0, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x46 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IBuffer (905A0FE0-BC53-11DF-8C49-001E4FC686DA). + public static ref readonly Guid IID_IBuffer + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xE0, 0x0F, 0x5A, 0x90, + 0x53, 0xBC, + 0xDF, 0x11, + 0x8C, + 0x49, + 0x00, + 0x1E, + 0x4F, + 0xC6, + 0x86, + 0xDA + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IBufferByteAccess (905A0FEF-BC53-11DF-8C49-001E4FC686DA). + public static ref readonly Guid IID_IBufferByteAccess + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xEF, 0x0F, 0x5A, 0x90, + 0x53, 0xBC, + 0xDF, 0x11, + 0x8C, + 0x49, + 0x00, + 0x1E, + 0x4F, + 0xC6, + 0x86, + 0xDA + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IMemoryBufferByteAccess (5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D). + public static ref readonly Guid IID_IMemoryBufferByteAccess + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x35, 0x32, 0x0D, 0x5B, + 0xBA, 0x4D, + 0x44, 0x4D, + 0x86, + 0x5E, + 0x8F, + 0x1D, + 0x0E, + 0x4F, + 0xD0, + 0x4D + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IContextCallback (000001DA-0000-0000-C000-000000000046). + internal static ref readonly Guid IID_IContextCallback + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xDA, 0x01, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0xC0, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x46 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for ICallbackWithNoReentrancyToApplicationSTA (0A299774-3E4E-FC42-1D9D-72CEE105CA57). + internal static ref readonly Guid IID_ICallbackWithNoReentrancyToApplicationSTA + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x74, 0x97, 0x29, 0x0A, + 0x4E, 0x3E, + 0x42, 0xFC, + 0x1D, + 0x9D, + 0x72, + 0xCE, + 0xE1, + 0x05, + 0xCA, + 0x57 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IErrorInfo (1CF2B120-547D-101B-8E65-08002B2BD119). + internal static ref readonly Guid IID_IErrorInfo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x20, 0xB1, 0xF2, 0x1C, + 0x7D, 0x54, + 0x1B, 0x10, + 0x8E, + 0x65, + 0x08, + 0x00, + 0x2B, + 0x2B, + 0xD1, + 0x19 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for ISupportErrorInfo (DF0B3D60-548F-101B-8E65-08002B2BD119). + internal static ref readonly Guid IID_ISupportErrorInfo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x60, 0x3D, 0x0B, 0xDF, + 0x8F, 0x54, + 0x1B, 0x10, + 0x8E, + 0x65, + 0x08, + 0x00, + 0x2B, + 0x2B, + 0xD1, + 0x19 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for ILanguageExceptionErrorInfo (04A2DBF3-DF83-116C-0946-0812ABF6E07D). + internal static ref readonly Guid IID_ILanguageExceptionErrorInfo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xF3, 0xDB, 0xA2, 0x04, + 0x83, 0xDF, + 0x6C, 0x11, + 0x09, + 0x46, + 0x08, + 0x12, + 0xAB, + 0xF6, + 0xE0, + 0x7D + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for ILanguageExceptionErrorInfo2 (5746E5C4-5B97-424C-B620-2822915734DD). + internal static ref readonly Guid IID_ILanguageExceptionErrorInfo2 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xC4, 0xE5, 0x46, 0x57, + 0x97, 0x5B, + 0x4C, 0x42, + 0xB6, + 0x20, + 0x28, + 0x22, + 0x91, + 0x57, + 0x34, + 0xDD + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IRestrictedErrorInfo (82BA7092-4C88-427D-A7BC-16DD93FEB67E). + internal static ref readonly Guid IID_IRestrictedErrorInfo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x92, 0x70, 0xBA, 0x82, + 0x88, 0x4C, + 0x7D, 0x42, + 0xA7, + 0xBC, + 0x16, + 0xDD, + 0x93, + 0xFE, + 0xB6, + 0x7E + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for MUX_INotifyPropertyChanged (90B17601-B065-586E-83D9-9ADC3A695284). + internal static ref readonly Guid IID_MUX_INotifyPropertyChanged + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x01, 0x76, 0xB1, 0x90, + 0x65, 0xB0, + 0x6E, 0x58, + 0x83, + 0xD9, + 0x9A, + 0xDC, + 0x3A, + 0x69, + 0x52, + 0x84 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for WUX_INotifyPropertyChanged (CF75D69C-F2F4-486B-B302-BB4C09BAEBFA). + internal static ref readonly Guid IID_WUX_INotifyPropertyChanged + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x9C, 0xD6, 0x75, 0xCF, + 0xF4, 0xF2, + 0x6B, 0x48, + 0xB3, + 0x02, + 0xBB, + 0x4C, + 0x09, + 0xBA, + 0xEB, + 0xFA + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for MUX_INotifyCollectionChanged (530155E1-28A5-5693-87CE-30724D95A06D). + internal static ref readonly Guid IID_MUX_INotifyCollectionChanged + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xE1, 0x55, 0x01, 0x53, + 0xA5, 0x28, + 0x93, 0x56, + 0x87, + 0xCE, + 0x30, + 0x72, + 0x4D, + 0x95, + 0xA0, + 0x6D + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for WUX_INotifyCollectionChanged (28B167D5-1A31-465B-9B25-D5C3AE686C40). + internal static ref readonly Guid IID_WUX_INotifyCollectionChanged + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xD5, 0x67, 0xB1, 0x28, + 0x31, 0x1A, + 0x5B, 0x46, + 0x9B, + 0x25, + 0xD5, + 0xC3, + 0xAE, + 0x68, + 0x6C, + 0x40 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for MUX_INotifyCollectionChangedEventArgsFactory (5108EBA4-4892-5A20-8374-A96815E0FD27). + internal static ref readonly Guid IID_MUX_INotifyCollectionChangedEventArgsFactory + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xA4, 0xEB, 0x08, 0x51, + 0x92, 0x48, + 0x20, 0x5A, + 0x83, + 0x74, + 0xA9, + 0x68, + 0x15, + 0xE0, + 0xFD, + 0x27 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for WUX_INotifyCollectionChangedEventArgsFactory (B30C3E3A-DF8D-44A5-9A38-7AC0D08CE63D). + internal static ref readonly Guid IID_WUX_INotifyCollectionChangedEventArgsFactory + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x3A, 0x3E, 0x0C, 0xB3, + 0x8D, 0xDF, + 0xA5, 0x44, + 0x9A, + 0x38, + 0x7A, + 0xC0, + 0xD0, + 0x8C, + 0xE6, + 0x3D + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for MUX_INotifyCollectionChangedEventArgs (DA049FF2-D2E0-5FE8-8C7B-F87F26060B6F). + internal static ref readonly Guid IID_MUX_INotifyCollectionChangedEventArgs + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xF2, 0x9F, 0x04, 0xDA, + 0xE0, 0xD2, + 0xE8, 0x5F, + 0x8C, + 0x7B, + 0xF8, + 0x7F, + 0x26, + 0x06, + 0x0B, + 0x6F + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for WUX_INotifyCollectionChangedEventArgs (4CF68D33-E3F2-4964-B85E-945B4F7E2F21). + internal static ref readonly Guid IID_WUX_INotifyCollectionChangedEventArgs + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x33, 0x8D, 0xF6, 0x4C, + 0xF2, 0xE3, + 0x64, 0x49, + 0xB8, + 0x5E, + 0x94, + 0x5B, + 0x4F, + 0x7E, + 0x2F, + 0x21 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for MUX_NotifyCollectionChangedEventHandler (8B0909DC-2005-5D93-BF8A-725F017BAA8D). + internal static ref readonly Guid IID_MUX_NotifyCollectionChangedEventHandler + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xDC, 0x09, 0x09, 0x8B, + 0x05, 0x20, + 0x93, 0x5D, + 0xBF, + 0x8A, + 0x72, + 0x5F, + 0x01, + 0x7B, + 0xAA, + 0x8D + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for WUX_NotifyCollectionChangedEventHandler (CA10B37C-F382-4591-8557-5E24965279B0). + internal static ref readonly Guid IID_WUX_NotifyCollectionChangedEventHandler + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x7C, 0xB3, 0x10, 0xCA, + 0x82, 0xF3, + 0x91, 0x45, + 0x85, + 0x57, + 0x5E, + 0x24, + 0x96, + 0x52, + 0x79, + 0xB0 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for MUX_PropertyChangedEventArgsRuntimeClassFactory (7C0C27A8-0B41-5070-B160-FC9AE960A36C). + internal static ref readonly Guid IID_MUX_PropertyChangedEventArgsRuntimeClassFactory + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xA8, 0x27, 0x0C, 0x7C, + 0x41, 0x0B, + 0x70, 0x50, + 0xB1, + 0x60, + 0xFC, + 0x9A, + 0xE9, + 0x60, + 0xA3, + 0x6C + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for WUX_PropertyChangedEventArgsRuntimeClassFactory (6DCC9C03-E0C7-4EEE-8EA9-37E3406EEB1C). + internal static ref readonly Guid IID_WUX_PropertyChangedEventArgsRuntimeClassFactory + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x03, 0x9C, 0xCC, 0x6D, + 0xC7, 0xE0, + 0xEE, 0x4E, + 0x8E, + 0xA9, + 0x37, + 0xE3, + 0x40, + 0x6E, + 0xEB, + 0x1C + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for MUX_PropertyChangedEventHandler (E3DE52F6-1E32-5DA6-BB2D-B5B6096C962D). + internal static ref readonly Guid IID_MUX_PropertyChangedEventHandler + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xF6, 0x52, 0xDE, 0xE3, + 0x32, 0x1E, + 0xA6, 0x5D, + 0xBB, + 0x2D, + 0xB5, + 0xB6, + 0x09, + 0x6C, + 0x96, + 0x2D + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for WUX_PropertyChangedEventHandler (50F19C16-0A22-4D8E-A089-1EA9951657D2). + internal static ref readonly Guid IID_WUX_PropertyChangedEventHandler + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x16, 0x9C, 0xF1, 0x50, + 0x22, 0x0A, + 0x8E, 0x4D, + 0xA0, + 0x89, + 0x1E, + 0xA9, + 0x95, + 0x16, + 0x57, + 0xD2 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for DataErrorsChangedEventArgsRuntimeClassFactory (62D0BD1E-B85F-5FCC-842A-7CB0DDA37FE5). + internal static ref readonly Guid IID_DataErrorsChangedEventArgsRuntimeClassFactory + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x1E, 0xBD, 0xD0, 0x62, + 0x5F, 0xB8, + 0xCC, 0x5F, + 0x84, + 0x2A, + 0x7C, + 0xB0, + 0xDD, + 0xA3, + 0x7F, + 0xE5 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for UriRuntimeClassFactory (44A9796F-723E-4FDF-A218-033E75B0C084). + internal static ref readonly Guid IID_UriRuntimeClassFactory + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x6F, 0x79, 0xA9, 0x44, + 0x3E, 0x72, + 0xDF, 0x4F, + 0xA2, + 0x18, + 0x03, + 0x3E, + 0x75, + 0xB0, + 0xC0, + 0x84 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for INotifyDataErrorInfo (0EE6C2CC-273E-567D-BC0A-1DD87EE51EBA). + internal static ref readonly Guid IID_INotifyDataErrorInfo + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xCC, 0xC2, 0xE6, 0x0E, + 0x3E, 0x27, + 0x7D, 0x56, + 0xBC, + 0x0A, + 0x1D, + 0xD8, + 0x7E, + 0xE5, + 0x1E, + 0xBA + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for ICommand (E5AF3542-CA67-4081-995B-709DD13792DF). + internal static ref readonly Guid IID_ICommand + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x42, 0x35, 0xAF, 0xE5, + 0x67, 0xCA, + 0x81, 0x40, + 0x99, + 0x5B, + 0x70, + 0x9D, + 0xD1, + 0x37, + 0x92, + 0xDF + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IGlobalInterfaceTable (00000146-0000-0000-C000-000000000046). + internal static ref readonly Guid IID_IGlobalInterfaceTable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x46, 0x01, 0x00, 0x00, + 0x00, 0x00, + 0x00, 0x00, + 0xC0, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x46 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for EventHandler (C50898F6-C536-5F47-8583-8B2C2438A13B). + internal static ref readonly Guid IID_EventHandler + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xF6, 0x98, 0x08, 0xC5, + 0x36, 0xC5, + 0x47, 0x5F, + 0x85, + 0x83, + 0x8B, + 0x2C, + 0x24, + 0x38, + 0xA1, + 0x3B + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IBindableVectorView (346DD6E7-976E-4BC3-815D-ECE243BC0F33). + internal static ref readonly Guid IID_IBindableVectorView + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xE7, 0xD6, 0x6D, 0x34, + 0x6E, 0x97, + 0xC3, 0x4B, + 0x81, + 0x5D, + 0xEC, + 0xE2, + 0x43, + 0xBC, + 0x0F, + 0x33 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IEnumerable (036D2C08-DF29-41AF-8AA2-D774BE62BA6F). + internal static ref readonly Guid IID_IEnumerable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x08, 0x2C, 0x6D, 0x03, + 0x29, 0xDF, + 0xAF, 0x41, + 0x8A, + 0xA2, + 0xD7, + 0x74, + 0xBE, + 0x62, + 0xBA, + 0x6F + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IList (393DE7DE-6FD0-4C0D-BB71-47244A113E93). + internal static ref readonly Guid IID_IList + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xDE, 0xE7, 0x3D, 0x39, + 0xD0, 0x6F, + 0x0D, 0x4C, + 0xBB, + 0x71, + 0x47, + 0x24, + 0x4A, + 0x11, + 0x3E, + 0x93 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for ICustomProperty (30DA92C0-23E8-42A0-AE7C-734A0E5D2782). + internal static ref readonly Guid IID_ICustomProperty + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xC0, 0x92, 0xDA, 0x30, + 0xE8, 0x23, + 0xA0, 0x42, + 0xAE, + 0x7C, + 0x73, + 0x4A, + 0x0E, + 0x5D, + 0x27, + 0x82 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for ICustomPropertyProvider (7C925755-3E48-42B4-8677-76372267033F). + internal static ref readonly Guid IID_ICustomPropertyProvider + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x55, 0x57, 0x92, 0x7C, + 0x48, 0x3E, + 0xB4, 0x42, + 0x86, + 0x77, + 0x76, + 0x37, + 0x22, + 0x67, + 0x03, + 0x3F + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IPropertyValue (4BD682DD-7554-40E9-9A9B-82654EDE7E62). + internal static ref readonly Guid IID_IPropertyValue + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xDD, 0x82, 0xD6, 0x4B, + 0x54, 0x75, + 0xE9, 0x40, + 0x9A, + 0x9B, + 0x82, + 0x65, + 0x4E, + 0xDE, + 0x7E, + 0x62 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IDisposable (30D5A829-7FA4-4026-83BB-D75BAE4EA99E). + internal static ref readonly Guid IID_IDisposable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x29, 0xA8, 0xD5, 0x30, + 0xA4, 0x7F, + 0x26, 0x40, + 0x83, + 0xBB, + 0xD7, + 0x5B, + 0xAE, + 0x4E, + 0xA9, + 0x9E + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IStringable (96369F54-8EB6-48F0-ABCE-C1B211E627C3). + internal static ref readonly Guid IID_IStringable + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x54, 0x9F, 0x36, 0x96, + 0xB6, 0x8E, + 0xF0, 0x48, + 0xAB, + 0xCE, + 0xC1, + 0xB2, + 0x11, + 0xE6, + 0x27, + 0xC3 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IServiceProvider (68B3A2DF-8173-539F-B524-C8A2348F5AFB). + internal static ref readonly Guid IID_IServiceProvider + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xDF, 0xA2, 0xB3, 0x68, + 0x73, 0x81, + 0x9F, 0x53, + 0xB5, + 0x24, + 0xC8, + 0xA2, + 0x34, + 0x8F, + 0x5A, + 0xFB + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceOfPoint (84F14C22-A00A-5272-8D3D-82112E66DF00). + internal static ref readonly Guid IID_IReferenceOfPoint + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x22, 0x4C, 0xF1, 0x84, + 0x0A, 0xA0, + 0x72, 0x52, + 0x8D, + 0x3D, + 0x82, + 0x11, + 0x2E, + 0x66, + 0xDF, + 0x00 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceOfSize (61723086-8E53-5276-9F36-2A4BB93E2B75). + internal static ref readonly Guid IID_IReferenceOfSize + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x86, 0x30, 0x72, 0x61, + 0x53, 0x8E, + 0x76, 0x52, + 0x9F, + 0x36, + 0x2A, + 0x4B, + 0xB9, + 0x3E, + 0x2B, + 0x75 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceOfRect (80423F11-054F-5EAC-AFD3-63B6CE15E77B). + internal static ref readonly Guid IID_IReferenceOfRect + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x11, 0x3F, 0x42, 0x80, + 0x4F, 0x05, + 0xAC, 0x5E, + 0xAF, + 0xD3, + 0x63, + 0xB6, + 0xCE, + 0x15, + 0xE7, + 0x7B + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceMatrix3x2 (76358CFD-2CBD-525B-A49E-90EE18247B71). + internal static ref readonly Guid IID_IReferenceMatrix3x2 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xFD, 0x8C, 0x35, 0x76, + 0xBD, 0x2C, + 0x5B, 0x52, + 0xA4, + 0x9E, + 0x90, + 0xEE, + 0x18, + 0x24, + 0x7B, + 0x71 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceMatrix4x4 (DACBFFDC-68EF-5FD0-B657-782D0AC9807E). + internal static ref readonly Guid IID_IReferenceMatrix4x4 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xDC, 0xFF, 0xCB, 0xDA, + 0xEF, 0x68, + 0xD0, 0x5F, + 0xB6, + 0x57, + 0x78, + 0x2D, + 0x0A, + 0xC9, + 0x80, + 0x7E + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferencePlane (46D542A1-52F7-58E7-ACFC-9A6D364DA022). + internal static ref readonly Guid IID_IReferencePlane + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xA1, 0x42, 0xD5, 0x46, + 0xF7, 0x52, + 0xE7, 0x58, + 0xAC, + 0xFC, + 0x9A, + 0x6D, + 0x36, + 0x4D, + 0xA0, + 0x22 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceQuaternion (B27004BB-C014-5DCE-9A21-799C5A3C1461). + internal static ref readonly Guid IID_IReferenceQuaternion + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xBB, 0x04, 0x70, 0xB2, + 0x14, 0xC0, + 0xCE, 0x5D, + 0x9A, + 0x21, + 0x79, + 0x9C, + 0x5A, + 0x3C, + 0x14, + 0x61 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceVector2 (48F6A69E-8465-57AE-9400-9764087F65AD). + internal static ref readonly Guid IID_IReferenceVector2 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x9E, 0xA6, 0xF6, 0x48, + 0x65, 0x84, + 0xAE, 0x57, + 0x94, + 0x00, + 0x97, + 0x64, + 0x08, + 0x7F, + 0x65, + 0xAD + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceVector3 (1EE770FF-C954-59CA-A754-6199A9BE282C). + internal static ref readonly Guid IID_IReferenceVector3 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xFF, 0x70, 0xE7, 0x1E, + 0x54, 0xC9, + 0xCA, 0x59, + 0xA7, + 0x54, + 0x61, + 0x99, + 0xA9, + 0xBE, + 0x28, + 0x2C + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceVector4 (A5E843C9-ED20-5339-8F8D-9FE404CF3654). + internal static ref readonly Guid IID_IReferenceVector4 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xC9, 0x43, 0xE8, 0xA5, + 0x20, 0xED, + 0x39, 0x53, + 0x8F, + 0x8D, + 0x9F, + 0xE4, + 0x04, + 0xCF, + 0x36, + 0x54 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfInt32 (A6D080A5-B087-5BC2-9A9F-5CD687B4D1F7). + internal static ref readonly Guid IID_IReferenceArrayOfInt32 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xA5, 0x80, 0xD0, 0xA6, + 0x87, 0xB0, + 0xC2, 0x5B, + 0x9A, + 0x9F, + 0x5C, + 0xD6, + 0x87, + 0xB4, + 0xD1, + 0xF7 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfString (0385688E-E3C7-5C5E-A389-5524EDE349F1). + internal static ref readonly Guid IID_IReferenceArrayOfString + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x8E, 0x68, 0x85, 0x03, + 0xC7, 0xE3, + 0x5E, 0x5C, + 0xA3, + 0x89, + 0x55, + 0x24, + 0xED, + 0xE3, + 0x49, + 0xF1 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfByte (2AF22683-3734-56D0-A60E-688CC85D1619). + internal static ref readonly Guid IID_IReferenceArrayOfByte + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x83, 0x26, 0xF2, 0x2A, + 0x34, 0x37, + 0xD0, 0x56, + 0xA6, + 0x0E, + 0x68, + 0x8C, + 0xC8, + 0x5D, + 0x16, + 0x19 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfSByte (08DE13EA-BBBF-50DB-A403-578420BB49EF). + internal static ref readonly Guid IID_IReferenceArrayOfSByte + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xEA, 0x13, 0xDE, 0x08, + 0xBF, 0xBB, + 0xDB, 0x50, + 0xA4, + 0x03, + 0x57, + 0x84, + 0x20, + 0xBB, + 0x49, + 0xEF + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfInt16 (912F8FD7-ADC0-5D60-A896-7ED76089CC5B). + internal static ref readonly Guid IID_IReferenceArrayOfInt16 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xD7, 0x8F, 0x2F, 0x91, + 0xC0, 0xAD, + 0x60, 0x5D, + 0xA8, + 0x96, + 0x7E, + 0xD7, + 0x60, + 0x89, + 0xCC, + 0x5B + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfUInt16 (6624A2DD-83F7-519C-9D55-BB1F6560456B). + internal static ref readonly Guid IID_IReferenceArrayOfUInt16 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xDD, 0xA2, 0x24, 0x66, + 0xF7, 0x83, + 0x9C, 0x51, + 0x9D, + 0x55, + 0xBB, + 0x1F, + 0x65, + 0x60, + 0x45, + 0x6B + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfUInt32 (97374B68-EB87-56CC-B18E-27EF0F9CFC0C). + internal static ref readonly Guid IID_IReferenceArrayOfUInt32 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x68, 0x4B, 0x37, 0x97, + 0x87, 0xEB, + 0xCC, 0x56, + 0xB1, + 0x8E, + 0x27, + 0xEF, + 0x0F, + 0x9C, + 0xFC, + 0x0C + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfInt64 (6E333271-2E2A-5955-8790-836C76EE53B6). + internal static ref readonly Guid IID_IReferenceArrayOfInt64 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x71, 0x32, 0x33, 0x6E, + 0x2A, 0x2E, + 0x55, 0x59, + 0x87, + 0x90, + 0x83, + 0x6C, + 0x76, + 0xEE, + 0x53, + 0xB6 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfUInt64 (38B60434-D67C-523E-9D0E-24D643411073). + internal static ref readonly Guid IID_IReferenceArrayOfUInt64 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x34, 0x04, 0xB6, 0x38, + 0x7C, 0xD6, + 0x3E, 0x52, + 0x9D, + 0x0E, + 0x24, + 0xD6, + 0x43, + 0x41, + 0x10, + 0x73 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfSingle (6AB1EA83-CB41-5F99-92CC-23BD4336A1FB). + internal static ref readonly Guid IID_IReferenceArrayOfSingle + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x83, 0xEA, 0xB1, 0x6A, + 0x41, 0xCB, + 0x99, 0x5F, + 0x92, + 0xCC, + 0x23, + 0xBD, + 0x43, + 0x36, + 0xA1, + 0xFB + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfDouble (D301F253-E0A3-5D2B-9A41-A4D62BEC4623). + internal static ref readonly Guid IID_IReferenceArrayOfDouble + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x53, 0xF2, 0x01, 0xD3, + 0xA3, 0xE0, + 0x2B, 0x5D, + 0x9A, + 0x41, + 0xA4, + 0xD6, + 0x2B, + 0xEC, + 0x46, + 0x23 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfChar (A4095AAB-EB7D-5782-8FAD-1609DEA249AD). + internal static ref readonly Guid IID_IReferenceArrayOfChar + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xAB, 0x5A, 0x09, 0xA4, + 0x7D, 0xEB, + 0x82, 0x57, + 0x8F, + 0xAD, + 0x16, + 0x09, + 0xDE, + 0xA2, + 0x49, + 0xAD + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfBoolean (E8E72666-48CC-593F-BA85-2663496956E3). + internal static ref readonly Guid IID_IReferenceArrayOfBoolean + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x66, 0x26, 0xE7, 0xE8, + 0xCC, 0x48, + 0x3F, 0x59, + 0xBA, + 0x85, + 0x26, + 0x63, + 0x49, + 0x69, + 0x56, + 0xE3 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfGuid (EECF9838-C1C2-5B4A-976F-CEC261AE1D55). + internal static ref readonly Guid IID_IReferenceArrayOfGuid + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x38, 0x98, 0xCF, 0xEE, + 0xC2, 0xC1, + 0x4A, 0x5B, + 0x97, + 0x6F, + 0xCE, + 0xC2, + 0x61, + 0xAE, + 0x1D, + 0x55 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfDateTimeOffset (1B8E9594-588E-5A07-9E65-0731A4C9A2DB). + internal static ref readonly Guid IID_IReferenceArrayOfDateTimeOffset + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x94, 0x95, 0x8E, 0x1B, + 0x8E, 0x58, + 0x07, 0x5A, + 0x9E, + 0x65, + 0x07, + 0x31, + 0xA4, + 0xC9, + 0xA2, + 0xDB + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfTimeSpan (AD73197D-2CFA-57A6-8993-9FAC40FEB791). + internal static ref readonly Guid IID_IReferenceArrayOfTimeSpan + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x7D, 0x19, 0x73, 0xAD, + 0xFA, 0x2C, + 0xA6, 0x57, + 0x89, + 0x93, + 0x9F, + 0xAC, + 0x40, + 0xFE, + 0xB7, + 0x91 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfObject (9CD7A84F-0C80-59C5-B44E-977841BB43D9). + internal static ref readonly Guid IID_IReferenceArrayOfObject + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x4F, 0xA8, 0xD7, 0x9C, + 0x80, 0x0C, + 0xC5, 0x59, + 0xB4, + 0x4E, + 0x97, + 0x78, + 0x41, + 0xBB, + 0x43, + 0xD9 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfType (DA8457A7-C2EB-5DA1-80BE-7132A2E1BFA4). + internal static ref readonly Guid IID_IReferenceArrayOfType + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xA7, 0x57, 0x84, 0xDA, + 0xEB, 0xC2, + 0xA1, 0x5D, + 0x80, + 0xBE, + 0x71, + 0x32, + 0xA2, + 0xE1, + 0xBF, + 0xA4 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfPoint (39313214-5C7D-599D-AE5A-17D9D6492258). + internal static ref readonly Guid IID_IReferenceArrayOfPoint + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x14, 0x32, 0x31, 0x39, + 0x7D, 0x5C, + 0x9D, 0x59, + 0xAE, + 0x5A, + 0x17, + 0xD9, + 0xD6, + 0x49, + 0x22, + 0x58 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfSize (3B40E9D4-E0C3-56F6-B88B-E505EB73757B). + internal static ref readonly Guid IID_IReferenceArrayOfSize + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xD4, 0xE9, 0x40, 0x3B, + 0xC3, 0xE0, + 0xF6, 0x56, + 0xB8, + 0x8B, + 0xE5, + 0x05, + 0xEB, + 0x73, + 0x75, + 0x7B + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfRect (8A444256-D661-5E9A-A72B-D8F1D7962D0C). + internal static ref readonly Guid IID_IReferenceArrayOfRect + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x56, 0x42, 0x44, 0x8A, + 0x61, 0xD6, + 0x9A, 0x5E, + 0xA7, + 0x2B, + 0xD8, + 0xF1, + 0xD7, + 0x96, + 0x2D, + 0x0C + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfMatrix3x2 (A525D9FF-C09B-501A-A785-4D1ED9E102B8). + internal static ref readonly Guid IID_IReferenceArrayOfMatrix3x2 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xFF, 0xD9, 0x25, 0xA5, + 0x9B, 0xC0, + 0x1A, 0x50, + 0xA7, + 0x85, + 0x4D, + 0x1E, + 0xD9, + 0xE1, + 0x02, + 0xB8 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfMatrix4x4 (FC0D5A15-8F9D-5E8F-8828-AEF2C2E25BAD). + internal static ref readonly Guid IID_IReferenceArrayOfMatrix4x4 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x15, 0x5A, 0x0D, 0xFC, + 0x9D, 0x8F, + 0x8F, 0x5E, + 0x88, + 0x28, + 0xAE, + 0xF2, + 0xC2, + 0xE2, + 0x5B, + 0xAD + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfPlane (F9CF7F7D-5459-5F98-91B9-F2632A9EC298). + internal static ref readonly Guid IID_IReferenceArrayOfPlane + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x7D, 0x7F, 0xCF, 0xF9, + 0x59, 0x54, + 0x98, 0x5F, + 0x91, + 0xB9, + 0xF2, + 0x63, + 0x2A, + 0x9E, + 0xC2, + 0x98 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfQuaternion (E9BA76BE-2C31-5E1D-98A4-EBDB625AEE93). + internal static ref readonly Guid IID_IReferenceArrayOfQuaternion + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xBE, 0x76, 0xBA, 0xE9, + 0x31, 0x2C, + 0x1D, 0x5E, + 0x98, + 0xA4, + 0xEB, + 0xDB, + 0x62, + 0x5A, + 0xEE, + 0x93 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfVector2 (29DF2178-FFDB-563E-88DB-3869A007305E). + internal static ref readonly Guid IID_IReferenceArrayOfVector2 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x78, 0x21, 0xDF, 0x29, + 0xDB, 0xFF, + 0x3E, 0x56, + 0x88, + 0xDB, + 0x38, + 0x69, + 0xA0, + 0x07, + 0x30, + 0x5E + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfVector3 (AA1A35FA-0B4E-5248-BD79-FFD47CFE4027). + internal static ref readonly Guid IID_IReferenceArrayOfVector3 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xFA, 0x35, 0x1A, 0xAA, + 0x4E, 0x0B, + 0x48, 0x52, + 0xBD, + 0x79, + 0xFF, + 0xD4, + 0x7C, + 0xFE, + 0x40, + 0x27 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfVector4 (68757250-5849-5772-90E3-AADB4C970BFF). + internal static ref readonly Guid IID_IReferenceArrayOfVector4 + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x50, 0x72, 0x75, 0x68, + 0x49, 0x58, + 0x72, 0x57, + 0x90, + 0xE3, + 0xAA, + 0xDB, + 0x4C, + 0x97, + 0x0B, + 0xFF + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for IReferenceArrayOfException (401AE4CC-4AB9-5A8F-B993-E327900C364D). + internal static ref readonly Guid IID_IReferenceArrayOfException + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xCC, 0xE4, 0x1A, 0x40, + 0xB9, 0x4A, + 0x8F, 0x5A, + 0xB9, + 0x93, + 0xE3, + 0x27, + 0x90, + 0x0C, + 0x36, + 0x4D + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableByte (E5198CC8-2873-55F5-B0A1-84FF9E4AAD62). + internal static ref readonly Guid IID_NullableByte + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xC8, 0x8C, 0x19, 0xE5, + 0x73, 0x28, + 0xF5, 0x55, + 0xB0, + 0xA1, + 0x84, + 0xFF, + 0x9E, + 0x4A, + 0xAD, + 0x62 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableSByte (95500129-FBF6-5AFC-89DF-70642D741990). + internal static ref readonly Guid IID_NullableSByte + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x29, 0x01, 0x50, 0x95, + 0xF6, 0xFB, + 0xFC, 0x5A, + 0x89, + 0xDF, + 0x70, + 0x64, + 0x2D, + 0x74, + 0x19, + 0x90 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableShort (6EC9E41B-6709-5647-9918-A1270110FC4E). + internal static ref readonly Guid IID_NullableShort + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x1B, 0xE4, 0xC9, 0x6E, + 0x09, 0x67, + 0x47, 0x56, + 0x99, + 0x18, + 0xA1, + 0x27, + 0x01, + 0x10, + 0xFC, + 0x4E + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableUShort (5AB7D2C3-6B62-5E71-A4B6-2D49C4F238FD). + internal static ref readonly Guid IID_NullableUShort + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xC3, 0xD2, 0xB7, 0x5A, + 0x62, 0x6B, + 0x71, 0x5E, + 0xA4, + 0xB6, + 0x2D, + 0x49, + 0xC4, + 0xF2, + 0x38, + 0xFD + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableInt (548CEFBD-BC8A-5FA0-8DF2-957440FC8BF4). + internal static ref readonly Guid IID_NullableInt + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xBD, 0xEF, 0x8C, 0x54, + 0x8A, 0xBC, + 0xA0, 0x5F, + 0x8D, + 0xF2, + 0x95, + 0x74, + 0x40, + 0xFC, + 0x8B, + 0xF4 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableUInt (513EF3AF-E784-5325-A91E-97C2B8111CF3). + internal static ref readonly Guid IID_NullableUInt + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xAF, 0xF3, 0x3E, 0x51, + 0x84, 0xE7, + 0x25, 0x53, + 0xA9, + 0x1E, + 0x97, + 0xC2, + 0xB8, + 0x11, + 0x1C, + 0xF3 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableLong (4DDA9E24-E69F-5C6A-A0A6-93427365AF2A). + internal static ref readonly Guid IID_NullableLong + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x24, 0x9E, 0xDA, 0x4D, + 0x9F, 0xE6, + 0x6A, 0x5C, + 0xA0, + 0xA6, + 0x93, + 0x42, + 0x73, + 0x65, + 0xAF, + 0x2A + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableULong (6755E376-53BB-568B-A11D-17239868309E). + internal static ref readonly Guid IID_NullableULong + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x76, 0xE3, 0x55, 0x67, + 0xBB, 0x53, + 0x8B, 0x56, + 0xA1, + 0x1D, + 0x17, + 0x23, + 0x98, + 0x68, + 0x30, + 0x9E + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableFloat (719CC2BA-3E76-5DEF-9F1A-38D85A145EA8). + internal static ref readonly Guid IID_NullableFloat + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xBA, 0xC2, 0x9C, 0x71, + 0x76, 0x3E, + 0xEF, 0x5D, + 0x9F, + 0x1A, + 0x38, + 0xD8, + 0x5A, + 0x14, + 0x5E, + 0xA8 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableDouble (2F2D6C29-5473-5F3E-92E7-96572BB990E2). + internal static ref readonly Guid IID_NullableDouble + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x29, 0x6C, 0x2D, 0x2F, + 0x73, 0x54, + 0x3E, 0x5F, + 0x92, + 0xE7, + 0x96, + 0x57, + 0x2B, + 0xB9, + 0x90, + 0xE2 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableChar (FB393EF3-BBAC-5BD5-9144-84F23576F415). + internal static ref readonly Guid IID_NullableChar + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xF3, 0x3E, 0x39, 0xFB, + 0xAC, 0xBB, + 0xD5, 0x5B, + 0x91, + 0x44, + 0x84, + 0xF2, + 0x35, + 0x76, + 0xF4, + 0x15 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableBool (3C00FD60-2950-5939-A21A-2D12C5A01B8A). + internal static ref readonly Guid IID_NullableBool + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x60, 0xFD, 0x00, 0x3C, + 0x50, 0x29, + 0x39, 0x59, + 0xA2, + 0x1A, + 0x2D, + 0x12, + 0xC5, + 0xA0, + 0x1B, + 0x8A + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableGuid (7D50F649-632C-51F9-849A-EE49428933EA). + internal static ref readonly Guid IID_NullableGuid + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x49, 0xF6, 0x50, 0x7D, + 0x2C, 0x63, + 0xF9, 0x51, + 0x84, + 0x9A, + 0xEE, + 0x49, + 0x42, + 0x89, + 0x33, + 0xEA + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableDateTimeOffset (5541D8A7-497C-5AA4-86FC-7713ADBF2A2C). + internal static ref readonly Guid IID_NullableDateTimeOffset + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xA7, 0xD8, 0x41, 0x55, + 0x7C, 0x49, + 0xA4, 0x5A, + 0x86, + 0xFC, + 0x77, + 0x13, + 0xAD, + 0xBF, + 0x2A, + 0x2C + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableTimeSpan (604D0C4C-91DE-5C2A-935F-362F13EAF800). + internal static ref readonly Guid IID_NullableTimeSpan + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x4C, 0x0C, 0x4D, 0x60, + 0xDE, 0x91, + 0x2A, 0x5C, + 0x93, + 0x5F, + 0x36, + 0x2F, + 0x13, + 0xEA, + 0xF8, + 0x00 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableObject (06DCCC90-A058-5C88-87B7-6F3360A2FC16). + internal static ref readonly Guid IID_NullableObject + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x90, 0xCC, 0xDC, 0x06, + 0x58, 0xA0, + 0x88, 0x5C, + 0x87, + 0xB7, + 0x6F, + 0x33, + 0x60, + 0xA2, + 0xFC, + 0x16 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableType (3830AD99-D8DA-53F3-989B-FC92AD222778). + internal static ref readonly Guid IID_NullableType + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x99, 0xAD, 0x30, 0x38, + 0xDA, 0xD8, + 0xF3, 0x53, + 0x98, + 0x9B, + 0xFC, + 0x92, + 0xAD, + 0x22, + 0x27, + 0x78 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableException (6FF27A1E-4B6A-59B7-B2C3-D1F2EE474593). + internal static ref readonly Guid IID_NullableException + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x1E, 0x7A, 0xF2, 0x6F, + 0x6A, 0x4B, + 0xB7, 0x59, + 0xB2, + 0xC3, + 0xD1, + 0xF2, + 0xEE, + 0x47, + 0x45, + 0x93 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableEventHandler (25230F05-B49C-57EE-8961-5373D98E1AB1). + internal static ref readonly Guid IID_NullableEventHandler + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x05, 0x0F, 0x23, 0x25, + 0x9C, 0xB4, + 0xEE, 0x57, + 0x89, + 0x61, + 0x53, + 0x73, + 0xD9, + 0x8E, + 0x1A, + 0xB1 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for NullableString (FD416DFB-2A07-52EB-AAE3-DFCE14116C05). + internal static ref readonly Guid IID_NullableString + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xFB, 0x6D, 0x41, 0xFD, + 0x07, 0x2A, + 0xEB, 0x52, + 0xAA, + 0xE3, + 0xDF, + 0xCE, + 0x14, + 0x11, + 0x6C, + 0x05 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for MUX_NullablePropertyChangedEventHandler (1EEAE0CB-8F57-5C37-A087-A55d46E2FE3F). + internal static ref readonly Guid IID_MUX_NullablePropertyChangedEventHandler + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xCB, 0xE0, 0xEA, 0x1E, + 0x57, 0x8F, + 0x37, 0x5C, + 0xA0, + 0x87, + 0xA5, + 0x5D, + 0x46, + 0xE2, + 0xFE, + 0x3F + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for WUX_NullablePropertyChangedEventHandler (B1A920A9-C2F2-5453-A53E-66B1294A8BFE). + internal static ref readonly Guid IID_WUX_NullablePropertyChangedEventHandler + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0xA9, 0x20, 0xA9, 0xB1, + 0xF2, 0xC2, + 0x53, 0x54, + 0xA5, + 0x3E, + 0x66, + 0xB1, + 0x29, + 0x4A, + 0x8B, + 0xFE + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for MUX_NullableNotifyCollectionChangedEventHandler (779D5A21-0E7d-5476-BB90-27FA3B4B8DE5). + internal static ref readonly Guid IID_MUX_NullableNotifyCollectionChangedEventHandler + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x21, 0x5A, 0x9D, 0x77, + 0x7D, 0x0E, + 0x76, 0x54, + 0xBB, + 0x90, + 0x27, + 0xFA, + 0x3B, + 0x4B, + 0x8D, + 0xE5 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + + /// The IID for WUX_NullableNotifyCollectionChangedEventHandler (A4FD5C6E-6549-59A9-86EF-5A490A1875D9). + internal static ref readonly Guid IID_WUX_NullableNotifyCollectionChangedEventHandler + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + 0x6E, 0x5C, 0xFD, 0xA4, + 0x49, 0x65, + 0xA9, 0x59, + 0x86, + 0xEF, + 0x5A, + 0x49, + 0x0A, + 0x18, + 0x75, + 0xD9 + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime/Interop/IID.tt b/src/WinRT.Runtime/Interop/IID.tt new file mode 100644 index 000000000..ba2fbad57 --- /dev/null +++ b/src/WinRT.Runtime/Interop/IID.tt @@ -0,0 +1,174 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ output extension=".g.cs"#> +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace WinRT.Interop +{ + /// IIDs for common COM/WinRT interfaces. +#if EMBED + internal +#else + public +#endif + static class IID + { +<# +var entries = new (string Name, string IID, bool IsPublic)[] +{ + ("IUnknown", "00000000-0000-0000-C000-000000000046", true), + ("IInspectable", "AF86E2E0-B12D-4C6A-9C5A-D7AA65101E90", true), + ("IWeakReference", "00000037-0000-0000-C000-000000000046", false), + ("IWeakReferenceSource", "00000038-0000-0000-C000-000000000046", true), + ("IReferenceTracker", "11D3B13A-180E-4789-A8BE-7712882893E6", false), + ("IReferenceTrackerTarget", "64BD43F8-BFEE-4EC4-B7EB-2935158DAE21", false), + ("IActivationFactory", "00000035-0000-0000-C000-000000000046", true), + ("IAgileObject", "94EA2B94-E9CC-49E0-C0FF-EE64CA8F5B90", true), + ("IMarshal", "00000003-0000-0000-C000-000000000046", true), + ("IBuffer", "905A0FE0-BC53-11DF-8C49-001E4FC686DA", true), + ("IBufferByteAccess", "905A0FEF-BC53-11DF-8C49-001E4FC686DA", true), + ("IMemoryBufferByteAccess", "5B0D3235-4DBA-4D44-865E-8F1D0E4FD04D", true), + ("IContextCallback", "000001DA-0000-0000-C000-000000000046", false), + ("ICallbackWithNoReentrancyToApplicationSTA", "0A299774-3E4E-FC42-1D9D-72CEE105CA57", false), + ("IErrorInfo", "1CF2B120-547D-101B-8E65-08002B2BD119", false), + ("ISupportErrorInfo", "DF0B3D60-548F-101B-8E65-08002B2BD119", false), + ("ILanguageExceptionErrorInfo", "04A2DBF3-DF83-116C-0946-0812ABF6E07D", false), + ("ILanguageExceptionErrorInfo2", "5746E5C4-5B97-424C-B620-2822915734DD", false), + ("IRestrictedErrorInfo", "82BA7092-4C88-427D-A7BC-16DD93FEB67E", false), + ("MUX_INotifyPropertyChanged", "90B17601-B065-586E-83D9-9ADC3A695284", false), + ("WUX_INotifyPropertyChanged", "CF75D69C-F2F4-486B-B302-BB4C09BAEBFA", false), + ("MUX_INotifyCollectionChanged", "530155E1-28A5-5693-87CE-30724D95A06D", false), + ("WUX_INotifyCollectionChanged", "28B167D5-1A31-465B-9B25-D5C3AE686C40", false), + ("MUX_INotifyCollectionChangedEventArgsFactory", "5108EBA4-4892-5A20-8374-A96815E0FD27", false), + ("WUX_INotifyCollectionChangedEventArgsFactory", "B30C3E3A-DF8D-44A5-9A38-7AC0D08CE63D", false), + ("MUX_INotifyCollectionChangedEventArgs", "DA049FF2-D2E0-5FE8-8C7B-F87F26060B6F", false), + ("WUX_INotifyCollectionChangedEventArgs", "4CF68D33-E3F2-4964-B85E-945B4F7E2F21", false), + ("MUX_NotifyCollectionChangedEventHandler", "8B0909DC-2005-5D93-BF8A-725F017BAA8D", false), + ("WUX_NotifyCollectionChangedEventHandler", "CA10B37C-F382-4591-8557-5E24965279B0", false), + ("MUX_PropertyChangedEventArgsRuntimeClassFactory", "7C0C27A8-0B41-5070-B160-FC9AE960A36C", false), + ("WUX_PropertyChangedEventArgsRuntimeClassFactory", "6DCC9C03-E0C7-4EEE-8EA9-37E3406EEB1C", false), + ("MUX_PropertyChangedEventHandler", "E3DE52F6-1E32-5DA6-BB2D-B5B6096C962D", false), + ("WUX_PropertyChangedEventHandler", "50F19C16-0A22-4D8E-A089-1EA9951657D2", false), + ("DataErrorsChangedEventArgsRuntimeClassFactory", "62D0BD1E-B85F-5FCC-842A-7CB0DDA37FE5", false), + ("UriRuntimeClassFactory", "44A9796F-723E-4FDF-A218-033E75B0C084", false), + ("INotifyDataErrorInfo", "0EE6C2CC-273E-567D-BC0A-1DD87EE51EBA", false), + ("ICommand", "E5AF3542-CA67-4081-995B-709DD13792DF", false), + ("IGlobalInterfaceTable", "00000146-0000-0000-C000-000000000046", false), + ("EventHandler", "C50898F6-C536-5F47-8583-8B2C2438A13B", false), + ("IBindableVectorView", "346DD6E7-976E-4BC3-815D-ECE243BC0F33", false), + ("IEnumerable", "036D2C08-DF29-41AF-8AA2-D774BE62BA6F", false), + ("IList", "393DE7DE-6FD0-4C0D-BB71-47244A113E93", false), + ("ICustomProperty", "30DA92C0-23E8-42A0-AE7C-734A0E5D2782", false), + ("ICustomPropertyProvider", "7C925755-3E48-42B4-8677-76372267033F", false), + ("IPropertyValue", "4BD682DD-7554-40E9-9A9B-82654EDE7E62", false), + ("IDisposable", "30D5A829-7FA4-4026-83BB-D75BAE4EA99E", false), + ("IStringable", "96369F54-8EB6-48F0-ABCE-C1B211E627C3", false), + ("IServiceProvider", "68B3A2DF-8173-539F-B524-C8A2348F5AFB", false), + ("IReferenceOfPoint", "84F14C22-A00A-5272-8D3D-82112E66DF00", false), + ("IReferenceOfSize", "61723086-8E53-5276-9F36-2A4BB93E2B75", false), + ("IReferenceOfRect", "80423F11-054F-5EAC-AFD3-63B6CE15E77B", false), + ("IReferenceMatrix3x2", "76358CFD-2CBD-525B-A49E-90EE18247B71", false), + ("IReferenceMatrix4x4", "DACBFFDC-68EF-5FD0-B657-782D0AC9807E", false), + ("IReferencePlane", "46D542A1-52F7-58E7-ACFC-9A6D364DA022", false), + ("IReferenceQuaternion", "B27004BB-C014-5DCE-9A21-799C5A3C1461", false), + ("IReferenceVector2", "48F6A69E-8465-57AE-9400-9764087F65AD", false), + ("IReferenceVector3", "1EE770FF-C954-59CA-A754-6199A9BE282C", false), + ("IReferenceVector4", "A5E843C9-ED20-5339-8F8D-9FE404CF3654", false), + ("IReferenceArrayOfInt32", "A6D080A5-B087-5BC2-9A9F-5CD687B4D1F7", false), + ("IReferenceArrayOfString", "0385688E-E3C7-5C5E-A389-5524EDE349F1", false), + ("IReferenceArrayOfByte", "2AF22683-3734-56D0-A60E-688CC85D1619", false), + ("IReferenceArrayOfSByte", "08DE13EA-BBBF-50DB-A403-578420BB49EF", false), + ("IReferenceArrayOfInt16", "912F8FD7-ADC0-5D60-A896-7ED76089CC5B", false), + ("IReferenceArrayOfUInt16", "6624A2DD-83F7-519C-9D55-BB1F6560456B", false), + ("IReferenceArrayOfUInt32", "97374B68-EB87-56CC-B18E-27EF0F9CFC0C", false), + ("IReferenceArrayOfInt64", "6E333271-2E2A-5955-8790-836C76EE53B6", false), + ("IReferenceArrayOfUInt64", "38B60434-D67C-523E-9D0E-24D643411073", false), + ("IReferenceArrayOfSingle", "6AB1EA83-CB41-5F99-92CC-23BD4336A1FB", false), + ("IReferenceArrayOfDouble", "D301F253-E0A3-5D2B-9A41-A4D62BEC4623", false), + ("IReferenceArrayOfChar", "A4095AAB-EB7D-5782-8FAD-1609DEA249AD", false), + ("IReferenceArrayOfBoolean", "E8E72666-48CC-593F-BA85-2663496956E3", false), + ("IReferenceArrayOfGuid", "EECF9838-C1C2-5B4A-976F-CEC261AE1D55", false), + ("IReferenceArrayOfDateTimeOffset", "1B8E9594-588E-5A07-9E65-0731A4C9A2DB", false), + ("IReferenceArrayOfTimeSpan", "AD73197D-2CFA-57A6-8993-9FAC40FEB791", false), + ("IReferenceArrayOfObject", "9CD7A84F-0C80-59C5-B44E-977841BB43D9", false), + ("IReferenceArrayOfType", "DA8457A7-C2EB-5DA1-80BE-7132A2E1BFA4", false), + ("IReferenceArrayOfPoint", "39313214-5C7D-599D-AE5A-17D9D6492258", false), + ("IReferenceArrayOfSize", "3B40E9D4-E0C3-56F6-B88B-E505EB73757B", false), + ("IReferenceArrayOfRect", "8A444256-D661-5E9A-A72B-D8F1D7962D0C", false), + ("IReferenceArrayOfMatrix3x2", "A525D9FF-C09B-501A-A785-4D1ED9E102B8", false), + ("IReferenceArrayOfMatrix4x4", "FC0D5A15-8F9D-5E8F-8828-AEF2C2E25BAD", false), + ("IReferenceArrayOfPlane", "F9CF7F7D-5459-5F98-91B9-F2632A9EC298", false), + ("IReferenceArrayOfQuaternion", "E9BA76BE-2C31-5E1D-98A4-EBDB625AEE93", false), + ("IReferenceArrayOfVector2", "29DF2178-FFDB-563E-88DB-3869A007305E", false), + ("IReferenceArrayOfVector3", "AA1A35FA-0B4E-5248-BD79-FFD47CFE4027", false), + ("IReferenceArrayOfVector4", "68757250-5849-5772-90E3-AADB4C970BFF", false), + ("IReferenceArrayOfException", "401AE4CC-4AB9-5A8F-B993-E327900C364D", false), + ("NullableByte", "E5198CC8-2873-55F5-B0A1-84FF9E4AAD62", false), + ("NullableSByte", "95500129-FBF6-5AFC-89DF-70642D741990", false), + ("NullableShort", "6EC9E41B-6709-5647-9918-A1270110FC4E", false), + ("NullableUShort", "5AB7D2C3-6B62-5E71-A4B6-2D49C4F238FD", false), + ("NullableInt", "548CEFBD-BC8A-5FA0-8DF2-957440FC8BF4", false), + ("NullableUInt", "513EF3AF-E784-5325-A91E-97C2B8111CF3", false), + ("NullableLong", "4DDA9E24-E69F-5C6A-A0A6-93427365AF2A", false), + ("NullableULong", "6755E376-53BB-568B-A11D-17239868309E", false), + ("NullableFloat", "719CC2BA-3E76-5DEF-9F1A-38D85A145EA8", false), + ("NullableDouble", "2F2D6C29-5473-5F3E-92E7-96572BB990E2", false), + ("NullableChar", "FB393EF3-BBAC-5BD5-9144-84F23576F415", false), + ("NullableBool", "3C00FD60-2950-5939-A21A-2D12C5A01B8A", false), + ("NullableGuid", "7D50F649-632C-51F9-849A-EE49428933EA", false), + ("NullableDateTimeOffset", "5541D8A7-497C-5AA4-86FC-7713ADBF2A2C", false), + ("NullableTimeSpan", "604D0C4C-91DE-5C2A-935F-362F13EAF800", false), + ("NullableObject", "06DCCC90-A058-5C88-87B7-6F3360A2FC16", false), + ("NullableType", "3830AD99-D8DA-53F3-989B-FC92AD222778", false), + ("NullableException", "6FF27A1E-4B6A-59B7-B2C3-D1F2EE474593", false), + ("NullableEventHandler", "25230F05-B49C-57EE-8961-5373D98E1AB1", false), + ("NullableString", "FD416DFB-2A07-52EB-AAE3-DFCE14116C05", false), + ("MUX_NullablePropertyChangedEventHandler", "1EEAE0CB-8F57-5C37-A087-A55d46E2FE3F", false), + ("WUX_NullablePropertyChangedEventHandler", "B1A920A9-C2F2-5453-A53E-66B1294A8BFE", false), + ("MUX_NullableNotifyCollectionChangedEventHandler", "779D5A21-0E7d-5476-BB90-27FA3B4B8DE5", false), + ("WUX_NullableNotifyCollectionChangedEventHandler", "A4FD5C6E-6549-59A9-86EF-5A490A1875D9", false), +}; + +for (int i = 0; i < entries.Length; i++) +{ + if (i > 0) WriteLine(""); + + var bytes = new Guid(entries[i].IID).ToByteArray(); + var hex = bytes.Select(b => $"0x{b.ToString("X2").ToUpperInvariant()}").ToArray(); + var modifier = entries[i].IsPublic ? "public" : "internal"; +#> + /// The IID for <#=entries[i].Name#> (<#=entries[i].IID#>). + <#=modifier#> static ref readonly Guid IID_<#=entries[i].Name#> + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + ReadOnlySpan data = new byte[] + { + <#=hex[0]#>, <#=hex[1]#>, <#=hex[2]#>, <#=hex[3]#>, + <#=hex[4]#>, <#=hex[5]#>, + <#=hex[6]#>, <#=hex[7]#>, + <#=hex[8]#>, + <#=hex[9]#>, + <#=hex[10]#>, + <#=hex[11]#>, + <#=hex[12]#>, + <#=hex[13]#>, + <#=hex[14]#>, + <#=hex[15]#> + }; + + return ref Unsafe.As(ref MemoryMarshal.GetReference(data)); + } + } +<# +} +#> + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime/Interop/IMarshal.net5.cs b/src/WinRT.Runtime/Interop/IMarshal.net5.cs new file mode 100644 index 000000000..2f557459a --- /dev/null +++ b/src/WinRT.Runtime/Interop/IMarshal.net5.cs @@ -0,0 +1,257 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using WinRT; +using WinRT.Interop; + +namespace WinRT.Interop +{ + internal enum MSHCTX : int { Local = 0, NoSharedMem = 1, DifferentMachine = 2, InProc = 3, CrossCtx = 4 } + + internal enum MSHLFLAGS : int { Normal = 0, TableStrong = 1, TableWeak = 2, NoPing = 4 } +} + +namespace ABI.WinRT.Interop +{ + [Guid("00000003-0000-0000-c000-000000000046")] + internal sealed class IMarshal + { + private const string NotImplemented_NativeRoutineNotFound = "A native library routine was not found: {0}."; + + private static readonly object _IID_InProcFreeThreadedMarshalerLock = new(); + internal static volatile object _IID_InProcFreeThreadedMarshaler; + + // Simple singleton lazy-initialization scheme (and saving the Lazy size) + internal static Guid IID_InProcFreeThreadedMarshaler + { + get + { + object iid = _IID_InProcFreeThreadedMarshaler; + + if (iid is not null) + { + return (Guid)iid; + } + + return IID_InProcFreeThreadedMarshaler_Slow(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static Guid IID_InProcFreeThreadedMarshaler_Slow() + { + lock (_IID_InProcFreeThreadedMarshalerLock) + { + return (Guid)(_IID_InProcFreeThreadedMarshaler ??= Vftbl.GetInProcFreeThreadedMarshalerIID()); + } + } + } + } + + [Guid("00000003-0000-0000-c000-000000000046")] + public unsafe struct Vftbl + { + internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; + public delegate* unmanaged[Stdcall] GetUnmarshalClass_0; + public delegate* unmanaged[Stdcall] GetMarshalSizeMax_1; + public delegate* unmanaged[Stdcall] MarshalInterface_2; + public delegate* unmanaged[Stdcall] UnmarshalInterface_3; + public delegate* unmanaged[Stdcall] ReleaseMarshalData_4; + public delegate* unmanaged[Stdcall] DisconnectObject_5; + + public static readonly IntPtr AbiToProjectionVftablePtr; + + static Vftbl() + { + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.Interop.IUnknownVftbl) + sizeof(IntPtr) * 6); + (*(Vftbl*)AbiToProjectionVftablePtr) = new Vftbl + { + IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, + GetUnmarshalClass_0 = &Do_Abi_GetUnmarshalClass_0, + GetMarshalSizeMax_1 = &Do_Abi_GetMarshalSizeMax_1, + MarshalInterface_2 = &Do_Abi_MarshalInterface_2, + UnmarshalInterface_3 = &Do_Abi_UnmarshalInterface_3, + ReleaseMarshalData_4 = &Do_Abi_ReleaseMarshalData_4, + DisconnectObject_5 = &Do_Abi_DisconnectObject_5 + }; + } + + // This object handles IMarshal calls for us for most scenarios. + [ThreadStatic] + private static IMarshal t_freeThreadedMarshaler; + + private static void EnsureHasFreeThreadedMarshaler() + { + if (t_freeThreadedMarshaler != null) + return; + + try + { + IntPtr proxyPtr; + Marshal.ThrowExceptionForHR(Platform.CoCreateFreeThreadedMarshaler(IntPtr.Zero, &proxyPtr)); + using var objRef = ObjectReference.Attach(ref proxyPtr, global::WinRT.Interop.IID.IID_IUnknown); + IMarshal proxy = new IMarshal(objRef); + t_freeThreadedMarshaler = proxy; + } + catch (DllNotFoundException ex) + { + throw new NotImplementedException(string.Format(NotImplemented_NativeRoutineNotFound, "CoCreateFreeThreadedMarshaler"), ex); + } + } + + internal static Guid GetInProcFreeThreadedMarshalerIID() + { + EnsureHasFreeThreadedMarshaler(); + + Guid iid_IUnknown = IID.IID_IUnknown; + Guid iid_unmarshalClass; + t_freeThreadedMarshaler.GetUnmarshalClass(&iid_IUnknown, IntPtr.Zero, MSHCTX.InProc, IntPtr.Zero, MSHLFLAGS.Normal, &iid_unmarshalClass); + return iid_unmarshalClass; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static int Do_Abi_GetUnmarshalClass_0(IntPtr thisPtr, Guid* riid, IntPtr pv, global::WinRT.Interop.MSHCTX dwDestContext, IntPtr pvDestContext, global::WinRT.Interop.MSHLFLAGS mshlFlags, Guid* pCid) + { + *pCid = default; + try + { + EnsureHasFreeThreadedMarshaler(); + t_freeThreadedMarshaler.GetUnmarshalClass(riid, pv, dwDestContext, pvDestContext, mshlFlags, pCid); + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + return 0; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static int Do_Abi_GetMarshalSizeMax_1(IntPtr thisPtr, Guid* riid, IntPtr pv, global::WinRT.Interop.MSHCTX dwDestContext, IntPtr pvDestContext, global::WinRT.Interop.MSHLFLAGS mshlflags, uint* pSize) + { + *pSize = default; + try + { + EnsureHasFreeThreadedMarshaler(); + t_freeThreadedMarshaler.GetMarshalSizeMax(riid, pv, dwDestContext, pvDestContext, mshlflags, pSize); + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + return 0; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static int Do_Abi_MarshalInterface_2(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr pv, global::WinRT.Interop.MSHCTX dwDestContext, IntPtr pvDestContext, global::WinRT.Interop.MSHLFLAGS mshlflags) + { + try + { + EnsureHasFreeThreadedMarshaler(); + t_freeThreadedMarshaler.MarshalInterface(pStm, riid, pv, dwDestContext, pvDestContext, mshlflags); + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + return 0; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static int Do_Abi_UnmarshalInterface_3(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr* ppv) + { + *ppv = default; + try + { + EnsureHasFreeThreadedMarshaler(); + t_freeThreadedMarshaler.UnmarshalInterface(pStm, riid, ppv); + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + return 0; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static int Do_Abi_ReleaseMarshalData_4(IntPtr thisPtr, IntPtr pStm) + { + try + { + EnsureHasFreeThreadedMarshaler(); + t_freeThreadedMarshaler.ReleaseMarshalData(pStm); + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + return 0; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static int Do_Abi_DisconnectObject_5(IntPtr thisPtr, uint dwReserved) + { + try + { + EnsureHasFreeThreadedMarshaler(); + t_freeThreadedMarshaler.DisconnectObject(dwReserved); + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + return 0; + } + } + + private readonly ObjectReference _obj; + public IObjectReference ObjRef { get => _obj; } + public IntPtr ThisPtr => _obj.ThisPtr; + public IMarshal(IObjectReference obj) + { + _obj = obj.As(IID.IID_IMarshal); + } + + public unsafe void GetUnmarshalClass(Guid* riid, IntPtr pv, MSHCTX dwDestContext, IntPtr pvDestContext, MSHLFLAGS mshlFlags, Guid* pCid) + { + IntPtr thisPtr = ThisPtr; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[3])(thisPtr, riid, pv, dwDestContext, pvDestContext, mshlFlags, pCid)); + GC.KeepAlive(_obj); + } + + public unsafe void GetMarshalSizeMax(Guid* riid, IntPtr pv, MSHCTX dwDestContext, IntPtr pvDestContext, MSHLFLAGS mshlflags, uint* pSize) + { + IntPtr thisPtr = ThisPtr; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[4])(thisPtr, riid, pv, dwDestContext, pvDestContext, mshlflags, pSize)); + GC.KeepAlive(_obj); + } + + public unsafe void MarshalInterface(IntPtr pStm, Guid* riid, IntPtr pv, MSHCTX dwDestContext, IntPtr pvDestContext, MSHLFLAGS mshlflags) + { + IntPtr thisPtr = ThisPtr; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[5])(thisPtr, pStm, riid, pv, dwDestContext, pvDestContext, mshlflags)); + GC.KeepAlive(_obj); + } + + public unsafe void UnmarshalInterface(IntPtr pStm, Guid* riid, IntPtr* ppv) + { + IntPtr thisPtr = ThisPtr; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[6])(thisPtr, pStm, riid, ppv)); + GC.KeepAlive(_obj); + } + + public unsafe void ReleaseMarshalData(IntPtr pStm) + { + IntPtr thisPtr = ThisPtr; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[7])(thisPtr, pStm)); + GC.KeepAlive(_obj); + } + + public unsafe void DisconnectObject(uint dwReserved) + { + IntPtr thisPtr = ThisPtr; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[8])(thisPtr, dwReserved)); + GC.KeepAlive(_obj); + } + } +} \ No newline at end of file diff --git a/src/WinRT.Runtime/Interop/IMarshal.cs b/src/WinRT.Runtime/Interop/IMarshal.netstandard2.0.cs similarity index 88% rename from src/WinRT.Runtime/Interop/IMarshal.cs rename to src/WinRT.Runtime/Interop/IMarshal.netstandard2.0.cs index dbd421014..f7b30817f 100644 --- a/src/WinRT.Runtime/Interop/IMarshal.cs +++ b/src/WinRT.Runtime/Interop/IMarshal.netstandard2.0.cs @@ -1,19 +1,19 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using WinRT; -using WinRT.Interop; - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using WinRT; +using WinRT.Interop; + namespace WinRT.Interop -{ +{ internal enum MSHCTX : int { Local = 0, NoSharedMem = 1, DifferentMachine = 2, InProc = 3, CrossCtx = 4 } internal enum MSHLFLAGS : int { Normal = 0, TableStrong = 1, TableWeak = 2, NoPing = 4 } [global::WinRT.WindowsRuntimeType("Windows.Foundation.UniversalApiContract")] - [Guid("00000003-0000-0000-c000-000000000046")] + [Guid("00000003-0000-0000-c000-000000000046")] [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.WinRT.Interop.IMarshal))] internal interface IMarshal { @@ -32,25 +32,48 @@ internal interface IMarshal } namespace ABI.WinRT.Interop -{ +{ [Guid("00000003-0000-0000-c000-000000000046")] internal sealed class IMarshal - { - internal static readonly Guid IID = new(0x00000003, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46); + { + internal static readonly Guid IID = global::WinRT.Interop.IID.IID_IMarshal; - [DllImport("api-ms-win-core-com-l1-1-0.dll")] - private static extern int CoCreateFreeThreadedMarshaler(IntPtr outer, out IntPtr marshalerPtr); - - private static readonly string NotImplemented_NativeRoutineNotFound = "A native library routine was not found: {0}."; - - internal static readonly Lazy IID_InProcFreeThreadedMarshaler = new Lazy(Vftbl.GetInProcFreeThreadedMarshalerIID); + private const string NotImplemented_NativeRoutineNotFound = "A native library routine was not found: {0}."; + + private static readonly object _IID_InProcFreeThreadedMarshalerLock = new(); + internal static volatile object _IID_InProcFreeThreadedMarshaler; + + // Simple singleton lazy-initialization scheme (and saving the Lazy size) + internal static Guid IID_InProcFreeThreadedMarshaler + { + get + { + object iid = _IID_InProcFreeThreadedMarshaler; + + if (iid is not null) + { + return (Guid)iid; + } + + return IID_InProcFreeThreadedMarshaler_Slow(); + + [MethodImpl(MethodImplOptions.NoInlining)] + static Guid IID_InProcFreeThreadedMarshaler_Slow() + { + lock (_IID_InProcFreeThreadedMarshalerLock) + { + return (Guid)(_IID_InProcFreeThreadedMarshaler ??= Vftbl.GetInProcFreeThreadedMarshalerIID()); + } + } + } + } [Guid("00000003-0000-0000-c000-000000000046")] public unsafe struct Vftbl { - internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; - -#if !NET + internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; + +#if !NET private void* _GetUnmarshalClass_0; public delegate* unmanaged[Stdcall] GetUnmarshalClass_0 { get => (delegate* unmanaged[Stdcall])_GetUnmarshalClass_0; set => _GetUnmarshalClass_0 = value; } private void* _GetMarshalSizeMax_1; @@ -74,26 +97,26 @@ public unsafe struct Vftbl public delegate* unmanaged[Stdcall] ReleaseMarshalData_4; public delegate* unmanaged[Stdcall] DisconnectObject_5; #endif - - public static readonly IntPtr AbiToProjectionVftablePtr; - + + public static readonly IntPtr AbiToProjectionVftablePtr; + static Vftbl() { #if !NET AbiToProjectionVftable = new Vftbl { IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, - _GetUnmarshalClass_0 = Marshal.GetFunctionPointerForDelegate(DelegateCache[0] = new IMarshal_Delegates.GetUnmarshalClass_0(Do_Abi_GetUnmarshalClass_0)).ToPointer(), - _GetMarshalSizeMax_1 = Marshal.GetFunctionPointerForDelegate(DelegateCache[1] = new IMarshal_Delegates.GetMarshalSizeMax_1(Do_Abi_GetMarshalSizeMax_1)).ToPointer(), + _GetUnmarshalClass_0 = Marshal.GetFunctionPointerForDelegate(DelegateCache[0] = new IMarshal_Delegates.GetUnmarshalClass_0(Do_Abi_GetUnmarshalClass_0)).ToPointer(), + _GetMarshalSizeMax_1 = Marshal.GetFunctionPointerForDelegate(DelegateCache[1] = new IMarshal_Delegates.GetMarshalSizeMax_1(Do_Abi_GetMarshalSizeMax_1)).ToPointer(), _MarshalInterface_2 = Marshal.GetFunctionPointerForDelegate(DelegateCache[2] = new IMarshal_Delegates.MarshalInterface_2(Do_Abi_MarshalInterface_2)).ToPointer(), _UnmarshalInterface_3 = Marshal.GetFunctionPointerForDelegate(DelegateCache[3] = new IMarshal_Delegates.UnmarshalInterface_3(Do_Abi_UnmarshalInterface_3)).ToPointer(), _ReleaseMarshalData_4 = Marshal.GetFunctionPointerForDelegate(DelegateCache[4] = new IMarshal_Delegates.ReleaseMarshalData_4(Do_Abi_ReleaseMarshalData_4)).ToPointer(), _DisconnectObject_5 = Marshal.GetFunctionPointerForDelegate(DelegateCache[5] = new IMarshal_Delegates.DisconnectObject_5(Do_Abi_DisconnectObject_5)).ToPointer(), }; - AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf()); + AbiToProjectionVftablePtr = Marshal.AllocHGlobal(sizeof(Vftbl)); Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false); -#else - AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 6); +#else + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.Interop.IUnknownVftbl) + sizeof(IntPtr) * 6); (*(Vftbl*)AbiToProjectionVftablePtr) = new Vftbl { IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, @@ -103,44 +126,45 @@ static Vftbl() UnmarshalInterface_3 = &Do_Abi_UnmarshalInterface_3, ReleaseMarshalData_4 = &Do_Abi_ReleaseMarshalData_4, DisconnectObject_5 = &Do_Abi_DisconnectObject_5 - }; + }; #endif - } - - // This object handles IMarshal calls for us for most scenarios. - [ThreadStatic] - private static IMarshal t_freeThreadedMarshaler = null; - - private static void EnsureHasFreeThreadedMarshaler() - { - if (t_freeThreadedMarshaler != null) - return; - - try - { - Marshal.ThrowExceptionForHR(CoCreateFreeThreadedMarshaler(IntPtr.Zero, out IntPtr proxyPtr)); - using var objRef = ObjectReference.Attach(ref proxyPtr); - IMarshal proxy = new IMarshal(objRef); - t_freeThreadedMarshaler = proxy; - } - catch (DllNotFoundException ex) - { - throw new NotImplementedException(string.Format(NotImplemented_NativeRoutineNotFound, "CoCreateFreeThreadedMarshaler"), ex); - } - } - - internal static Guid GetInProcFreeThreadedMarshalerIID() - { - EnsureHasFreeThreadedMarshaler(); - - Guid iid_IUnknown = IUnknownVftbl.IID; - Guid iid_unmarshalClass; - t_freeThreadedMarshaler.GetUnmarshalClass(&iid_IUnknown, IntPtr.Zero, MSHCTX.InProc, IntPtr.Zero, MSHLFLAGS.Normal, &iid_unmarshalClass); - return iid_unmarshalClass; - } - + } + + // This object handles IMarshal calls for us for most scenarios. + [ThreadStatic] + private static IMarshal t_freeThreadedMarshaler = null; + + private static void EnsureHasFreeThreadedMarshaler() + { + if (t_freeThreadedMarshaler != null) + return; + + try + { + IntPtr proxyPtr; + Marshal.ThrowExceptionForHR(Platform.CoCreateFreeThreadedMarshaler(IntPtr.Zero, &proxyPtr)); + using var objRef = ObjectReference.Attach(ref proxyPtr, global::WinRT.Interop.IID.IID_IUnknown); + IMarshal proxy = new IMarshal(objRef); + t_freeThreadedMarshaler = proxy; + } + catch (DllNotFoundException ex) + { + throw new NotImplementedException(string.Format(NotImplemented_NativeRoutineNotFound, "CoCreateFreeThreadedMarshaler"), ex); + } + } + + internal static Guid GetInProcFreeThreadedMarshalerIID() + { + EnsureHasFreeThreadedMarshaler(); + + Guid iid_IUnknown = global::WinRT.Interop.IID.IID_IUnknown; + Guid iid_unmarshalClass; + t_freeThreadedMarshaler.GetUnmarshalClass(&iid_IUnknown, IntPtr.Zero, MSHCTX.InProc, IntPtr.Zero, MSHLFLAGS.Normal, &iid_unmarshalClass); + return iid_unmarshalClass; + } + #if NET - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] #endif private static int Do_Abi_GetUnmarshalClass_0(IntPtr thisPtr, Guid* riid, IntPtr pv, global::WinRT.Interop.MSHCTX dwDestContext, IntPtr pvDestContext, global::WinRT.Interop.MSHLFLAGS mshlFlags, Guid* pCid) { @@ -155,16 +179,16 @@ private static int Do_Abi_GetUnmarshalClass_0(IntPtr thisPtr, Guid* riid, IntPtr return Marshal.GetHRForException(ex); } return 0; - } - + } + #if NET - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] #endif private static int Do_Abi_GetMarshalSizeMax_1(IntPtr thisPtr, Guid* riid, IntPtr pv, global::WinRT.Interop.MSHCTX dwDestContext, IntPtr pvDestContext, global::WinRT.Interop.MSHLFLAGS mshlflags, uint* pSize) { *pSize = default; try - { + { EnsureHasFreeThreadedMarshaler(); t_freeThreadedMarshaler.GetMarshalSizeMax(riid, pv, dwDestContext, pvDestContext, mshlflags, pSize); } @@ -173,15 +197,15 @@ private static int Do_Abi_GetMarshalSizeMax_1(IntPtr thisPtr, Guid* riid, IntPtr return Marshal.GetHRForException(ex); } return 0; - } - + } + #if NET - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] #endif private static int Do_Abi_MarshalInterface_2(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr pv, global::WinRT.Interop.MSHCTX dwDestContext, IntPtr pvDestContext, global::WinRT.Interop.MSHLFLAGS mshlflags) { try - { + { EnsureHasFreeThreadedMarshaler(); t_freeThreadedMarshaler.MarshalInterface(pStm, riid, pv, dwDestContext, pvDestContext, mshlflags); } @@ -190,16 +214,16 @@ private static int Do_Abi_MarshalInterface_2(IntPtr thisPtr, IntPtr pStm, Guid* return Marshal.GetHRForException(ex); } return 0; - } - + } + #if NET - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] #endif private static int Do_Abi_UnmarshalInterface_3(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr* ppv) { *ppv = default; try - { + { EnsureHasFreeThreadedMarshaler(); t_freeThreadedMarshaler.UnmarshalInterface(pStm, riid, ppv); } @@ -208,15 +232,15 @@ private static int Do_Abi_UnmarshalInterface_3(IntPtr thisPtr, IntPtr pStm, Guid return Marshal.GetHRForException(ex); } return 0; - } - + } + #if NET - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] #endif private static int Do_Abi_ReleaseMarshalData_4(IntPtr thisPtr, IntPtr pStm) { try - { + { EnsureHasFreeThreadedMarshaler(); t_freeThreadedMarshaler.ReleaseMarshalData(pStm); } @@ -225,15 +249,15 @@ private static int Do_Abi_ReleaseMarshalData_4(IntPtr thisPtr, IntPtr pStm) return Marshal.GetHRForException(ex); } return 0; - } - + } + #if NET - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] #endif private static int Do_Abi_DisconnectObject_5(IntPtr thisPtr, uint dwReserved) { try - { + { EnsureHasFreeThreadedMarshaler(); t_freeThreadedMarshaler.DisconnectObject(dwReserved); } @@ -244,14 +268,12 @@ private static int Do_Abi_DisconnectObject_5(IntPtr thisPtr, uint dwReserved) return 0; } } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); + internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr, global::WinRT.Interop.IID.IID_IMarshal); public static implicit operator IMarshal(IObjectReference obj) => (obj != null) ? new IMarshal(obj) : null; private readonly ObjectReference _obj; public IObjectReference ObjRef { get => _obj; } public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); public IMarshal(IObjectReference obj) : this(obj.As(IID)) { } internal IMarshal(ObjectReference obj) { @@ -260,42 +282,48 @@ internal IMarshal(ObjectReference obj) public unsafe void GetUnmarshalClass(Guid* riid, IntPtr pv, global::WinRT.Interop.MSHCTX dwDestContext, IntPtr pvDestContext, global::WinRT.Interop.MSHLFLAGS mshlFlags, Guid* pCid) { - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetUnmarshalClass_0(ThisPtr, riid, pv, dwDestContext, pvDestContext, mshlFlags, pCid)); + Marshal.ThrowExceptionForHR(_obj.Vftbl.GetUnmarshalClass_0(ThisPtr, riid, pv, dwDestContext, pvDestContext, mshlFlags, pCid)); + GC.KeepAlive(_obj); } public unsafe void GetMarshalSizeMax(Guid* riid, IntPtr pv, global::WinRT.Interop.MSHCTX dwDestContext, IntPtr pvDestContext, global::WinRT.Interop.MSHLFLAGS mshlflags, uint* pSize) { - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetMarshalSizeMax_1(ThisPtr, riid, pv, dwDestContext, pvDestContext, mshlflags, pSize)); + Marshal.ThrowExceptionForHR(_obj.Vftbl.GetMarshalSizeMax_1(ThisPtr, riid, pv, dwDestContext, pvDestContext, mshlflags, pSize)); + GC.KeepAlive(_obj); } public unsafe void MarshalInterface(IntPtr pStm, Guid* riid, IntPtr pv, global::WinRT.Interop.MSHCTX dwDestContext, IntPtr pvDestContext, global::WinRT.Interop.MSHLFLAGS mshlflags) { - Marshal.ThrowExceptionForHR(_obj.Vftbl.MarshalInterface_2(ThisPtr, pStm, riid, pv, dwDestContext, pvDestContext, mshlflags)); + Marshal.ThrowExceptionForHR(_obj.Vftbl.MarshalInterface_2(ThisPtr, pStm, riid, pv, dwDestContext, pvDestContext, mshlflags)); + GC.KeepAlive(_obj); } public unsafe void UnmarshalInterface(IntPtr pStm, Guid* riid, IntPtr* ppv) { - Marshal.ThrowExceptionForHR(_obj.Vftbl.UnmarshalInterface_3(ThisPtr, pStm, riid, ppv)); + Marshal.ThrowExceptionForHR(_obj.Vftbl.UnmarshalInterface_3(ThisPtr, pStm, riid, ppv)); + GC.KeepAlive(_obj); } public unsafe void ReleaseMarshalData(IntPtr pStm) { - Marshal.ThrowExceptionForHR(_obj.Vftbl.ReleaseMarshalData_4(ThisPtr, pStm)); + Marshal.ThrowExceptionForHR(_obj.Vftbl.ReleaseMarshalData_4(ThisPtr, pStm)); + GC.KeepAlive(_obj); } public unsafe void DisconnectObject(uint dwReserved) { - Marshal.ThrowExceptionForHR(_obj.Vftbl.DisconnectObject_5(ThisPtr, dwReserved)); + Marshal.ThrowExceptionForHR(_obj.Vftbl.DisconnectObject_5(ThisPtr, dwReserved)); + GC.KeepAlive(_obj); } - } - + } + internal static unsafe class IMarshal_Delegates - { - public delegate int GetUnmarshalClass_0(IntPtr thisPtr, Guid* riid, IntPtr pv, global::WinRT.Interop.MSHCTX dwDestContext, IntPtr pvDestContext, global::WinRT.Interop.MSHLFLAGS mshlFlags, Guid* pCid); - public delegate int GetMarshalSizeMax_1(IntPtr thisPtr, Guid* riid, IntPtr pv, global::WinRT.Interop.MSHCTX dwDestContext, IntPtr pvDestContext, global::WinRT.Interop.MSHLFLAGS mshlflags, uint* pSize); - public delegate int MarshalInterface_2(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr pv, global::WinRT.Interop.MSHCTX dwDestContext, IntPtr pvDestContext, global::WinRT.Interop.MSHLFLAGS mshlflags); - public delegate int UnmarshalInterface_3(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr* ppv); - public delegate int ReleaseMarshalData_4(IntPtr thisPtr, IntPtr pStm); - public delegate int DisconnectObject_5(IntPtr thisPtr, uint dwReserved); + { + public delegate int GetUnmarshalClass_0(IntPtr thisPtr, Guid* riid, IntPtr pv, global::WinRT.Interop.MSHCTX dwDestContext, IntPtr pvDestContext, global::WinRT.Interop.MSHLFLAGS mshlFlags, Guid* pCid); + public delegate int GetMarshalSizeMax_1(IntPtr thisPtr, Guid* riid, IntPtr pv, global::WinRT.Interop.MSHCTX dwDestContext, IntPtr pvDestContext, global::WinRT.Interop.MSHLFLAGS mshlflags, uint* pSize); + public delegate int MarshalInterface_2(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr pv, global::WinRT.Interop.MSHCTX dwDestContext, IntPtr pvDestContext, global::WinRT.Interop.MSHLFLAGS mshlflags); + public delegate int UnmarshalInterface_3(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr* ppv); + public delegate int ReleaseMarshalData_4(IntPtr thisPtr, IntPtr pStm); + public delegate int DisconnectObject_5(IntPtr thisPtr, uint dwReserved); } } \ No newline at end of file diff --git a/src/WinRT.Runtime/Interop/IReferenceTracker.cs b/src/WinRT.Runtime/Interop/IReferenceTracker.cs index 93fcd4506..18e667d55 100644 --- a/src/WinRT.Runtime/Interop/IReferenceTracker.cs +++ b/src/WinRT.Runtime/Interop/IReferenceTracker.cs @@ -10,7 +10,7 @@ namespace WinRT.Interop [StructLayout(LayoutKind.Sequential)] internal unsafe struct IReferenceTrackerVftbl { - public global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; + public IUnknownVftbl IUnknownVftbl; private void* _ConnectFromTrackerSource_0; private void* _DisconnectFromTrackerSource_1; private void* _FindTrackerTargets_2; @@ -20,7 +20,5 @@ internal unsafe struct IReferenceTrackerVftbl private void* _ReleaseFromTrackerSource_5; public delegate* unmanaged[Stdcall] ReleaseFromTrackerSource { get => (delegate* unmanaged[Stdcall])_ReleaseFromTrackerSource_5; set => _ReleaseFromTrackerSource_5 = (void*)value; } private void* _PegFromTrackerSource_6; - - internal static readonly Guid IID = new(0x11D3B13A, 0x180E, 0x4789, 0xA8, 0xBE, 0x77, 0x12, 0x88, 0x28, 0x93, 0xE6); } } \ No newline at end of file diff --git a/src/WinRT.Runtime/Interop/IUnknownVftbl.cs b/src/WinRT.Runtime/Interop/IUnknownVftbl.cs index 55a455c58..9984beaf6 100644 --- a/src/WinRT.Runtime/Interop/IUnknownVftbl.cs +++ b/src/WinRT.Runtime/Interop/IUnknownVftbl.cs @@ -22,9 +22,7 @@ unsafe struct IUnknownVftbl public delegate* unmanaged[Stdcall] Release { get => (delegate* unmanaged[Stdcall])_Release; set => _Release = (void*)value; } public static IUnknownVftbl AbiToProjectionVftbl => ComWrappersSupport.IUnknownVftbl; - public static IntPtr AbiToProjectionVftblPtr => ComWrappersSupport.IUnknownVftblPtr; - - internal static readonly Guid IID = InterfaceIIDs.IUnknown_IID; + public static IntPtr AbiToProjectionVftblPtr => ComWrappersSupport.IUnknownVftblPtr; // Avoids boxing when using default Equals. internal bool Equals(IUnknownVftbl other) diff --git a/src/WinRT.Runtime/Interop/IWeakReferenceSource.net5.cs b/src/WinRT.Runtime/Interop/IWeakReferenceSource.net5.cs index 1e09d0f78..82fcbd1ae 100644 --- a/src/WinRT.Runtime/Interop/IWeakReferenceSource.net5.cs +++ b/src/WinRT.Runtime/Interop/IWeakReferenceSource.net5.cs @@ -34,6 +34,7 @@ interface IWeakReference IObjectReference Resolve(Guid riid); } + [WinRTExposedType(typeof(ManagedWeakReferenceTypeDetails))] internal sealed class ManagedWeakReference : IWeakReference { private readonly WeakReference _ref; @@ -62,6 +63,21 @@ public IntPtr ResolveForABI(Guid riid) return ComWrappersSupport.CreateCCWForObjectForABI(target, riid); } } + + internal sealed class ManagedWeakReferenceTypeDetails : IWinRTExposedTypeDetails + { + public ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + { + return new ComWrappers.ComInterfaceEntry[] + { + new ComWrappers.ComInterfaceEntry + { + IID = IID.IID_IWeakReference, + Vtable = ABI.WinRT.Interop.IWeakReference.AbiToProjectionVftablePtr + } + }; + } + } } @@ -80,7 +96,8 @@ static class IWeakReferenceSourceMethods IntPtr objRef = IntPtr.Zero; try { - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3](ThisPtr, &objRef)); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3](ThisPtr, &objRef)); + GC.KeepAlive(_obj); return MarshalInterface.FromAbi(objRef); } finally @@ -94,8 +111,6 @@ static class IWeakReferenceSourceMethods [Guid("00000038-0000-0000-C000-000000000046")] internal unsafe interface IWeakReferenceSource : global::WinRT.Interop.IWeakReferenceSource { - internal static readonly Guid IID = InterfaceIIDs.IWeakReferenceSource_IID; - public static IntPtr AbiToProjectionVftablePtr; static unsafe IWeakReferenceSource() { @@ -111,7 +126,7 @@ private static int Do_Abi_GetWeakReference(IntPtr thisPtr, IntPtr* weakReference try { - *weakReference = ComWrappersSupport.CreateCCWForObjectForABI(new global::WinRT.Interop.ManagedWeakReference(ComWrappersSupport.FindObject(thisPtr)), IWeakReference.IID); + *weakReference = ComWrappersSupport.CreateCCWForObjectForABI(new global::WinRT.Interop.ManagedWeakReference(ComWrappersSupport.FindObject(thisPtr)), global::WinRT.Interop.IID.IID_IWeakReference); } catch (Exception __exception__) { @@ -131,8 +146,6 @@ private static int Do_Abi_GetWeakReference(IntPtr thisPtr, IntPtr* weakReference [Guid("00000037-0000-0000-C000-000000000046")] internal unsafe interface IWeakReference : global::WinRT.Interop.IWeakReference { - internal static readonly Guid IID = new(0x00000037, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46); - public static IntPtr AbiToProjectionVftablePtr; static unsafe IWeakReference() { @@ -173,9 +186,10 @@ private static int Do_Abi_Resolve(IntPtr thisPtr, Guid* riid, IntPtr* objectRefe IntPtr objRef; ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3](ThisPtr, &riid, &objRef)); + GC.KeepAlive(_obj); try { - return ComWrappersSupport.GetObjectReferenceForInterface(objRef); + return ComWrappersSupport.GetObjectReferenceForInterface(objRef, riid, requireQI: false); } finally { diff --git a/src/WinRT.Runtime/Interop/IWeakReferenceSource.netstandard2.0.cs b/src/WinRT.Runtime/Interop/IWeakReferenceSource.netstandard2.0.cs index 15cb04999..05953b773 100644 --- a/src/WinRT.Runtime/Interop/IWeakReferenceSource.netstandard2.0.cs +++ b/src/WinRT.Runtime/Interop/IWeakReferenceSource.netstandard2.0.cs @@ -99,7 +99,7 @@ private static int Do_Abi_GetWeakReference(IntPtr thisPtr, IntPtr* weakReference } } - internal static readonly Guid IID = new(0x00000038, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46); + internal static readonly Guid IID = global::WinRT.Interop.IID.IID_IWeakReferenceSource; public static IntPtr AbiToProjectionVftablePtr => Vftbl.AbiToProjectionVftablePtr; public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); @@ -120,7 +120,8 @@ public IWeakReferenceSource(ObjectReference obj) IntPtr objRef = IntPtr.Zero; try { - ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetWeakReference(ThisPtr, out objRef)); + ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetWeakReference(ThisPtr, out objRef)); + GC.KeepAlive(_obj); return MarshalInterface.FromAbi(objRef); } finally @@ -192,10 +193,11 @@ public IWeakReference(ObjectReference obj) public IObjectReference Resolve(Guid riid) { - ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Resolve(ThisPtr, ref riid, out IntPtr objRef)); + ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Resolve(ThisPtr, ref riid, out IntPtr objRef)); + GC.KeepAlive(_obj); try { - return ComWrappersSupport.GetObjectReferenceForInterface(objRef); + return ComWrappersSupport.GetObjectReferenceForInterface(objRef, riid, requireQI: false); } finally { diff --git a/src/WinRT.Runtime/Interop/StandardDelegates.cs b/src/WinRT.Runtime/Interop/StandardDelegates.cs index e0027e064..90bbc5851 100644 --- a/src/WinRT.Runtime/Interop/StandardDelegates.cs +++ b/src/WinRT.Runtime/Interop/StandardDelegates.cs @@ -13,7 +13,11 @@ namespace WinRT.Interop #else public #endif - delegate int _get_PropertyAsBoolean(IntPtr thisPtr, out byte value); + delegate int _get_PropertyAsBoolean(IntPtr thisPtr, out byte value); + + [EditorBrowsable(EditorBrowsableState.Never)] + internal unsafe delegate int _get_PropertyAsBoolean_Abi(IntPtr thisPtr, byte* value); + [EditorBrowsable(EditorBrowsableState.Never)] #if EMBED internal @@ -105,13 +109,17 @@ namespace WinRT.Interop public #endif delegate int _put_PropertyAsInt32(IntPtr thisPtr, int value); - [EditorBrowsable(EditorBrowsableState.Never)] + [EditorBrowsable(EditorBrowsableState.Never)] #if EMBED internal #else public #endif - delegate int _get_PropertyAsUInt32(IntPtr thisPtr, out uint value); + delegate int _get_PropertyAsUInt32(IntPtr thisPtr, out uint value); + + [EditorBrowsable(EditorBrowsableState.Never)] + internal unsafe delegate int _get_PropertyAsUInt32_Abi(IntPtr thisPtr, uint* value); + [EditorBrowsable(EditorBrowsableState.Never)] #if EMBED internal @@ -231,4 +239,36 @@ namespace WinRT.Interop public #endif delegate int _remove_EventHandler(IntPtr thisPtr, EventRegistrationToken token); -} + +#if !NET + internal unsafe delegate int _get_Current_IntPtr(void* thisPtr, out IntPtr __return_value__); + internal unsafe delegate int _get_Current_Type(void* thisPtr, out ABI.System.Type __return_value__); + + internal unsafe delegate int _get_At_IntPtr(void* thisPtr, uint index, out IntPtr __return_value__); + internal unsafe delegate int _get_At_Type(void* thisPtr, uint index, out ABI.System.Type __return_value__); + internal unsafe delegate int _index_Of_IntPtr(void* thisPtr, IntPtr value, out uint index, out byte found); + internal unsafe delegate int _index_Of_Type(void* thisPtr, ABI.System.Type value, out uint index, out byte found); + internal unsafe delegate int _set_At_IntPtr(void* thisPtr, uint index, IntPtr value); + internal unsafe delegate int _set_At_Type(void* thisPtr, uint index, ABI.System.Type value); + internal unsafe delegate int _append_IntPtr(void* thisPtr, IntPtr value); + internal unsafe delegate int _append_Type(void* thisPtr, ABI.System.Type value); + + internal unsafe delegate int _lookup_IntPtr_IntPtr(void* thisPtr, IntPtr key, out IntPtr value); + internal unsafe delegate int _lookup_Type_Type(void* thisPtr, ABI.System.Type key, out ABI.System.Type value); + internal unsafe delegate int _lookup_IntPtr_Type(void* thisPtr, IntPtr key, out ABI.System.Type value); + internal unsafe delegate int _lookup_Type_IntPtr(void* thisPtr, ABI.System.Type key, out IntPtr value); + internal unsafe delegate int _has_key_IntPtr(void* thisPtr, IntPtr key, out byte found); + internal unsafe delegate int _has_key_Type(void* thisPtr, ABI.System.Type key, out byte found); + internal unsafe delegate int _insert_IntPtr_IntPtr(void* thisPtr, IntPtr key, IntPtr value, out byte replaced); + internal unsafe delegate int _insert_Type_Type(void* thisPtr, ABI.System.Type key, ABI.System.Type value, out byte replaced); + internal unsafe delegate int _insert_IntPtr_Type(void* thisPtr, IntPtr key, ABI.System.Type value, out byte replaced); + internal unsafe delegate int _insert_Type_IntPtr(void* thisPtr, ABI.System.Type key, IntPtr value, out byte replaced); + + internal unsafe delegate int _invoke_IntPtr_IntPtr(void* thisPtr, IntPtr sender, IntPtr args); + internal unsafe delegate int _invoke_IntPtr_Type(void* thisPtr, IntPtr sender, ABI.System.Type args); + internal unsafe delegate int _invoke_Type_IntPtr(void* thisPtr, ABI.System.Type sender, IntPtr args); + internal unsafe delegate int _invoke_Type_Type(void* thisPtr, ABI.System.Type sender, ABI.System.Type args); + + internal unsafe delegate int _get_Key_IntPtr(IntPtr thisPtr, IntPtr* __return_value__); +#endif +} \ No newline at end of file diff --git a/src/WinRT.Runtime/Interop/WuxMuxProjectedTypeAttribute.cs b/src/WinRT.Runtime/Interop/WuxMuxProjectedTypeAttribute.cs new file mode 100644 index 000000000..3f56b6dc7 --- /dev/null +++ b/src/WinRT.Runtime/Interop/WuxMuxProjectedTypeAttribute.cs @@ -0,0 +1,17 @@ +using System; + +namespace WinRT.Interop +{ + // This attribute is only used by the IIDOptimizer for resolving the signature of a type that has different + // IIDs on WUX/MUX targets. The actual IIDs are hardcoded in WinRT.Runtime, so they are not needed here. + // TODO: remove this entirely when either IIDOptimizer is removed, or when the option to hardcode IIDs is added. + + /// + /// This type signals that the type it is applied to is projected into .NET from either a Windows.UI.Xaml type or a Microsoft.UI.Xaml type. + /// For this type, the GuidAttribute is not used and instead the GetGuidSignature method must be called to get the IID or generic IID signature part of the type. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Interface, AllowMultiple = false, Inherited = false)] + internal sealed class WuxMuxProjectedTypeAttribute : Attribute + { + } +} diff --git a/src/WinRT.Runtime/Marshalers.cs b/src/WinRT.Runtime/Marshalers.cs index f03556e72..ec6f3f169 100644 --- a/src/WinRT.Runtime/Marshalers.cs +++ b/src/WinRT.Runtime/Marshalers.cs @@ -2,9 +2,9 @@ // Licensed under the MIT License. using System; +using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -17,6 +17,21 @@ namespace WinRT { internal static class MarshalExtensions { + /// + /// Releases a COM object, if not . + /// + /// The input COM object to release. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe void ReleaseIfNotNull(IntPtr pUnk) + { + if ((void*)pUnk == null) + { + return; + } + + _ = ((delegate* unmanaged[Stdcall])(*(*(void***)pUnk + 2 /* IUnknown.Release slot */)))(pUnk); + } + public static void Dispose(this GCHandle handle) { if (handle.IsAllocated) @@ -33,12 +48,34 @@ public static unsafe ref readonly char GetPinnableReference(this string str) return ref *p; } } +#endif + +#if NET8_0_OR_GREATER +#nullable enable + /// + /// Tries to create a for a given method on a helper type. + /// + /// The input helper type. + /// The name of the method to get. + /// The resulting instance, if the method was present. + public static MethodInvoker? TryGetMethodInvoker([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] Type helperType, string methodName) + { + MethodInfo? method = helperType.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static); + + if (method is null) + { + return null; + } + + return MethodInvoker.Create(method); + } +#nullable restore #endif } #if EMBED internal -#else +#else public #endif class MarshalString @@ -58,8 +95,8 @@ internal struct HSTRING_HEADER public ref struct Pinnable { - private HSTRING_HEADER _header; - private string _value; + private readonly HSTRING_HEADER _header; + private readonly string _value; #if DEBUG private bool _pinned; #endif @@ -70,7 +107,7 @@ public Pinnable(string value) _header = default; #if DEBUG _pinned = false; -#endif +#endif } public ref readonly char GetPinnableReference() @@ -93,9 +130,9 @@ public unsafe IntPtr GetAbi() } IntPtr hstring; Marshal.ThrowExceptionForHR(Platform.WindowsCreateStringReference( - (char*)Unsafe.AsPointer(ref Unsafe.AsRef(in GetPinnableReference())), + (ushort*)Unsafe.AsPointer(ref Unsafe.AsRef(in GetPinnableReference())), _value.Length, - (IntPtr*)Unsafe.AsPointer(ref _header), + (IntPtr*)Unsafe.AsPointer(ref Unsafe.AsRef(in _header)), &hstring)); return hstring; } @@ -131,9 +168,9 @@ public unsafe IntPtr GetAbi() { IntPtr hstring; Debug.Assert(_header == IntPtr.Zero); - _header = Marshal.AllocHGlobal(Unsafe.SizeOf()); + _header = Marshal.AllocHGlobal(sizeof(HSTRING_HEADER)); Marshal.ThrowExceptionForHR(Platform.WindowsCreateStringReference( - chars, value.Length, (IntPtr*)_header, &hstring)); + (ushort*)chars, value.Length, (IntPtr*)_header, &hstring)); return hstring; } } @@ -171,6 +208,37 @@ public static unsafe string FromAbi(IntPtr value) return new string(buffer, 0, (int)length); } + /// + /// Marshals an input HSTRING value to a value. + /// + /// The input HSTRING value to marshal. + /// The resulting value. + /// + /// + /// This method is equivalent to , but it does not create a new instance. + /// Doing so makes it zero-allocation, but extra care should be taken by callers to ensure that the returned value + /// does not escape the scope where the source HSTRING is valid. + /// + /// + /// For instance, if this method is invoked in the scope of a method that receives the HSTRING value as one of + /// its parameters, the resulting is always valid for the scope of such method. But, if + /// the HSTRING was created by reference in a given scope, the resulting value + /// will also only be valid within such scope, and should not be used outside of it. + /// + /// + public static unsafe ReadOnlySpan FromAbiUnsafe(IntPtr value) + { + if (value == IntPtr.Zero) + { + return "".AsSpan(); + } + + uint length; + char* buffer = Platform.WindowsGetStringRawBuffer(value, &length); + + return new(buffer, (int)length); + } + public static unsafe IntPtr FromManaged(string value) { if (value is null) @@ -178,8 +246,11 @@ public static unsafe IntPtr FromManaged(string value) return IntPtr.Zero; } IntPtr handle; - Marshal.ThrowExceptionForHR( - Platform.WindowsCreateString(value, value.Length, &handle)); + fixed (char* lpValue = value) + { + Marshal.ThrowExceptionForHR( + Platform.WindowsCreateString((ushort*)lpValue, value.Length, &handle)); + } return handle; } @@ -215,7 +286,7 @@ public static unsafe MarshalerArray CreateMarshalerArray(string[] array) try { var length = array.Length; - m._array = Marshal.AllocCoTaskMem(length * Marshal.SizeOf()); + m._array = Marshal.AllocCoTaskMem(length * sizeof(IntPtr)); m._marshalers = new MarshalString[length]; var elements = (IntPtr*)m._array.ToPointer(); for (int i = 0; i < length; i++) @@ -283,7 +354,7 @@ public static unsafe (int length, IntPtr data) FromManagedArray(string[] array) try { var length = array.Length; - data = Marshal.AllocCoTaskMem(length * Marshal.SizeOf()); + data = Marshal.AllocCoTaskMem(length * sizeof(IntPtr)); var elements = (IntPtr*)data; for (i = 0; i < length; i++) { @@ -407,7 +478,9 @@ public static unsafe (int length, IntPtr data) FromManagedArray(Array array) return (0, IntPtr.Zero); } var length = array.Length; - var byte_length = length * Marshal.SizeOf(); +#pragma warning disable CS8500 // 'T' is always blittable + var byte_length = length * sizeof(T); +#pragma warning restore CS8500 var data = Marshal.AllocCoTaskMem(byte_length); CopyManagedArray(array, data); return (length, data); @@ -420,11 +493,20 @@ public static unsafe void CopyManagedArray(Array array, IntPtr data) return; } var length = array.Length; - var byte_length = length * Marshal.SizeOf(); +#pragma warning disable CS8500 // 'T' is always blittable + var byte_length = length * sizeof(T); +#pragma warning restore CS8500 +#if NET + fixed (byte* pArrayData = &MemoryMarshal.GetArrayDataReference(array)) + { + Buffer.MemoryCopy(pArrayData, data.ToPointer(), byte_length, byte_length); + } +#else var array_handle = GCHandle.Alloc(array, GCHandleType.Pinned); var array_data = array_handle.AddrOfPinnedObject(); Buffer.MemoryCopy(array_data.ToPointer(), data.ToPointer(), byte_length, byte_length); array_handle.Free(); +#endif } public static void DisposeMarshalerArray(object box) @@ -451,158 +533,517 @@ class MarshalGeneric #if NET [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] #endif - protected static readonly Type HelperType = typeof(T).GetHelperType(); + protected static readonly Type HelperType; + protected static readonly Type AbiType; + protected static readonly Type MarshalerType; + internal static readonly bool MarshalByObjectReferenceValueSupported; - protected static readonly Type AbiType = typeof(T).GetAbiType(); - protected static readonly Type MarshalerType = typeof(T).GetMarshalerType(); - private static readonly bool MarshalByObjectReferenceValueSupported = typeof(T).GetMarshaler2Type() == typeof(ObjectReferenceValue); + public static readonly Func CreateMarshaler; + public static readonly Func GetAbi; + public static readonly Action CopyAbi; + public static readonly Func FromAbi; + public static readonly Func FromManaged; + public static readonly Action CopyManaged; + public static readonly Action DisposeMarshaler; + internal static readonly Func CreateMarshaler2; + internal static readonly Action DisposeAbi; + internal static readonly Func CreateMarshalerArray; + internal static readonly Func GetAbiArray; + internal static readonly Func FromAbiArray; + internal static readonly Func FromManagedArray; + internal static readonly Action DisposeMarshalerArray; + internal static readonly Action DisposeAbiArray; - public static readonly Func CreateMarshaler = (T value) => CreateMarshalerLazy.Value(value); - private static readonly Lazy> CreateMarshalerLazy = new(BindCreateMarshaler); - private static Func BindCreateMarshaler() + static MarshalGeneric() { - var createMarshaler = HelperType.GetMethod("CreateMarshaler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - return (T arg) => createMarshaler.Invoke(null, new object[] { arg }); + // Special case some well known projected types that are blittable. + // For these, we directly load the ABI type and leave everything else + // set to default (null). That is, we have no special marshallers. + if (typeof(T) == typeof(global::System.Numerics.Vector2)) + { + HelperType = typeof(global::ABI.System.Numerics.Vector2); + } + else if (typeof(T) == typeof(global::System.Numerics.Vector3)) + { + HelperType = typeof(global::ABI.System.Numerics.Vector3); + } + else if (typeof(T) == typeof(global::System.Numerics.Vector4)) + { + HelperType = typeof(global::ABI.System.Numerics.Vector4); + } + else if (typeof(T) == typeof(global::System.Numerics.Plane)) + { + HelperType = typeof(global::ABI.System.Numerics.Plane); + } + else if (typeof(T) == typeof(global::System.Numerics.Matrix3x2)) + { + HelperType = typeof(global::ABI.System.Numerics.Matrix3x2); + } + else if (typeof(T) == typeof(global::System.Numerics.Matrix4x4)) + { + HelperType = typeof(global::ABI.System.Numerics.Matrix4x4); + } + else if (typeof(T) == typeof(global::System.Numerics.Quaternion)) + { + HelperType = typeof(global::ABI.System.Numerics.Quaternion); + } + else if (typeof(T) == typeof(global::Windows.Foundation.Size)) + { + HelperType = typeof(global::ABI.Windows.Foundation.Size); + } + else if (typeof(T) == typeof(global::Windows.Foundation.Point)) + { + HelperType = typeof(global::ABI.Windows.Foundation.Point); + } + else if (typeof(T) == typeof(global::Windows.Foundation.Rect)) + { + HelperType = typeof(global::ABI.Windows.Foundation.Rect); + } + else if (typeof(T) == typeof(int) || + typeof(T) == typeof(byte) || + typeof(T) == typeof(sbyte) || + typeof(T) == typeof(short) || + typeof(T) == typeof(ushort) || + typeof(T) == typeof(uint) || + typeof(T) == typeof(long) || + typeof(T) == typeof(ulong) || + typeof(T) == typeof(float) || + typeof(T) == typeof(double) || + typeof(T) == typeof(Guid) || + typeof(Exception).IsAssignableFrom(typeof(T))) + { + // Special case some well known primitive types that we know might be constructed + // for this type, but not actually used. For these, we just keep all default values. + // No consumer would ever actually be trying to use this marshaller for these types. + return; + } + else if (typeof(T) == typeof(bool)) + { + // Same as above, but we do have an ABI type + HelperType = typeof(global::ABI.System.Boolean); + AbiType = typeof(byte); + MarshalerType = typeof(bool); + + // Note: we're deliberately using object creation expressions here to create the delegates, rather than using + // method group expressions. This prevents Roslyn from generating a class to store a cached instance. This is + // not needed, because we're executing each of these paths once, and already caching the resulting delegates. + CreateMarshaler = (Func)(object)new Func(ABI.System.NonBlittableMarshallingStubs.Boolean_CreateMarshaler); + CreateMarshaler2 = CreateMarshaler; + GetAbi = new Func(ABI.System.NonBlittableMarshallingStubs.Boolean_GetAbi); + FromAbi = (Func)(object)new Func(ABI.System.NonBlittableMarshallingStubs.Boolean_FromAbi); + CopyAbi = new Action(ABI.System.NonBlittableMarshallingStubs.Boolean_CopyAbi); + FromManaged = (Func)(object)new Func(ABI.System.NonBlittableMarshallingStubs.Boolean_FromManaged); + CopyManaged = (Action)(object)new Action(ABI.System.Boolean.CopyManaged); + DisposeMarshaler = ABI.System.NonBlittableMarshallingStubs.NoOpFunc; + DisposeAbi = ABI.System.NonBlittableMarshallingStubs.NoOpFunc; + } + else if (typeof(T) == typeof(char)) + { + HelperType = typeof(global::ABI.System.Char); + AbiType = typeof(ushort); + MarshalerType = typeof(char); + CreateMarshaler = (Func)(object)new Func(ABI.System.NonBlittableMarshallingStubs.Char_CreateMarshaler); + CreateMarshaler2 = CreateMarshaler; + GetAbi = new Func(ABI.System.NonBlittableMarshallingStubs.Char_GetAbi); + FromAbi = (Func)(object)new Func(ABI.System.NonBlittableMarshallingStubs.Char_FromAbi); + CopyAbi = new Action(ABI.System.NonBlittableMarshallingStubs.Char_CopyAbi); + FromManaged = (Func)(object)new Func(ABI.System.NonBlittableMarshallingStubs.Char_FromManaged); + CopyManaged = (Action)(object)new Action(ABI.System.Char.CopyManaged); + DisposeMarshaler = ABI.System.NonBlittableMarshallingStubs.NoOpFunc; + DisposeAbi = ABI.System.NonBlittableMarshallingStubs.NoOpFunc; + } + else if (typeof(T) == typeof(TimeSpan)) + { + // Another well known projected type that we might will be constructed + HelperType = typeof(global::ABI.System.TimeSpan); + AbiType = typeof(global::ABI.System.TimeSpan); + MarshalerType = typeof(global::ABI.System.TimeSpan.Marshaler); + CreateMarshaler = (Func)(object)new Func(ABI.System.NonBlittableMarshallingStubs.TimeSpan_CreateMarshaler); + CreateMarshaler2 = CreateMarshaler; + GetAbi = new Func(ABI.System.NonBlittableMarshallingStubs.TimeSpan_GetAbi); + FromAbi = (Func)(object)new Func(ABI.System.NonBlittableMarshallingStubs.TimeSpan_FromAbi); + CopyAbi = new Action(ABI.System.NonBlittableMarshallingStubs.TimeSpan_CopyAbi); + FromManaged = (Func)(object)new Func(ABI.System.NonBlittableMarshallingStubs.TimeSpan_FromManaged); + CopyManaged = (Action)(object)new Action(ABI.System.TimeSpan.CopyManaged); + DisposeMarshaler = ABI.System.NonBlittableMarshallingStubs.NoOpFunc; + DisposeAbi = ABI.System.NonBlittableMarshallingStubs.NoOpFunc; + } + else if (typeof(T) == typeof(DateTimeOffset)) + { + // DateTimeOffset also has a custom marshaller and is always constructed. + // We can do the same as with TimeSpan: just special case all delegates. + HelperType = typeof(global::ABI.System.DateTimeOffset); + AbiType = typeof(global::ABI.System.DateTimeOffset); + MarshalerType = typeof(global::ABI.System.DateTimeOffset.Marshaler); + CreateMarshaler = (Func)(object)new Func(ABI.System.NonBlittableMarshallingStubs.DateTimeOffset_CreateMarshaler); + CreateMarshaler2 = CreateMarshaler; + GetAbi = new Func(ABI.System.NonBlittableMarshallingStubs.DateTimeOffset_GetAbi); + FromAbi = (Func)(object)new Func(ABI.System.NonBlittableMarshallingStubs.DateTimeOffset_FromAbi); + CopyAbi = new Action(ABI.System.NonBlittableMarshallingStubs.DateTimeOffset_CopyAbi); + FromManaged = (Func)(object)new Func(ABI.System.NonBlittableMarshallingStubs.DateTimeOffset_FromManaged); + CopyManaged = (Action)(object)new Action(ABI.System.DateTimeOffset.CopyManaged); + DisposeMarshaler = ABI.System.NonBlittableMarshallingStubs.NoOpFunc; + DisposeAbi = ABI.System.NonBlittableMarshallingStubs.NoOpFunc; + } + else if (typeof(T).IsEnum) + { + Func ReturnTypedParameterFunc = (T value) => value; + AbiType = typeof(T); + CreateMarshaler = ReturnTypedParameterFunc; + CreateMarshaler2 = CreateMarshaler; + GetAbi = Marshaler.ReturnParameterFunc; + FromAbi = (object value) => (T)value; + FromManaged = ReturnTypedParameterFunc; + DisposeMarshaler = ABI.System.NonBlittableMarshallingStubs.NoOpFunc; + DisposeAbi = ABI.System.NonBlittableMarshallingStubs.NoOpFunc; + if (typeof(T).IsEnum) + { + // For marshaling non-blittable enum arrays via MarshalNonBlittable + if (typeof(T).GetEnumUnderlyingType() == typeof(int)) + { + CopyAbi = Marshaler.CopyIntEnumFunc; + CopyManaged = Marshaler.CopyIntEnumDirectFunc.WithTypedT1(); + } + else + { + CopyAbi = Marshaler.CopyUIntEnumFunc; + CopyManaged = Marshaler.CopyUIntEnumDirectFunc.WithTypedT1(); + } + } + } + else if (typeof(T).IsValueType) + { + // Value types can have custom marshaller types and use value types in places where we can't construct + // delegates in the same efficient way as with reference types. Use the fallback path in this case + HelperType = typeof(T).GetHelperType(); + AbiType = typeof(T).GetAbiType(); + MarshalerType = typeof(T).GetMarshalerType(); + MarshalByObjectReferenceValueSupported = typeof(T).GetMarshaler2Type() == typeof(ObjectReferenceValue); + + MarshalGenericFallback fallback = new(HelperType); + + CreateMarshaler = fallback.CreateMarshaler; + CreateMarshaler2 = MarshalByObjectReferenceValueSupported ? fallback.CreateMarshaler2 : CreateMarshaler; + GetAbi = fallback.GetAbi; + CopyAbi = fallback.CopyAbi; + FromAbi = fallback.FromAbi; + FromManaged = fallback.FromManaged; + CopyManaged = fallback.CopyManaged; + DisposeMarshaler = fallback.DisposeMarshaler; + DisposeAbi = fallback.DisposeAbi; + CreateMarshalerArray = fallback.CreateMarshalerArray; + GetAbiArray = fallback.GetAbiArray; + FromAbiArray = fallback.FromAbiArray; + FromManagedArray = fallback.FromManagedArray; + DisposeMarshalerArray = fallback.DisposeMarshalerArray; + DisposeAbiArray = fallback.DisposeAbiArray; + } + else + { + // Fallback case for all other types (could be anything, really). These would be reference types, + // which means we can make some assumptions on the shape of the helper type methods. Specifically, + // we expect the returned marshallers to be IObjectReference-s, and the ABI to be an IntPtr value. + HelperType = typeof(T).GetHelperType(); + AbiType = typeof(T).GetAbiType(); + MarshalerType = typeof(T).GetMarshalerType(); + MarshalByObjectReferenceValueSupported = typeof(T).GetMarshaler2Type() == typeof(ObjectReferenceValue); +#if NET + CreateMarshaler = HelperType.GetMethod("CreateMarshaler", BindingFlags.Public | BindingFlags.Static)?.CreateDelegate>(); + CreateMarshaler2 = MarshalByObjectReferenceValueSupported + ? HelperType.GetMethod("CreateMarshaler2", BindingFlags.Public | BindingFlags.Static)?.CreateDelegate>().WithObjectTResult() + : CreateMarshaler; + GetAbi = HelperType.GetMethod("GetAbi", BindingFlags.Public | BindingFlags.Static)?.CreateDelegate>().WithMarshaler2Support(); + // CopyAbi = null; (Not used for class types) + FromAbi = HelperType.GetMethod("FromAbi", BindingFlags.Public | BindingFlags.Static)?.CreateDelegate>().WithObjectT(); + FromManaged = HelperType.GetMethod("FromManaged", BindingFlags.Public | BindingFlags.Static)?.CreateDelegate>().WithObjectTResult(); + // CopyManaged = null; (Also not used for class types) + DisposeMarshaler = HelperType.GetMethod("DisposeMarshaler", BindingFlags.Public | BindingFlags.Static)?.CreateDelegate>().WithMarshaler2Support(); + DisposeAbi = HelperType.GetMethod("DisposeAbi", BindingFlags.Public | BindingFlags.Static)?.CreateDelegate>().WithObjectParams(); + CreateMarshalerArray = HelperType.GetMethod("CreateMarshalerArray", BindingFlags.Public | BindingFlags.Static)?.CreateDelegate.MarshalerArray>>().WithObjectTResult(); + GetAbiArray = HelperType.GetMethod("GetAbiArray", BindingFlags.Public | BindingFlags.Static)?.CreateDelegate>(); + FromAbiArray = HelperType.GetMethod("FromAbiArray", BindingFlags.Public | BindingFlags.Static)?.CreateDelegate>(); + FromManagedArray = HelperType.GetMethod("FromManagedArray", BindingFlags.Public | BindingFlags.Static)?.CreateDelegate>(); + DisposeMarshalerArray = HelperType.GetMethod("DisposeMarshalerArray", BindingFlags.Public | BindingFlags.Static)?.CreateDelegate.MarshalerArray>>().WithObjectParams(); + DisposeAbiArray = HelperType.GetMethod("DisposeAbiArray", BindingFlags.Public | BindingFlags.Static)?.CreateDelegate>(); +#else + MarshalGenericFallback fallback = new(HelperType); + + CreateMarshaler = fallback.CreateMarshaler; + CreateMarshaler2 = MarshalByObjectReferenceValueSupported ? fallback.CreateMarshaler2 : CreateMarshaler; + GetAbi = fallback.GetAbi; + // CopyAbi = null; + FromAbi = fallback.FromAbi; + FromManaged = fallback.FromManaged; + // CopyManaged = null; + DisposeMarshaler = fallback.DisposeMarshaler; + DisposeAbi = fallback.DisposeAbi; + CreateMarshalerArray = fallback.CreateMarshalerArray; + GetAbiArray = fallback.GetAbiArray; + FromAbiArray = fallback.FromAbiArray; + FromManagedArray = fallback.FromManagedArray; + DisposeMarshalerArray = fallback.DisposeMarshalerArray; + DisposeAbiArray = fallback.DisposeAbiArray; +#endif + } } + } - internal static Func CreateMarshaler2 => MarshalByObjectReferenceValueSupported ? CreateMarshaler2Lazy.Value : CreateMarshaler; - private static readonly Lazy> CreateMarshaler2Lazy = new(BindCreateMarshaler2); - private static Func BindCreateMarshaler2() + internal sealed class MarshalGenericFallback + { +#if NET8_0_OR_GREATER + private readonly MethodInvoker _createMarshaler; + private readonly MethodInvoker _getAbi; + private readonly MethodInvoker _copyAbi; + private readonly MethodInvoker _fromAbi; + private readonly MethodInvoker _fromManaged; + private readonly MethodInvoker _copyManaged; + private readonly MethodInvoker _disposeMarshaler; + private readonly MethodInvoker _createMarshaler2; + private readonly MethodInvoker _disposeAbi; + private readonly MethodInvoker _createMarshalerArray; + private readonly MethodInvoker _getAbiArray; + private readonly MethodInvoker _fromAbiArray; + private readonly MethodInvoker _fromManagedArray; + private readonly MethodInvoker _disposeMarshalerArray; + private readonly MethodInvoker _disposeAbiArray; +#else + private readonly MethodInfo _createMarshaler; + private readonly MethodInfo _getAbi; + private readonly MethodInfo _copyAbi; + private readonly MethodInfo _fromAbi; + private readonly MethodInfo _fromManaged; + private readonly MethodInfo _copyManaged; + private readonly MethodInfo _disposeMarshaler; + private readonly MethodInfo _createMarshaler2; + private readonly MethodInfo _disposeAbi; + private readonly MethodInfo _createMarshalerArray; + private readonly MethodInfo _getAbiArray; + private readonly MethodInfo _fromAbiArray; + private readonly MethodInfo _fromManagedArray; + private readonly MethodInfo _disposeMarshalerArray; + private readonly MethodInfo _disposeAbiArray; +#endif + + public MarshalGenericFallback( +#if NET + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] +#endif + Type helperType) + { +#if NET8_0_OR_GREATER + _createMarshaler = MarshalExtensions.TryGetMethodInvoker(helperType, "CreateMarshaler"); + _getAbi = MarshalExtensions.TryGetMethodInvoker(helperType, "GetAbi"); + _copyAbi = MarshalExtensions.TryGetMethodInvoker(helperType, "CopyAbi"); + _fromAbi = MarshalExtensions.TryGetMethodInvoker(helperType, "FromAbi"); + _fromManaged = MarshalExtensions.TryGetMethodInvoker(helperType, "FromManaged"); + _copyManaged = MarshalExtensions.TryGetMethodInvoker(helperType, "CopyManaged"); + _disposeMarshaler = MarshalExtensions.TryGetMethodInvoker(helperType, "DisposeMarshaler"); + _createMarshaler2 = MarshalExtensions.TryGetMethodInvoker(helperType, "CreateMarshaler2"); + _disposeAbi = MarshalExtensions.TryGetMethodInvoker(helperType, "DisposeAbi"); + _createMarshalerArray = MarshalExtensions.TryGetMethodInvoker(helperType, "CreateMarshalerArray"); + _getAbiArray = MarshalExtensions.TryGetMethodInvoker(helperType, "GetAbiArray"); + _fromAbiArray = MarshalExtensions.TryGetMethodInvoker(helperType, "FromAbiArray"); + _fromManagedArray = MarshalExtensions.TryGetMethodInvoker(helperType, "FromManagedArray"); + _disposeMarshalerArray = MarshalExtensions.TryGetMethodInvoker(helperType, "DisposeMarshalerArray"); + _disposeAbiArray = MarshalExtensions.TryGetMethodInvoker(helperType, "DisposeAbiArray"); +#else + _createMarshaler = helperType.GetMethod("CreateMarshaler", BindingFlags.Public | BindingFlags.Static); + _getAbi = helperType.GetMethod("GetAbi", BindingFlags.Public | BindingFlags.Static); + _copyAbi = helperType.GetMethod("CopyAbi", BindingFlags.Public | BindingFlags.Static); + _fromAbi = helperType.GetMethod("FromAbi", BindingFlags.Public | BindingFlags.Static); + _fromManaged = helperType.GetMethod("FromManaged", BindingFlags.Public | BindingFlags.Static); + _copyManaged = helperType.GetMethod("CopyManaged", BindingFlags.Public | BindingFlags.Static); + _disposeMarshaler = helperType.GetMethod("DisposeMarshaler", BindingFlags.Public | BindingFlags.Static); + _createMarshaler2 = helperType.GetMethod("CreateMarshaler2", BindingFlags.Public | BindingFlags.Static); + _disposeAbi = helperType.GetMethod("DisposeAbi", BindingFlags.Public | BindingFlags.Static); + _createMarshalerArray = helperType.GetMethod("CreateMarshalerArray", BindingFlags.Public | BindingFlags.Static); + _getAbiArray = helperType.GetMethod("GetAbiArray", BindingFlags.Public | BindingFlags.Static); + _fromAbiArray = helperType.GetMethod("FromAbiArray", BindingFlags.Public | BindingFlags.Static); + _fromManagedArray = helperType.GetMethod("FromManagedArray", BindingFlags.Public | BindingFlags.Static); + _disposeMarshalerArray = helperType.GetMethod("DisposeMarshalerArray", BindingFlags.Public | BindingFlags.Static); + _disposeAbiArray = helperType.GetMethod("DisposeAbiArray", BindingFlags.Public | BindingFlags.Static); +#endif + } + + public object CreateMarshaler(T arg) { - var createMarshaler = (Func)HelperType.GetMethod("CreateMarshaler2", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static). - CreateDelegate(typeof(Func)); - return (T arg) => createMarshaler(arg); +#if NET8_0_OR_GREATER + return _createMarshaler.Invoke(null, arg); +#else + return _createMarshaler.Invoke(null, new object[] { arg }); +#endif } - public static readonly Func GetAbi = MarshalByObjectReferenceValueSupported ? (object objRef) => Marshaler.GetAbi(objRef, GetAbiLazy) : - (object objRef) => GetAbiLazy.Value(objRef); - private static readonly Lazy> GetAbiLazy = new(BindGetAbi); - private static Func BindGetAbi() + public object CreateMarshaler2(T arg) { - var getAbi = HelperType.GetMethod("GetAbi", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - return (object arg) => getAbi.Invoke(null, new[] { arg }); +#if NET8_0_OR_GREATER + return _createMarshaler2.Invoke(null, arg); +#else + return _createMarshaler2.Invoke(null, new object[] { arg }); +#endif } - public static readonly Action CopyAbi = (object box, IntPtr dest) => CopyAbiLazy.Value(box, dest); - private static readonly Lazy> CopyAbiLazy = new(BindCopyAbi); - private static Action BindCopyAbi() + public object GetAbi(object arg) { - var copyAbi = HelperType.GetMethod("CopyAbi", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - if (copyAbi == null) return null; - return (object arg, IntPtr dest) => copyAbi.Invoke(null, new[] { arg, dest }); + // In the fallback case (ie. when MarshalGenericFallback is used), we can't know whether the input + // marshaller will actually be an ObjectReferenceValue or not (which could be any other type). So to + // handle all possible cases, we just special case the value marshaller and get the ABI directly. + if (arg is ObjectReferenceValue objectReferenceValue) + { + return objectReferenceValue.GetAbi(); + } + +#if NET8_0_OR_GREATER + return _getAbi.Invoke(null, arg); +#else + return _getAbi.Invoke(null, new object[] { arg }); +#endif + } + + public void CopyAbi(object arg, IntPtr dest) + { +#if NET8_0_OR_GREATER + _ = _copyAbi.Invoke(null, arg, dest); +#else + _ = _copyAbi.Invoke(null, new[] { arg, dest }); +#endif } - public static readonly Func FromAbi = (object box) => FromAbiLazy.Value(box); - private static readonly Lazy> FromAbiLazy = new(BindFromAbi); - private static Func BindFromAbi() + public T FromAbi(object arg) { - var fromAbi = HelperType.GetMethod("FromAbi", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - return (object arg) => (T)fromAbi.Invoke(null, new[] { arg }); +#if NET8_0_OR_GREATER + return (T)_fromAbi.Invoke(null, arg); +#else + return (T)_fromAbi.Invoke(null, new[] { arg }); +#endif } - public static readonly Func FromManaged = (T value) => FromManagedLazy.Value(value); - private static readonly Lazy> FromManagedLazy = new(BindFromManaged); - private static Func BindFromManaged() + public object FromManaged(T arg) { - var fromManaged = HelperType.GetMethod("FromManaged", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - return (T arg) => fromManaged.Invoke(null, new object[] { arg }); +#if NET8_0_OR_GREATER + return _fromManaged.Invoke(null, arg); +#else + return _fromManaged.Invoke(null, new object[] { arg }); +#endif } - public static readonly Action CopyManaged = (T value, IntPtr dest) => CopyManagedLazy.Value(value, dest); - private static readonly Lazy> CopyManagedLazy = new(BindCopyManaged); - private static Action BindCopyManaged() + public void CopyManaged(T arg, IntPtr dest) { - var copyManaged = HelperType.GetMethod("CopyManaged", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - if (copyManaged == null) return null; - return (T arg, IntPtr dest) => copyManaged.Invoke(null, new object[] { arg, dest }); +#if NET8_0_OR_GREATER + _ = _copyManaged.Invoke(null, arg, dest); +#else + _ = _copyManaged.Invoke(null, new object[] { arg, dest }); +#endif } - public static readonly Action DisposeMarshaler = MarshalByObjectReferenceValueSupported ? (object objRef) => Marshaler.DisposeMarshaler(objRef, DisposeMarshalerLazy) : (object objRef) => DisposeMarshalerLazy.Value(objRef); - private static readonly Lazy> DisposeMarshalerLazy = new(BindDisposeMarshaler); - private static Action BindDisposeMarshaler() + public void DisposeMarshaler(object arg) { - var disposeMarshaler = HelperType.GetMethod("DisposeMarshaler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - return (object arg) => disposeMarshaler.Invoke(null, new[] { arg }); + // Same special casing for ObjectReferenceValue as above. + if (arg is ObjectReferenceValue objectReferenceValue) + { + objectReferenceValue.Dispose(); + } + else + { +#if NET8_0_OR_GREATER + _disposeMarshaler.Invoke(null, arg); +#else + _disposeMarshaler.Invoke(null, new[] { arg }); +#endif + } } - internal static readonly Action DisposeAbi = (object box) => DisposeAbiLazy.Value(box); - private static readonly Lazy> DisposeAbiLazy = new(BindDisposeAbi); - private static Action BindDisposeAbi() + public void DisposeAbi(object arg) { - var disposeAbi = HelperType.GetMethod("DisposeAbi", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - if (disposeAbi == null) return null; - return (object arg) => disposeAbi.Invoke(null, new[] { arg }); +#if NET8_0_OR_GREATER + _ = _disposeAbi.Invoke(null, arg); +#else + _ = _disposeAbi.Invoke(null, new object[] { arg }); +#endif } - internal static readonly Func CreateMarshalerArray = (T[] array) => CreateMarshalerArrayLazy.Value(array); - private static readonly Lazy> CreateMarshalerArrayLazy = new(BindCreateMarshalerArray); - private static Func BindCreateMarshalerArray() + public object CreateMarshalerArray(T[] arg) { - var createMarshalerArray = HelperType.GetMethod("CreateMarshalerArray", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - if (createMarshalerArray == null) return null; - return (T[] arg) => createMarshalerArray.Invoke(null, new object[] { arg }); +#if NET8_0_OR_GREATER + return _createMarshalerArray.Invoke(null, arg); +#else + return _createMarshalerArray.Invoke(null, new[] { arg }); +#endif } - internal static readonly Func GetAbiArray = (object box) => GetAbiArrayLazy.Value(box); - private static readonly Lazy> GetAbiArrayLazy = new(BindGetAbiArray); - private static Func BindGetAbiArray() + public (int, IntPtr) GetAbiArray(object arg) { - var getAbiArray = HelperType.GetMethod("GetAbiArray", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - if (getAbiArray == null) return null; - return (object arg) => ((int, IntPtr))getAbiArray.Invoke(null, new object[] { arg }); +#if NET8_0_OR_GREATER + return ((int, IntPtr))_getAbiArray.Invoke(null, arg); +#else + return ((int, IntPtr))_getAbiArray.Invoke(null, new object[] { arg }); +#endif } - internal static readonly Func FromAbiArray = (object box) => FromAbiArrayLazy.Value(box); - private static readonly Lazy> FromAbiArrayLazy = new(BindFromAbiArray); - private static Func BindFromAbiArray() + public T[] FromAbiArray(object arg) { - var fromAbiArray = HelperType.GetMethod("FromAbiArray", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - if (fromAbiArray == null) return null; - return (object arg) => (T[])fromAbiArray.Invoke(null, new[] { arg }); +#if NET8_0_OR_GREATER + return (T[])_fromAbiArray.Invoke(null, arg); +#else + return (T[])_fromAbiArray.Invoke(null, new[] { arg }); +#endif } - internal static readonly Func FromManagedArray = (T[] array) => FromManagedArrayLazy.Value(array); - private static readonly Lazy> FromManagedArrayLazy = new(BindFromManagedArray); - private static Func BindFromManagedArray() + public (int, IntPtr) FromManagedArray(T[] arg) { - var fromManagedArray = HelperType.GetMethod("FromManagedArray", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - if (fromManagedArray == null) return null; - return (T[] arg) => ((int, IntPtr))fromManagedArray.Invoke(null, new object[] { arg }); +#if NET8_0_OR_GREATER + return ((int, IntPtr))_fromManagedArray.Invoke(null, arg); +#else + return ((int, IntPtr))_fromManagedArray.Invoke(null, new object[] { arg }); +#endif } - internal static readonly Action DisposeMarshalerArray = (object box) => DisposeMarshalerArrayLazy.Value(box); - private static readonly Lazy> DisposeMarshalerArrayLazy = new(BindDisposeMarshalerArray); - private static Action BindDisposeMarshalerArray() + public void DisposeMarshalerArray(object arg) { - var disposeMarshalerArray = HelperType.GetMethod("DisposeMarshalerArray", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - if (disposeMarshalerArray == null) return null; - return (object arg) => disposeMarshalerArray.Invoke(null, new object[] { arg }); +#if NET8_0_OR_GREATER + _ = _disposeMarshalerArray.Invoke(null, arg); +#else + _ = _disposeMarshalerArray.Invoke(null, new object[] { arg }); +#endif } - internal static readonly Action DisposeAbiArray = (object box) => DisposeAbiArrayLazy.Value(box); - private static readonly Lazy> DisposeAbiArrayLazy = new(BindDisposeAbiArray); - private static Action BindDisposeAbiArray() + public void DisposeAbiArray(object arg) { - var disposeAbiArray = HelperType.GetMethod("DisposeAbiArray", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); - if (disposeAbiArray == null) return null; - return (object arg) => disposeAbiArray.Invoke(null, new object[] { arg }); +#if NET8_0_OR_GREATER + _ = _disposeAbiArray.Invoke(null, arg); +#else + _ = _disposeAbiArray.Invoke(null, new object[] { arg }); +#endif } + } + /// + /// This type is only meant to be used by generated marshalling stubs. Its API surface might change in the future. + /// + /// The managed type to marshal. + [EditorBrowsable(EditorBrowsableState.Never)] +#if EMBED + internal +#else + public +#endif + static class MarshalGenericHelper + { private static unsafe void CopyManagedFallback(T value, IntPtr dest) { - if (MarshalByObjectReferenceValueSupported) + if (MarshalGeneric.MarshalByObjectReferenceValueSupported) { *(IntPtr*)dest.ToPointer() = - (value is null) ? IntPtr.Zero : ((ObjectReferenceValue)CreateMarshaler2(value)).Detach(); + (value is null) ? IntPtr.Zero : ((ObjectReferenceValue)MarshalGeneric.CreateMarshaler2(value)).Detach(); } else { *(IntPtr*)dest.ToPointer() = - (value is null) ? IntPtr.Zero : ((IObjectReference)CreateMarshaler(value)).GetRef(); + (value is null) ? IntPtr.Zero : ((IObjectReference)MarshalGeneric.CreateMarshaler(value)).GetRef(); } } - internal static unsafe void CopyManagedArray(T[] array, IntPtr data) => MarshalInterfaceHelper.CopyManagedArray(array, data, CopyManagedLazy.Value ?? CopyManagedFallback); + public static unsafe void CopyManagedArray(T[] array, IntPtr data) => MarshalInterfaceHelper.CopyManagedArray(array, data, MarshalGeneric.CopyManaged ?? CopyManagedFallback); } #if EMBED @@ -612,7 +1053,58 @@ private static unsafe void CopyManagedFallback(T value, IntPtr dest) #endif class MarshalNonBlittable : MarshalGeneric { - private static readonly new Type AbiType = typeof(T).IsEnum ? Enum.GetUnderlyingType(typeof(T)) : MarshalGeneric.AbiType; + private static readonly new Type AbiType = GetAbiType(); + + private static Type GetAbiType() + { + if (typeof(T).IsEnum) + { + return Enum.GetUnderlyingType(typeof(T)); + } + + // These 5 types are true non blittable types that are valid to use here + if (typeof(T) == typeof(bool)) return typeof(byte); + if (typeof(T) == typeof(char)) return typeof(ushort); + if (typeof(T) == typeof(global::System.TimeSpan)) return typeof(global::ABI.System.TimeSpan); + if (typeof(T) == typeof(DateTimeOffset)) return typeof(global::ABI.System.DateTimeOffset); + if (typeof(T) == typeof(global::System.Exception)) return typeof(global::ABI.System.Exception); + + // These types are actually blittable, but this marshaller is still constructed elsewhere. + // Just return null instead of using MarshalGeneric, to avoid constructing that too. + if (typeof(T) == typeof(int) || + typeof(T) == typeof(byte) || + typeof(T) == typeof(sbyte) || + typeof(T) == typeof(short) || + typeof(T) == typeof(ushort) || + typeof(T) == typeof(uint) || + typeof(T) == typeof(long) || + typeof(T) == typeof(ulong) || + typeof(T) == typeof(float) || + typeof(T) == typeof(double) || + typeof(T) == typeof(Guid) || + typeof(T) == typeof(global::Windows.Foundation.Point) || + typeof(T) == typeof(global::Windows.Foundation.Rect) || + typeof(T) == typeof(global::Windows.Foundation.Size) || + typeof(T) == typeof(global::System.Numerics.Matrix3x2) || + typeof(T) == typeof(global::System.Numerics.Matrix4x4) || + typeof(T) == typeof(global::System.Numerics.Plane) || + typeof(T) == typeof(global::System.Numerics.Quaternion) || + typeof(T) == typeof(global::System.Numerics.Vector2) || + typeof(T) == typeof(global::System.Numerics.Vector3) || + typeof(T) == typeof(global::System.Numerics.Vector4)) + { + return null; + } + + // Exclude Type, as it has dedicated marshalling code available from Marshaler + if (typeof(T) == typeof(Type)) + { + throw new NotSupportedException("Using 'System.Type' with MarshalNonBlittable isn't supported, use Marshaler instead."); + } + + // Fallback path with the original logic + return typeof(T).GetAbiType(); + } public struct MarshalerArray { @@ -622,7 +1114,10 @@ public void Dispose() { foreach (var marshaler in _marshalers) { - Marshaler.DisposeMarshaler(marshaler); + // We make use of MarshalNonBlittable for array marshaling when T is non-blittable or when it is an enum. + // Both scenarios are handled by MarshalNonBlittable for marshaling T itself, so we just directly use that + // here and below without needing to go through Marshaler. + MarshalNonBlittable.DisposeMarshaler(marshaler); } } if (_array != IntPtr.Zero) @@ -637,6 +1132,12 @@ public void Dispose() public static new unsafe MarshalerArray CreateMarshalerArray(T[] array) { +#if NET && !NET9_0_OR_GREATER + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"Cannot handle array marshalling for non blittable type '{typeof(T)}'."); + } +#endif MarshalerArray m = new MarshalerArray(); if (array is null) { @@ -646,17 +1147,24 @@ public void Dispose() try { int length = array.Length; - var abi_element_size = Marshal.SizeOf(AbiType); +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + var abi_element_size = +#if NET9_0_OR_GREATER + RuntimeHelpers.SizeOf(AbiType.TypeHandle); +#else + Marshal.SizeOf(AbiType); +#endif var byte_length = length * abi_element_size; m._array = Marshal.AllocCoTaskMem(byte_length); m._marshalers = new object[length]; var element = (byte*)m._array.ToPointer(); for (int i = 0; i < length; i++) { - m._marshalers[i] = Marshaler.CreateMarshaler(array[i]); - Marshaler.CopyAbi(m._marshalers[i], (IntPtr)element); + m._marshalers[i] = MarshalNonBlittable.CreateMarshaler(array[i]); + MarshalNonBlittable.CopyAbi(m._marshalers[i], (IntPtr)element); element += abi_element_size; } +#pragma warning restore IL3050 success = true; return m; } @@ -677,6 +1185,12 @@ public void Dispose() public static new unsafe T[] FromAbiArray(object box) { +#if NET && !NET9_0_OR_GREATER + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"Cannot handle array marshalling for non blittable type '{typeof(T)}'."); + } +#endif if (box is null) { return null; @@ -688,35 +1202,71 @@ public void Dispose() } var array = new T[abi.length]; var data = (byte*)abi.data.ToPointer(); - var abi_element_size = Marshal.SizeOf(AbiType); +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + var abi_element_size = +#if NET9_0_OR_GREATER + RuntimeHelpers.SizeOf(AbiType.TypeHandle); +#else + Marshal.SizeOf(AbiType); +#endif for (int i = 0; i < abi.length; i++) { - var abi_element = Marshal.PtrToStructure((IntPtr)data, AbiType); - array[i] = Marshaler.FromAbi(abi_element); + var abi_element = +#if NET9_0_OR_GREATER + RuntimeHelpers.Box(ref *data, AbiType.TypeHandle); +#else + Marshal.PtrToStructure((IntPtr)data, AbiType); +#endif + array[i] = MarshalNonBlittable.FromAbi(abi_element); data += abi_element_size; } +#pragma warning restore IL3050 return array; } public static unsafe void CopyAbiArray(T[] array, object box) { +#if NET && !NET9_0_OR_GREATER + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"Cannot handle array marshalling for non blittable type '{typeof(T)}'."); + } +#endif var abi = ((int length, IntPtr data))box; if (abi.data == IntPtr.Zero) { return; } var data = (byte*)abi.data.ToPointer(); - var abi_element_size = Marshal.SizeOf(AbiType); +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + var abi_element_size = +#if NET9_0_OR_GREATER + RuntimeHelpers.SizeOf(AbiType.TypeHandle); +#else + Marshal.SizeOf(AbiType); +#endif for (int i = 0; i < abi.length; i++) { - var abi_element = Marshal.PtrToStructure((IntPtr)data, AbiType); - array[i] = Marshaler.FromAbi(abi_element); + var abi_element = +#if NET9_0_OR_GREATER + RuntimeHelpers.Box(ref *data, AbiType.TypeHandle); +#else + Marshal.PtrToStructure((IntPtr)data, AbiType); +#endif + array[i] = MarshalNonBlittable.FromAbi(abi_element); data += abi_element_size; } +#pragma warning restore IL3050 } public static new unsafe (int length, IntPtr data) FromManagedArray(T[] array) { +#if NET && !NET9_0_OR_GREATER + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"Cannot handle array marshalling for non blittable type '{typeof(T)}'."); + } +#endif if (array is null) { return (0, IntPtr.Zero); @@ -727,15 +1277,22 @@ public static unsafe void CopyAbiArray(T[] array, object box) try { int length = array.Length; - var abi_element_size = Marshal.SizeOf(AbiType); +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + var abi_element_size = +#if NET9_0_OR_GREATER + RuntimeHelpers.SizeOf(AbiType.TypeHandle); +#else + Marshal.SizeOf(AbiType); +#endif var byte_length = length * abi_element_size; data = Marshal.AllocCoTaskMem(byte_length); var bytes = (byte*)data.ToPointer(); for (i = 0; i < length; i++) { - Marshaler.CopyManaged(array[i], (IntPtr)bytes); + MarshalNonBlittable.CopyManaged(array[i], (IntPtr)bytes); bytes += abi_element_size; } +#pragma warning restore IL3050 success = true; return (i, data); } @@ -748,8 +1305,14 @@ public static unsafe void CopyAbiArray(T[] array, object box) } } - public static new unsafe void CopyManagedArray(T[] array, IntPtr data) + public static unsafe void CopyManagedArray(T[] array, IntPtr data) { +#if NET && !NET9_0_OR_GREATER + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"Cannot handle array marshalling for non blittable type '{typeof(T)}'."); + } +#endif if (array is null) { return; @@ -760,14 +1323,20 @@ public static unsafe void CopyAbiArray(T[] array, object box) try { int length = array.Length; - var abi_element_size = Marshal.SizeOf(AbiType); - var byte_length = length * abi_element_size; +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + var abi_element_size = +#if NET9_0_OR_GREATER + RuntimeHelpers.SizeOf(AbiType.TypeHandle); +#else + Marshal.SizeOf(AbiType); +#endif var bytes = (byte*)data.ToPointer(); for (i = 0; i < length; i++) { - Marshaler.CopyManaged(array[i], (IntPtr)bytes); + MarshalNonBlittable.CopyManaged(array[i], (IntPtr)bytes); bytes += abi_element_size; } +#pragma warning restore IL3050 success = true; } finally @@ -783,14 +1352,31 @@ public static unsafe void CopyAbiArray(T[] array, object box) public static unsafe void DisposeAbiArrayElements((int length, IntPtr data) abi) { +#if NET && !NET9_0_OR_GREATER + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"Cannot handle array marshalling for non blittable type '{typeof(T)}'."); + } +#endif var data = (byte*)abi.data.ToPointer(); - var abi_element_size = Marshal.SizeOf(AbiType); +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + var abi_element_size = +#if NET9_0_OR_GREATER + RuntimeHelpers.SizeOf(AbiType.TypeHandle); +#else + Marshal.SizeOf(AbiType); +#endif for (int i = 0; i < abi.length; i++) { - var abi_element = Marshal.PtrToStructure((IntPtr)data, AbiType); - Marshaler.DisposeAbi(abi_element); + var abi_element = +#if NET9_0_OR_GREATER + RuntimeHelpers.Box(ref *data, AbiType.TypeHandle); +#else + Marshal.PtrToStructure((IntPtr)data, AbiType); +#endif data += abi_element_size; } +#pragma warning restore IL3050 } public static new unsafe void DisposeAbiArray(object box) @@ -1045,15 +1631,44 @@ struct MarshalInterface { #if NET [DynamicallyAccessedMembers( - DynamicallyAccessedMemberTypes.PublicFields | - DynamicallyAccessedMemberTypes.NonPublicFields | - DynamicallyAccessedMemberTypes.PublicNestedTypes | - DynamicallyAccessedMemberTypes.PublicMethods | - DynamicallyAccessedMemberTypes.NonPublicMethods)] + DynamicallyAccessedMemberTypes.PublicFields | + DynamicallyAccessedMemberTypes.PublicMethods)] +#endif + private static Type _HelperType; + +#if NET + [DynamicallyAccessedMembers( + DynamicallyAccessedMemberTypes.PublicFields | + DynamicallyAccessedMemberTypes.PublicMethods)] #endif - private static readonly Type HelperType = typeof(T).GetHelperType(); - private static Func _ToAbi; - private static Func _CreateMarshaler; + private static Type HelperType => _HelperType ??= typeof(T).GetHelperType(); + + private static object _CreateMarshaler; + + // We are here using CreateIID on the projected type rather than GetIID on the helper type + // This allows us to avoid needing to do MakeGenericType calls or + // registration of helper types for projected generic types like System.Nullable<>. + // By using CreateIID with the projected type, we are still able to get the IID + // but at the same time don't need the generic instance of the helper type to do so. + // In addition, other than for that, we don't need the helper type in MarshalInterface. + // This does mean we are doing the PIID calculation here instead of using the cached + // one in the helper type, but we are also caching it here too so it should be only + // one additional call. + private static object _Iid; + private static Guid IID => (Guid)(_Iid ??= GetIID()); + + private static Guid GetIID() + { + // The JIT and linker cannot fully combine the 'IsGenericType' and generic type definition checks, and we don't + // want to root unnecessary code here for the shared generic instantiation. So we can give them a little nudge + // by also explicitly checking whether 'T' is a value type. If it is not, the whole branch will short-cirtuit. + if (typeof(T).IsValueType && typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(Nullable<>)) + { + return GuidGenerator.CreateIIDUnsafe(typeof(T)); + } + + return GuidGenerator.GetIID(HelperType); + } public static T FromAbi(IntPtr ptr) { @@ -1072,27 +1687,19 @@ public static IObjectReference CreateMarshaler(T value) return null; } - // If the value passed in is the native implementation of the interface - // use the ToAbi delegate since it will be faster than reflection. - if (value.GetType() == HelperType) +#if !NET + if (TryGetObjFieldValue(value, out IObjectReference objectReference)) { - if (_ToAbi == null) - { - _ToAbi = BindToAbi(); - } - var ptr = _ToAbi(value).GetRef(); + IntPtr ptr = objectReference.GetRef(); + // We can use ObjectReference.Attach here since this API is // only used during marshalling where we deterministically dispose // on the same thread (and as a result don't need to capture context). return ObjectReference.Attach(ref ptr); } +#endif - if (_CreateMarshaler is null) - { - _CreateMarshaler = BindCreateMarshaler(); - } - - return _CreateMarshaler(value); + return CreateMarshalerCore(value); } public static ObjectReferenceValue CreateMarshaler2(T value, Guid iid = default) @@ -1102,18 +1709,14 @@ public static ObjectReferenceValue CreateMarshaler2(T value, Guid iid = default) return new ObjectReferenceValue(); } - // If the value passed in is the native implementation of the interface - // use the ToAbi delegate since it will be faster than reflection. - if (value.GetType() == HelperType) +#if !NET + if (TryGetObjFieldValue(value, out IObjectReference objectReference)) { - if (_ToAbi == null) - { - _ToAbi = BindToAbi(); - } - return _ToAbi(value).AsValue(); + return objectReference.AsValue(); } +#endif - return MarshalInspectable.CreateMarshaler2(value, iid == default ? GuidGenerator.GetIID(HelperType) : iid, true); + return MarshalInspectable.CreateMarshaler2(value, iid == default ? IID : iid, true); } public static IntPtr GetAbi(IObjectReference value) => @@ -1157,57 +1760,79 @@ public static unsafe void CopyManaged(T value, IntPtr dest) public static unsafe void CopyAbiArray(T[] array, object box) => MarshalInterfaceHelper.CopyAbiArray(array, box, FromAbi); - public static unsafe (int length, IntPtr data) FromManagedArray(T[] array) => MarshalInterfaceHelper.FromManagedArray(array, (o) => FromManaged(o)); + public static unsafe (int length, IntPtr data) FromManagedArray(T[] array) => MarshalInterfaceHelper.FromManagedArray(array, FromManaged); - public static unsafe void CopyManagedArray(T[] array, IntPtr data) => MarshalInterfaceHelper.CopyManagedArray(array, data, (o, dest) => CopyManaged(o, dest)); + public static unsafe void CopyManagedArray(T[] array, IntPtr data) => MarshalInterfaceHelper.CopyManagedArray(array, data, CopyManaged); public static void DisposeMarshalerArray(object box) => MarshalInterfaceHelper.DisposeMarshalerArray(box); public static unsafe void DisposeAbiArray(object box) => MarshalInterfaceHelper.DisposeAbiArray(box); - private static Func BindToAbi() +#if !NET + private static FieldInfo _ObjField; + private static bool TryGetObjFieldValue(T value, out IObjectReference objectReference) { - var objField = HelperType.GetField("_obj", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly); - return (T arg) => (IObjectReference) objField.GetValue(arg); + // If the value passed in is the native implementation of the interface, + // cache the '_obj' field and access it directly rather using a marshaler. + if (value.GetType() == HelperType) + { + _ObjField ??= HelperType.GetField("_obj", BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly); + + objectReference = (IObjectReference)_ObjField.GetValue(value); + + return true; + } + + objectReference = null; + + return false; } +#endif + + private static IObjectReference CreateMarshalerCore(T value) + { +#if NET + // On NativeAOT, we can inline everything and skip creating any delegates + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return MarshalInspectable.CreateMarshaler(value, IID, true); + } +#endif + // Otherwise, just use the fallback path +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + _CreateMarshaler ??= BindCreateMarshaler(); +#pragma warning restore IL3050 + return ((Func)_CreateMarshaler)(value); + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif private static Func BindCreateMarshaler() { var vftblType = HelperType.FindVftblType(); - Guid iid = GuidGenerator.GetIID(HelperType); + if (vftblType is not null) { var methodInfo = typeof(MarshalInspectable).GetMethod("CreateMarshaler", new Type[] { typeof(T), typeof(Guid), typeof(bool) }). MakeGenericMethod(vftblType); - var createMarshaler = (Func) methodInfo.CreateDelegate(typeof(Func)); - return obj => createMarshaler(obj, iid, true); - } - else - { - return obj => MarshalInspectable.CreateMarshaler(obj, iid, true); + var createMarshaler = (Func)methodInfo.CreateDelegate(typeof(Func)); + return obj => createMarshaler(obj, IID, true); } + + return obj => MarshalInspectable.CreateMarshaler(obj, IID, true); } } #if EMBED internal -#else +#else public #endif - static class MarshalInspectable< -#if NET6_0_OR_GREATER - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces | DynamicallyAccessedMemberTypes.NonPublicConstructors)] -#elif NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] -#endif - T> + static class MarshalInspectable { public static IObjectReference CreateMarshaler( -#if NET6_0_OR_GREATER - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#elif NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] -#endif T o, Guid iid, bool unwrapObject = true) @@ -1225,7 +1850,7 @@ public static IObjectReference CreateMarshaler( Type helperType = Projections.FindCustomHelperTypeMapping(publicType, true); if (helperType != null) { - var createMarshaler = helperType.GetMethod("CreateMarshaler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + var createMarshaler = helperType.GetMethod("CreateMarshaler", BindingFlags.Public | BindingFlags.Static); return (IObjectReference) createMarshaler.Invoke(null, new[] { (object) o }); } @@ -1233,23 +1858,13 @@ public static IObjectReference CreateMarshaler( } public static IObjectReference CreateMarshaler( -#if NET6_0_OR_GREATER - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#elif NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] -#endif T o, bool unwrapObject = true) { - return CreateMarshaler(o, InterfaceIIDs.IInspectable_IID, unwrapObject); + return CreateMarshaler(o, IID.IID_IInspectable, unwrapObject); } public static ObjectReferenceValue CreateMarshaler2( -#if NET6_0_OR_GREATER - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#elif NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] -#endif T o, Guid iid, bool unwrapObject = true) @@ -1267,7 +1882,7 @@ public static ObjectReferenceValue CreateMarshaler2( Type helperType = Projections.FindCustomHelperTypeMapping(publicType, true); if (helperType != null) { - var createMarshaler = helperType.GetMethod("CreateMarshaler2", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + var createMarshaler = helperType.GetMethod("CreateMarshaler2", BindingFlags.Public | BindingFlags.Static); return ((ObjectReferenceValue)createMarshaler.Invoke(null, new[] { (object)o })); } @@ -1275,12 +1890,7 @@ public static ObjectReferenceValue CreateMarshaler2( } public static ObjectReferenceValue CreateMarshaler2( -#if NET6_0_OR_GREATER - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#elif NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)] -#endif - T o, bool unwrapObject = true) => CreateMarshaler2(o, InterfaceIIDs.IInspectable_IID, unwrapObject); + T o, bool unwrapObject = true) => CreateMarshaler2(o, IID.IID_IInspectable, unwrapObject); public static IntPtr GetAbi(IObjectReference objRef) => objRef is null ? IntPtr.Zero : MarshalInterfaceHelper.GetAbi(objRef); @@ -1297,11 +1907,22 @@ public static T FromAbi(IntPtr ptr) IntPtr iunknownPtr = IntPtr.Zero; try { - Guid iid_iunknown = IUnknownVftbl.IID; - Marshal.QueryInterface(ptr, ref iid_iunknown, out iunknownPtr); + Marshal.QueryInterface(ptr, ref Unsafe.AsRef(in IID.IID_IUnknown), out iunknownPtr); if (IUnknownVftbl.IsReferenceToManagedObject(iunknownPtr)) { - return (T)ComWrappersSupport.FindObject(iunknownPtr); + // We use a global instance of ComWrappers, but it's possible to use different projections of the same type + // when both server and client are managed and in the same process. + // In this case, we need to check if the object is of the same type as the one we're trying to cast to. + // If it's not, we need to create an RCW for the object. + // We cannot use T directly here as it may lead to invalid cast due to the above reason. + if (ComWrappersSupport.FindObject(iunknownPtr) is T obj) + { + return obj; + } + else + { + return ComWrappersSupport.CreateRcwForComObject(ptr); + } } else { @@ -1351,6 +1972,8 @@ public static unsafe void CopyManaged(T o, IntPtr dest, bool unwrapObject = true internal static unsafe T[] FromAbiArray(object box) => MarshalInterfaceHelper.FromAbiArray(box, FromAbi); + public static unsafe void CopyAbiArray(T[] array, object box) => MarshalInterfaceHelper.CopyAbiArray(array, box, FromAbi); + public static unsafe (int length, IntPtr data) FromManagedArray(T[] array) => MarshalInterfaceHelper.FromManagedArray(array, (o) => FromManaged(o)); public static unsafe void CopyManagedArray(T[] array, IntPtr data) => MarshalInterfaceHelper.CopyManagedArray(array, data, (o, dest) => CopyManaged(o, dest)); @@ -1381,10 +2004,10 @@ public static IObjectReference CreateMarshaler(object o, Guid delegateIID, bool if (unwrapObject && ComWrappersSupport.TryUnwrapObject(o, out var objRef)) { - return objRef.As(delegateIID); + return objRef.As(delegateIID); } - return ComWrappersSupport.CreateCCWForObject(o, delegateIID); + return ComWrappersSupport.CreateCCWForObject(o, delegateIID); } public static ObjectReferenceValue CreateMarshaler2(object o, Guid delegateIID, bool unwrapObject = true) @@ -1403,45 +2026,56 @@ public static ObjectReferenceValue CreateMarshaler2(object o, Guid delegateIID, } public static T FromAbi(IntPtr nativeDelegate) - where T : System.Delegate + where T : Delegate { if (nativeDelegate == IntPtr.Zero) { return null; } - else if (IUnknownVftbl.IsReferenceToManagedObject(nativeDelegate)) + + if (IUnknownVftbl.IsReferenceToManagedObject(nativeDelegate)) { - return ComWrappersSupport.FindObject(nativeDelegate); - } - else - { - return ComWrappersSupport.CreateRcwForComObject(nativeDelegate); + // We use a global instance of ComWrappers, but it's possible to use different projections of the same type + // when both server and client are managed and in the same process. + // In this case, we need to check if the object is of the same type as the one we're trying to cast to. + // If it's not, we need to create an RCW for the object. + // We cannot use T directly here as it may lead to invalid cast due to the above reason. + if (ComWrappersSupport.FindObject(nativeDelegate) is T obj) + { + return obj; + } + else + { + return ComWrappersSupport.CreateRcwForComObject(nativeDelegate); + } } + + return ComWrappersSupport.CreateRcwForComObject(nativeDelegate); } } internal static class Marshaler { - internal static Action EmptyFunc = (object box) => { }; - internal static Func ReturnParameterFunc = (object box) => box; - internal static unsafe Action CopyIntEnumFunc = - (object value, IntPtr dest) => *(int*)dest.ToPointer() = (int)Convert.ChangeType(value, typeof(int)); - internal static unsafe Action CopyUIntEnumFunc = - (object value, IntPtr dest) => *(uint*)dest.ToPointer() = (uint)Convert.ChangeType(value, typeof(uint)); - internal static Action>> DisposeMarshaler = (object arg, Lazy> genericDisposeMarshaler) => - { - if (arg is ObjectReferenceValue objectReferenceValue) - { - objectReferenceValue.Dispose(); - } - else - { - genericDisposeMarshaler.Value(arg); - } - }; - internal static Func>, object> GetAbi = (object arg, Lazy> genericGetAbi) => arg is ObjectReferenceValue objectReferenceValue ? objectReferenceValue.GetAbi() : genericGetAbi.Value(arg); + internal static readonly Func ReturnParameterFunc = ReturnParameter; + internal static readonly Action CopyIntEnumFunc = CopyIntEnum; + internal static readonly Action CopyIntEnumDirectFunc = CopyIntEnumDirect; + internal static readonly Action CopyUIntEnumFunc = CopyUIntEnum; + internal static readonly Action CopyUIntEnumDirectFunc = CopyUIntEnumDirect; + + private static object ReturnParameter(object arg) => arg; + + private static unsafe void CopyIntEnum(object value, IntPtr dest) => *(int*)dest.ToPointer() = Convert.ToInt32(value); + + private static unsafe void CopyIntEnumDirect(object value, IntPtr dest) => *(int*)dest.ToPointer() = (int)value; + + private static unsafe void CopyUIntEnum(object value, IntPtr dest) => *(uint*)dest.ToPointer() = Convert.ToUInt32(value); + + private static unsafe void CopyUIntEnumDirect(object value, IntPtr dest) => *(uint*)dest.ToPointer() = (uint)value; } +#if NET + [EditorBrowsable(EditorBrowsableState.Never)] +#endif #if EMBED internal #else @@ -1451,53 +2085,40 @@ class Marshaler { static Marshaler() { - Type type = typeof(T); +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException( + $"'Marshaler' is not supported in AOT environments, and is only supported for backwards compatibility in JIT environments. " + + $"The type '{typeof(T)}' cannot be marshalled using it. Consider using the appropriate, more specific marshaller type instead."); + } +#endif - // structs cannot contain arrays, and arrays may only ever appear as parameters - if (type.IsArray) + // Structs cannot contain arrays, and arrays may only ever appear as parameters + if (typeof(T).IsArray) { throw new InvalidOperationException("Arrays may not be marshaled generically."); } - if (type == typeof(String)) + if (typeof(T) == typeof(string)) { AbiType = typeof(IntPtr); - CreateMarshaler = (T value) => MarshalString.CreateMarshaler((string)(object)value); + CreateMarshaler = (Func)(object)(new Func(MarshalString.CreateMarshaler)); CreateMarshaler2 = CreateMarshaler; GetAbi = (object box) => MarshalString.GetAbi(box); FromAbi = (object value) => (T)(object)MarshalString.FromAbi((IntPtr)value); FromManaged = (T value) => MarshalString.FromManaged((string)(object)value); - DisposeMarshaler = (object box) => MarshalString.DisposeMarshaler(box); - DisposeAbi = (object box) => MarshalString.DisposeAbi(box); + DisposeMarshaler = MarshalString.DisposeMarshaler; + DisposeAbi = MarshalString.DisposeAbi; CreateMarshalerArray = (T[] array) => MarshalString.CreateMarshalerArray((string[])(object)array); - GetAbiArray = (object box) => MarshalString.GetAbiArray(box); - FromAbiArray = (object box) => (T[])(object)MarshalString.FromAbiArray(box); - FromManagedArray = (T[] array) => MarshalString.FromManagedArray((string[])(object)array); - CopyManagedArray = (T[] array, IntPtr data) => MarshalString.CopyManagedArray((string[])(object)array, data); - DisposeMarshalerArray = (object box) => MarshalString.DisposeMarshalerArray(box); - DisposeAbiArray = (object box) => MarshalString.DisposeAbiArray(box); - } - else if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Collections.Generic.KeyValuePair<,>)) - { - AbiType = typeof(IntPtr); - CreateMarshaler = MarshalGeneric.CreateMarshaler2; - CreateMarshaler2 = MarshalGeneric.CreateMarshaler2; - GetAbi = MarshalGeneric.GetAbi; - CopyAbi = MarshalGeneric.CopyAbi; - FromAbi = MarshalGeneric.FromAbi; - FromManaged = MarshalGeneric.FromManaged; - CopyManaged = MarshalGeneric.CopyManaged; - DisposeMarshaler = MarshalGeneric.DisposeMarshaler; - DisposeAbi = MarshalGeneric.DisposeAbi; - CreateMarshalerArray = MarshalGeneric.CreateMarshalerArray; - GetAbiArray = MarshalGeneric.GetAbiArray; - FromAbiArray = MarshalGeneric.FromAbiArray; - FromManagedArray = MarshalGeneric.FromManagedArray; - CopyManagedArray = (T[] array, IntPtr data) => MarshalGeneric.CopyManagedArray(array, data); - DisposeMarshalerArray = (object box) => MarshalInterface.DisposeMarshalerArray(box); - DisposeAbiArray = (object box) => MarshalInterface.DisposeAbiArray(box); + GetAbiArray = MarshalString.GetAbiArray; + FromAbiArray = (Func)(object)new Func(MarshalString.FromAbiArray); + FromManagedArray = (Func)(object)new Func(MarshalString.FromManagedArray); + CopyManagedArray = (Action)(object)new Action(MarshalString.CopyManagedArray); + DisposeMarshalerArray = MarshalString.DisposeMarshalerArray; + DisposeAbiArray = MarshalString.DisposeAbiArray; } - else if (type == typeof(Type)) + else if (typeof(T) == typeof(Type)) { AbiType = typeof(ABI.System.Type); CreateMarshaler = (T value) => ABI.System.Type.CreateMarshaler((Type)(object)value); @@ -1505,62 +2126,213 @@ static Marshaler() GetAbi = (object box) => ABI.System.Type.GetAbi((ABI.System.Type.Marshaler)box); FromAbi = (object value) => (T)(object)ABI.System.Type.FromAbi((ABI.System.Type)value); CopyAbi = (object box, IntPtr dest) => ABI.System.Type.CopyAbi((ABI.System.Type.Marshaler)box, dest); - CopyManaged = (T value, IntPtr dest) => ABI.System.Type.CopyManaged((global::System.Type)(object)value, dest); + CopyManaged = (Action)(object)new Action(ABI.System.Type.CopyManaged); FromManaged = (T value) => ABI.System.Type.FromManaged((Type)(object)value); DisposeMarshaler = (object box) => ABI.System.Type.DisposeMarshaler((ABI.System.Type.Marshaler)box); DisposeAbi = (object box) => ABI.System.Type.DisposeAbi((ABI.System.Type)box); - CreateMarshalerArray = (T[] array) => MarshalNonBlittable.CreateMarshalerArray(array); - GetAbiArray = (object box) => MarshalNonBlittable.GetAbiArray(box); - FromAbiArray = (object box) => MarshalNonBlittable.FromAbiArray(box); - FromManagedArray = (T[] array) => MarshalNonBlittable.FromManagedArray(array); - CopyManagedArray = (T[] array, IntPtr data) => MarshalNonBlittable.CopyManagedArray(array, data); - DisposeMarshalerArray = (object box) => MarshalNonBlittable.DisposeMarshalerArray(box); - DisposeAbiArray = (object box) => MarshalNonBlittable.DisposeAbiArray(box); - } - else if (type.IsValueType) - { - AbiType = type.FindHelperType(); - if (AbiType != null) + CreateMarshalerArray = (Func)(object)new Func(ABI.System.NonBlittableMarshallingStubs.Type_CreateMarshalerArray); + GetAbiArray = new Func(ABI.System.Type.GetAbiArray); + FromAbiArray = (Func)(object)new Func(ABI.System.Type.FromAbiArray); + FromManagedArray = (Func)(object)new Func(ABI.System.Type.FromManagedArray); + CopyManagedArray = (Action)(object)new Action(ABI.System.Type.CopyManagedArray); + DisposeMarshalerArray = new Action(ABI.System.Type.DisposeMarshalerArray); + DisposeAbiArray = new Action(ABI.System.Type.DisposeAbiArray); + } + else if (typeof(Exception).IsAssignableFrom(typeof(T))) + { + AbiType = typeof(ABI.System.Exception); + CreateMarshaler = (T value) => ABI.System.Exception.CreateMarshaler((Exception)(object)value); + CreateMarshaler2 = CreateMarshaler; + GetAbi = (object box) => ABI.System.Exception.GetAbi((ABI.System.Exception.Marshaler)box); + FromAbi = (object value) => (T)(object)ABI.System.Exception.FromAbi((ABI.System.Exception)value); + CopyAbi = (object box, IntPtr dest) => ABI.System.Exception.CopyAbi((ABI.System.Exception.Marshaler)box, dest); + CopyManaged = (Action)(object)new Action(ABI.System.Exception.CopyManaged); + FromManaged = (T value) => ABI.System.Exception.FromManaged((Exception)(object)value); + DisposeMarshaler = (object box) => ABI.System.Exception.DisposeMarshaler((ABI.System.Exception.Marshaler)box); + DisposeAbi = (object box) => ABI.System.Exception.DisposeAbi((ABI.System.Exception)box); + CreateMarshalerArray = (Func)(object)new Func(ABI.System.NonBlittableMarshallingStubs.Exception_CreateMarshalerArray); + GetAbiArray = new Func(MarshalNonBlittable.GetAbiArray); + FromAbiArray = (Func)(object)new Func(MarshalNonBlittable.FromAbiArray); + FromManagedArray = (Func)(object)new Func(MarshalNonBlittable.FromManagedArray); + CopyManagedArray = (Action)(object)new Action(MarshalNonBlittable.CopyManagedArray); + DisposeMarshalerArray = new Action(MarshalNonBlittable.DisposeMarshalerArray); + DisposeAbiArray = new Action(MarshalNonBlittable.DisposeAbiArray); + } + else if (typeof(T).IsValueType) + { + if (typeof(T) == typeof(bool)) + { + AbiType = typeof(byte); + } + else if (typeof(T) == typeof(char)) { + AbiType = typeof(ushort); + } + else if (typeof(T) == typeof(int) || + typeof(T) == typeof(byte) || + typeof(T) == typeof(sbyte) || + typeof(T) == typeof(short) || + typeof(T) == typeof(ushort) || + typeof(T) == typeof(uint) || + typeof(T) == typeof(long) || + typeof(T) == typeof(ulong) || + typeof(T) == typeof(float) || + typeof(T) == typeof(double) || + typeof(T) == typeof(Guid) || + typeof(T) == typeof(global::Windows.Foundation.Point) || + typeof(T) == typeof(global::Windows.Foundation.Rect) || + typeof(T) == typeof(global::Windows.Foundation.Size) || + typeof(T) == typeof(global::System.Numerics.Matrix3x2) || + typeof(T) == typeof(global::System.Numerics.Matrix4x4) || + typeof(T) == typeof(global::System.Numerics.Plane) || + typeof(T) == typeof(global::System.Numerics.Quaternion) || + typeof(T) == typeof(global::System.Numerics.Vector2) || + typeof(T) == typeof(global::System.Numerics.Vector3) || + typeof(T) == typeof(global::System.Numerics.Vector4)) + { + // Manually handle well known primitive types and common types, as well + // as two common projected types (below). This allows the linker to trim + // all the non-taken branch below, which it wouldn't otherwise do, because + // the path below with the fallback logic to check for ABI types is dynamic. + AbiType = null; + } + else if (typeof(T) == typeof(global::System.TimeSpan)) + { + AbiType = typeof(global::ABI.System.TimeSpan); + } + else if (typeof(T) == typeof(global::System.DateTimeOffset)) + { + AbiType = typeof(global::ABI.System.DateTimeOffset); + } + else if (typeof(T).IsGenericType && typeof(T).GetGenericTypeDefinition() == typeof(System.Collections.Generic.KeyValuePair<,>)) + { + // This check for KeyValuePair<,> types cannot be statically determined, so we move it + // down to still allow the linker to see more possible branches before. This should + // avoid constructing all of these MarshalGeneric types when not actually needed. + // Because this is a more specific case of the value type check, which is already + // recognized by the linker, we can put it here to further improve trimming. We just + // also set the ref type and then return because if we do have a KeyValuePair<,> type, + // we have already set all the marshalling delegates we need, and we can just stop here. + AbiType = typeof(IntPtr); + CreateMarshaler = MarshalGeneric.CreateMarshaler2; + CreateMarshaler2 = MarshalGeneric.CreateMarshaler2; + GetAbi = MarshalGeneric.GetAbi; + CopyAbi = MarshalGeneric.CopyAbi; + FromAbi = MarshalGeneric.FromAbi; + FromManaged = MarshalGeneric.FromManaged; + CopyManaged = MarshalGeneric.CopyManaged; + DisposeMarshaler = MarshalGeneric.DisposeMarshaler; + DisposeAbi = MarshalGeneric.DisposeAbi; + CreateMarshalerArray = MarshalGeneric.CreateMarshalerArray; + GetAbiArray = MarshalGeneric.GetAbiArray; + FromAbiArray = MarshalGeneric.FromAbiArray; + FromManagedArray = MarshalGeneric.FromManagedArray; + CopyManagedArray = MarshalGenericHelper.CopyManagedArray; + DisposeMarshalerArray = MarshalInterface.DisposeMarshalerArray; + DisposeAbiArray = MarshalInterface.DisposeAbiArray; +#if !NET + RefAbiType = AbiType.MakeByRefType(); +#endif + + return; + } + else if (typeof(T).IsNullableT()) + { + AbiType = typeof(IntPtr); + CreateMarshaler = (T value) => MarshalInterface.CreateMarshaler2(value); + CreateMarshaler2 = CreateMarshaler; + GetAbi = (object objRef) => objRef is ObjectReferenceValue objRefValue ? + MarshalInspectable.GetAbi(objRefValue) : MarshalInterface.GetAbi((IObjectReference)objRef); + FromAbi = (object value) => MarshalInterface.FromAbi((IntPtr)value); + FromManaged = (T value) => MarshalInterface.CreateMarshaler2(value).Detach(); + DisposeMarshaler = MarshalInterface.DisposeMarshaler; + DisposeAbi = (object box) => MarshalInterface.DisposeAbi((IntPtr)box); + CreateMarshalerArray = (T[] array) => MarshalInterface.CreateMarshalerArray(array); + GetAbiArray = MarshalInterface.GetAbiArray; + FromAbiArray = MarshalInterface.FromAbiArray; + FromManagedArray = MarshalInterface.FromManagedArray; + CopyManagedArray = MarshalInterface.CopyManagedArray; + DisposeMarshalerArray = MarshalInterface.DisposeMarshalerArray; + DisposeAbiArray = MarshalInterface.DisposeAbiArray; + +#if !NET + RefAbiType = AbiType.MakeByRefType(); +#endif + + return; + } + else + { + Type abiType = typeof(T).FindHelperType(); + // Could still be blittable and the 'ABI.*' type exists for other reasons (e.g. it's a mapped type) - if (AbiType.GetMethod("FromAbi", BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Static) == null) + if (abiType?.GetMethod("FromAbi", BindingFlags.Public | BindingFlags.Static) is null) { AbiType = null; } + else + { + AbiType = abiType; + } } - if (AbiType == null) + // We repeat all primitive checks here as the linker is not able to propagate the constant + // expression from above, and ends up instantiating MarshalNonBlittable below for many + // blittable types, unnecessarily, which wastes binary size. If the type doesn't match + // any of the primitive types, then we do the usual ABI type check as well. + if (typeof(T) == typeof(int) || + typeof(T) == typeof(byte) || + typeof(T) == typeof(sbyte) || + typeof(T) == typeof(short) || + typeof(T) == typeof(ushort) || + typeof(T) == typeof(uint) || + typeof(T) == typeof(long) || + typeof(T) == typeof(ulong) || + typeof(T) == typeof(float) || + typeof(T) == typeof(double) || + typeof(T) == typeof(Guid) || + typeof(T) == typeof(global::Windows.Foundation.Point) || + typeof(T) == typeof(global::Windows.Foundation.Rect) || + typeof(T) == typeof(global::Windows.Foundation.Size) || + typeof(T) == typeof(global::System.Numerics.Matrix3x2) || + typeof(T) == typeof(global::System.Numerics.Matrix4x4) || + typeof(T) == typeof(global::System.Numerics.Plane) || + typeof(T) == typeof(global::System.Numerics.Quaternion) || + typeof(T) == typeof(global::System.Numerics.Vector2) || + typeof(T) == typeof(global::System.Numerics.Vector3) || + typeof(T) == typeof(global::System.Numerics.Vector4) || + AbiType == null) { Func ReturnTypedParameterFunc = (T value) => value; - AbiType = type; + AbiType = typeof(T); CreateMarshaler = ReturnTypedParameterFunc; CreateMarshaler2 = CreateMarshaler; GetAbi = Marshaler.ReturnParameterFunc; FromAbi = (object value) => (T)value; FromManaged = ReturnTypedParameterFunc; - DisposeMarshaler = Marshaler.EmptyFunc; - DisposeAbi = Marshaler.EmptyFunc; - if (type.IsEnum) + DisposeMarshaler = ABI.System.NonBlittableMarshallingStubs.NoOpFunc; + DisposeAbi = ABI.System.NonBlittableMarshallingStubs.NoOpFunc; + if (typeof(T).IsEnum) { // For marshaling non-blittable enum arrays via MarshalNonBlittable - if (type.GetEnumUnderlyingType() == typeof(int)) + if (typeof(T).GetEnumUnderlyingType() == typeof(int)) { CopyAbi = Marshaler.CopyIntEnumFunc; - CopyManaged = (T value, IntPtr dest) => Marshaler.CopyIntEnumFunc(value, dest); + CopyManaged = Marshaler.CopyIntEnumDirectFunc.WithTypedT1(); } else { CopyAbi = Marshaler.CopyUIntEnumFunc; - CopyManaged = (T value, IntPtr dest) => Marshaler.CopyUIntEnumFunc(value, dest); + CopyManaged = Marshaler.CopyUIntEnumDirectFunc.WithTypedT1(); } } CreateMarshalerArray = (T[] array) => MarshalBlittable.CreateMarshalerArray(array); - GetAbiArray = (object box) => MarshalBlittable.GetAbiArray(box); - FromAbiArray = (object box) => MarshalBlittable.FromAbiArray(box); - FromManagedArray = (T[] array) => MarshalBlittable.FromManagedArray(array); - CopyManagedArray = (T[] array, IntPtr data) => MarshalBlittable.CopyManagedArray(array, data); - DisposeMarshalerArray = (object box) => MarshalBlittable.DisposeMarshalerArray(box); - DisposeAbiArray = (object box) => MarshalBlittable.DisposeAbiArray(box); + GetAbiArray = MarshalBlittable.GetAbiArray; + FromAbiArray = MarshalBlittable.FromAbiArray; + FromManagedArray = MarshalBlittable.FromManagedArray; + CopyManagedArray = MarshalBlittable.CopyManagedArray; + DisposeMarshalerArray = MarshalBlittable.DisposeMarshalerArray; + DisposeAbiArray = MarshalBlittable.DisposeAbiArray; } else { @@ -1574,15 +2346,15 @@ static Marshaler() DisposeMarshaler = MarshalNonBlittable.DisposeMarshaler; DisposeAbi = MarshalNonBlittable.DisposeAbi; CreateMarshalerArray = (T[] array) => MarshalNonBlittable.CreateMarshalerArray(array); - GetAbiArray = (object box) => MarshalNonBlittable.GetAbiArray(box); - FromAbiArray = (object box) => MarshalNonBlittable.FromAbiArray(box); - FromManagedArray = (T[] array) => MarshalNonBlittable.FromManagedArray(array); - CopyManagedArray = (T[] array, IntPtr data) => MarshalNonBlittable.CopyManagedArray(array, data); - DisposeMarshalerArray = (object box) => MarshalNonBlittable.DisposeMarshalerArray(box); - DisposeAbiArray = (object box) => MarshalNonBlittable.DisposeAbiArray(box); + GetAbiArray = MarshalNonBlittable.GetAbiArray; + FromAbiArray = MarshalNonBlittable.FromAbiArray; + FromManagedArray = MarshalNonBlittable.FromManagedArray; + CopyManagedArray = MarshalNonBlittable.CopyManagedArray; + DisposeMarshalerArray = MarshalNonBlittable.DisposeMarshalerArray; + DisposeAbiArray = MarshalNonBlittable.DisposeAbiArray; } } - else if (type.IsInterface) + else if (typeof(T).IsInterface) { AbiType = typeof(IntPtr); CreateMarshaler = (T value) => MarshalInterface.CreateMarshaler2(value); @@ -1594,12 +2366,12 @@ static Marshaler() DisposeMarshaler = MarshalInterface.DisposeMarshaler; DisposeAbi = (object box) => MarshalInterface.DisposeAbi((IntPtr)box); CreateMarshalerArray = (T[] array) => MarshalInterface.CreateMarshalerArray(array); - GetAbiArray = (object box) => MarshalInterface.GetAbiArray(box); - FromAbiArray = (object box) => MarshalInterface.FromAbiArray(box); - FromManagedArray = (T[] array) => MarshalInterface.FromManagedArray(array); - CopyManagedArray = (T[] array, IntPtr data) => MarshalInterface.CopyManagedArray(array, data); - DisposeMarshalerArray = (object box) => MarshalInterface.DisposeMarshalerArray(box); - DisposeAbiArray = (object box) => MarshalInterface.DisposeAbiArray(box); + GetAbiArray = MarshalInterface.GetAbiArray; + FromAbiArray = MarshalInterface.FromAbiArray; + FromManagedArray = MarshalInterface.FromManagedArray; + CopyManagedArray = MarshalInterface.CopyManagedArray; + DisposeMarshalerArray = MarshalInterface.DisposeMarshalerArray; + DisposeAbiArray = MarshalInterface.DisposeAbiArray; } else if (typeof(T) == typeof(object)) { @@ -1614,12 +2386,12 @@ static Marshaler() DisposeMarshaler = MarshalInspectable.DisposeMarshaler; DisposeAbi = (object box) => MarshalInspectable.DisposeAbi((IntPtr)box); CreateMarshalerArray = (T[] array) => MarshalInspectable.CreateMarshalerArray(array); - GetAbiArray = (object box) => MarshalInspectable.GetAbiArray(box); - FromAbiArray = (object box) => MarshalInspectable.FromAbiArray(box); - FromManagedArray = (T[] array) => MarshalInspectable.FromManagedArray(array); - CopyManagedArray = (T[] array, IntPtr data) => MarshalInspectable.CopyManagedArray(array, data); - DisposeMarshalerArray = (object box) => MarshalInspectable.DisposeMarshalerArray(box); - DisposeAbiArray = (object box) => MarshalInspectable.DisposeAbiArray(box); + GetAbiArray = MarshalInspectable.GetAbiArray; + FromAbiArray = MarshalInspectable.FromAbiArray; + FromManagedArray = MarshalInspectable.FromManagedArray; + CopyManagedArray = MarshalInspectable.CopyManagedArray; + DisposeMarshalerArray = MarshalInspectable.DisposeMarshalerArray; + DisposeAbiArray = MarshalInspectable.DisposeAbiArray; } else // delegate, class { @@ -1639,17 +2411,20 @@ static Marshaler() GetAbiArray = MarshalGeneric.GetAbiArray; FromAbiArray = MarshalGeneric.FromAbiArray; FromManagedArray = MarshalGeneric.FromManagedArray; - CopyManagedArray = (T[] array, IntPtr data) => MarshalGeneric.CopyManagedArray(array, data); + CopyManagedArray = MarshalGenericHelper.CopyManagedArray; DisposeMarshalerArray = MarshalGeneric.DisposeMarshalerArray; DisposeAbiArray = MarshalGeneric.DisposeAbiArray; } + +#if !NET RefAbiType = AbiType.MakeByRefType(); +#endif } + public static readonly Type AbiType; #if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods)] + [Obsolete(AttributeMessages.GenericDeprecatedMessage)] #endif - public static readonly Type AbiType; public static readonly Type RefAbiType; public static readonly Func CreateMarshaler; internal static readonly Func CreateMarshaler2; diff --git a/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net5.0.txt b/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net5.0.txt deleted file mode 100644 index 5921e61f2..000000000 --- a/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net5.0.txt +++ /dev/null @@ -1,68 +0,0 @@ -Compat issues with assembly WinRT.Runtime: -MembersMustExist : Member 'public WinRT.ObjectReferenceValue ABI.System.EventHandler.CreateMarshaler2(System.EventHandler)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public System.EventHandler ABI.System.EventHandler.CreateRcw(System.IntPtr)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReferenceValue ABI.System.Nullable.CreateMarshaler2(System.Object)' does not exist in the reference but it does exist in the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute' exists on 'ABI.System.Type.FromAbi(ABI.System.Type)' in the implementation but not the reference. -MembersMustExist : Member 'public ABI.System.Type ABI.System.Type.GetAbi(ABI.System.Type.Pinnable)' does not exist in the reference but it does exist in the implementation. -TypesMustExist : Type 'ABI.System.Type.Pinnable' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReferenceValue ABI.System.Uri.CreateMarshaler2(System.Uri)' does not exist in the reference but it does exist in the implementation. -TypesMustExist : Type 'ABI.System.Collections.Generic.IDictionaryMethods' does not exist in the reference but it does exist in the implementation. -TypesMustExist : Type 'ABI.System.Collections.Generic.IEnumerableMethods' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReferenceValue ABI.System.Collections.Generic.IEnumerator.CreateMarshaler2(System.Collections.Generic.IEnumerator)' does not exist in the reference but it does exist in the implementation. -TypesMustExist : Type 'ABI.System.Collections.Generic.IListMethods' does not exist in the reference but it does exist in the implementation. -TypesMustExist : Type 'ABI.System.Collections.Generic.IReadOnlyDictionaryMethods' does not exist in the reference but it does exist in the implementation. -TypesMustExist : Type 'ABI.System.Collections.Generic.IReadOnlyListMethods' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReferenceValue ABI.System.Collections.Generic.KeyValuePair.CreateMarshaler2(System.Collections.Generic.KeyValuePair)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReferenceValue ABI.System.Collections.Specialized.NotifyCollectionChangedEventArgs.CreateMarshaler2(System.Collections.Specialized.NotifyCollectionChangedEventArgs)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReferenceValue ABI.System.Collections.Specialized.NotifyCollectionChangedEventHandler.CreateMarshaler2(System.Collections.Specialized.NotifyCollectionChangedEventHandler)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public System.Collections.Specialized.NotifyCollectionChangedEventHandler ABI.System.Collections.Specialized.NotifyCollectionChangedEventHandler.CreateRcw(System.IntPtr)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReferenceValue ABI.System.ComponentModel.DataErrorsChangedEventArgs.CreateMarshaler2(System.ComponentModel.DataErrorsChangedEventArgs)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReferenceValue ABI.System.ComponentModel.PropertyChangedEventArgs.CreateMarshaler2(System.ComponentModel.PropertyChangedEventArgs)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReferenceValue ABI.System.ComponentModel.PropertyChangedEventHandler.CreateMarshaler2(System.ComponentModel.PropertyChangedEventHandler)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public System.ComponentModel.PropertyChangedEventHandler ABI.System.ComponentModel.PropertyChangedEventHandler.CreateRcw(System.IntPtr)' does not exist in the reference but it does exist in the implementation. -TypesMustExist : Type 'ABI.WinRT.Interop.IWeakReferenceSourceMethods' does not exist in the reference but it does exist in the implementation. -TypesMustExist : Type 'System.Numerics.VectorExtensions' does not exist in the reference but it does exist in the implementation. -TypesMustExist : Type 'System.Runtime.InteropServices.WindowsRuntime.ReadOnlyArrayAttribute' does not exist in the reference but it does exist in the implementation. -TypesMustExist : Type 'System.Runtime.InteropServices.WindowsRuntime.WriteOnlyArrayAttribute' does not exist in the reference but it does exist in the implementation. -CannotRemoveAttribute : Attribute 'WinRT.WindowsRuntimeHelperTypeAttribute' exists on 'Windows.Foundation.Point' in the implementation but not the reference. -CannotRemoveAttribute : Attribute 'WinRT.WindowsRuntimeHelperTypeAttribute' exists on 'Windows.Foundation.Rect' in the implementation but not the reference. -CannotRemoveAttribute : Attribute 'WinRT.WindowsRuntimeHelperTypeAttribute' exists on 'Windows.Foundation.Size' in the implementation but not the reference. -TypesMustExist : Type 'WinRT.ComWrappersHelper' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReference WinRT.ComWrappersSupport.GetObjectReferenceForInterface(System.IntPtr)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReference WinRT.ComWrappersSupport.GetObjectReferenceForInterface(System.IntPtr, System.Guid)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public void WinRT.ComWrappersSupport.RegisterObjectForInterface(System.Object, System.IntPtr, System.Runtime.InteropServices.CreateObjectFlags)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public void WinRT.ComWrappersSupport.RegisterProjectionTypeBaseTypeMapping(System.Collections.Generic.IDictionary)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'protected void WinRT.IObjectReference.AddRef(System.Boolean)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReference WinRT.IObjectReference.AsKnownPtr(System.IntPtr)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReferenceValue WinRT.IObjectReference.AsValue()' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReferenceValue WinRT.IObjectReference.AsValue(System.Guid)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public System.Int32 WinRT.IObjectReference.TryAs(System.Guid, System.IntPtr)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReferenceValue WinRT.MarshalDelegate.CreateMarshaler2(System.Object, System.Guid, System.Boolean)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public T WinRT.MarshalDelegate.FromAbi(System.IntPtr)' does not exist in the reference but it does exist in the implementation. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' exists on 'System.Type WinRT.Marshaler.AbiType' in the implementation but not the reference. -CannotRemoveAttribute : Attribute 'System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute' exists on 'System.Type WinRT.MarshalGeneric.HelperType' in the implementation but not the reference. -MembersMustExist : Member 'public WinRT.ObjectReferenceValue WinRT.MarshalInspectable.CreateMarshaler2(T, System.Boolean)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReferenceValue WinRT.MarshalInspectable.CreateMarshaler2(T, System.Guid, System.Boolean)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.IObjectReference WinRT.MarshalInspectable.CreateMarshaler(T, System.Guid, System.Boolean)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public void WinRT.MarshalInspectable.DisposeMarshaler(WinRT.ObjectReferenceValue)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public System.IntPtr WinRT.MarshalInspectable.GetAbi(WinRT.ObjectReferenceValue)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.ObjectReferenceValue WinRT.MarshalInterface.CreateMarshaler2(T, System.Guid)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public void WinRT.MarshalInterface.DisposeMarshaler(WinRT.ObjectReferenceValue)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public System.IntPtr WinRT.MarshalInterface.GetAbi(WinRT.ObjectReferenceValue)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.MarshalInterfaceHelper.MarshalerArray WinRT.MarshalInterfaceHelper.CreateMarshalerArray2(T[], System.Func)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public void WinRT.MarshalInterfaceHelper.DisposeMarshaler(WinRT.ObjectReferenceValue)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public System.IntPtr WinRT.MarshalInterfaceHelper.GetAbi(WinRT.ObjectReferenceValue)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public void WinRT.MarshalString..ctor(System.String)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public WinRT.MarshalString.Pinnable WinRT.MarshalString.CreatePinnable(System.String)' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public System.IntPtr WinRT.MarshalString.GetAbi()' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public System.IntPtr WinRT.MarshalString.GetAbi(WinRT.MarshalString.Pinnable)' does not exist in the reference but it does exist in the implementation. -TypesMustExist : Type 'WinRT.MarshalString.Pinnable' does not exist in the reference but it does exist in the implementation. -TypesMustExist : Type 'WinRT.ObjectReferenceValue' does not exist in the reference but it does exist in the implementation. -TypesMustExist : Type 'WinRT.WindowsRuntimeHelperTypeAttribute' does not exist in the reference but it does exist in the implementation. -CannotRemoveAttribute : Attribute 'WinRT.WindowsRuntimeHelperTypeAttribute' exists on 'WinRT.Interop.IActivationFactory' in the implementation but not the reference. -CannotRemoveAttribute : Attribute 'WinRT.WindowsRuntimeHelperTypeAttribute' exists on 'WinRT.Interop.IAgileObject' in the implementation but not the reference. -MembersMustExist : Member 'public function System.Int32 (System.IntPtr, System.Guid*, System.IntPtr*) WinRT.Interop.IUnknownVftbl.QueryInterface.get()' does not exist in the reference but it does exist in the implementation. -MembersMustExist : Member 'public void WinRT.Interop.IUnknownVftbl.QueryInterface.set(function System.Int32 (System.IntPtr, System.Guid*, System.IntPtr*))' does not exist in the reference but it does exist in the implementation. -TypesMustExist : Type 'WinRT.Interop.IWeakReference' does not exist in the reference but it does exist in the implementation. -TypesMustExist : Type 'WinRT.Interop.IWeakReferenceSource' does not exist in the reference but it does exist in the implementation. -Total Issues: 66 diff --git a/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net8.0.txt b/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net8.0.txt new file mode 100644 index 000000000..3abbc34c7 --- /dev/null +++ b/src/WinRT.Runtime/MatchingRefApiCompatBaseline.net8.0.txt @@ -0,0 +1,18 @@ +Compat issues with assembly WinRT.Runtime: +TypesMustExist : Type 'WinRT.GeneratedWinRTExposedExternalTypeAttribute' does not exist in the reference but it does exist in the implementation. +TypesMustExist : Type 'WinRT.GeneratedWinRTExposedTypeAttribute' does not exist in the reference but it does exist in the implementation. +CannotChangeAttribute : Attribute 'System.AttributeUsageAttribute' on 'WinRT.GeneratedBindableCustomPropertyAttribute' changed from '[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Struct, Inherited=false, AllowMultiple=false)]' in the implementation to '[AttributeUsageAttribute(AttributeTargets.Class, Inherited=false, AllowMultiple=false)]' in the reference. +TypesMustExist : Type 'WinRT.WinRTManagedOnlyTypeDetails' does not exist in the reference but it does exist in the implementation. +CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.NullableAttribute' exists on 'WinRT.ActivationFactory' in the implementation but not the reference. +CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.NullableContextAttribute' exists on 'WinRT.ActivationFactory' in the implementation but not the reference. +CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.NullableAttribute' exists on 'WinRT.ActivationFactory.ActivationHandler' in the implementation but not the reference. +CannotRemoveAttribute : Attribute 'System.Runtime.CompilerServices.NullableContextAttribute' exists on 'WinRT.EventRegistrationTokenTable.AddEventHandler(T)' in the implementation but not the reference. +TypesMustExist : Type 'WinRT.DynamicWindowsRuntimeCastAttribute' does not exist in the reference but it does exist in the implementation. +MembersMustExist : Member 'public System.Guid WinRT.Interop.IID.IID_IWeakReferenceSource.get()' does not exist in the reference but it does exist in the implementation. +CannotRemoveAttribute : Attribute 'System.ComponentModel.EditorBrowsableAttribute' exists on 'WinRT.Marshaler' in the implementation but not the reference. +TypesMustExist : Type 'WinRT.MarshalGenericHelper' does not exist in the reference but it does exist in the implementation. +MembersMustExist : Member 'protected void ABI.WinRT.Interop.EventSource..ctor(WinRT.IObjectReference, System.Int32)' does not exist in the reference but it does exist in the implementation. +MembersMustExist : Member 'public void ABI.WinRT.Interop.EventHandlerEventSource..ctor(WinRT.IObjectReference, System.Int32)' does not exist in the reference but it does exist in the implementation. +MembersMustExist : Member 'public void ABI.WinRT.Interop.EventHandlerEventSource..ctor(WinRT.IObjectReference, System.Int32)' does not exist in the reference but it does exist in the implementation. +MembersMustExist : Member 'public void ABI.System.Type.CopyManagedArray(System.Type[], System.IntPtr)' does not exist in the reference but it does exist in the implementation. +Total Issues: 16 diff --git a/src/WinRT.Runtime/MonoSupport.cs b/src/WinRT.Runtime/MonoSupport.cs index 58a6a265c..412f8a4eb 100644 --- a/src/WinRT.Runtime/MonoSupport.cs +++ b/src/WinRT.Runtime/MonoSupport.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Runtime.InteropServices; +using WinRT.Interop; #pragma warning disable 0169 // The field 'xxx' is never used #pragma warning disable 0649 // Field 'xxx' is never assigned to, and will always have its default value @@ -12,9 +13,9 @@ namespace WinRT { internal static class Mono { - static Lazy _usingMono = new Lazy(() => + static readonly unsafe Lazy _usingMono = new Lazy(() => { - var modulePtr = Platform.LoadLibraryExW("mono-2.0-bdwgc.dll", IntPtr.Zero, 0); + IntPtr modulePtr = Platform.LoadLibraryExW("mono-2.0-bdwgc.dll", IntPtr.Zero, 0); if (modulePtr == IntPtr.Zero) return false; if (!Platform.FreeLibrary(modulePtr)) @@ -74,7 +75,7 @@ struct MonoInternalThread_x64 public sealed class ThreadContext : IDisposable { - static Lazy> _foreignThreads = new Lazy>(); + static readonly Lazy> _foreignThreads = new Lazy>(); readonly IntPtr _threadPtr = IntPtr.Zero; diff --git a/src/WinRT.Runtime/ObjectReference.cs b/src/WinRT.Runtime/ObjectReference.cs index 08606d430..7100f3409 100644 --- a/src/WinRT.Runtime/ObjectReference.cs +++ b/src/WinRT.Runtime/ObjectReference.cs @@ -3,10 +3,12 @@ using System; using System.Collections.Concurrent; -using System.Diagnostics.CodeAnalysis; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using System.Threading; using WinRT.Interop; #pragma warning disable 0169 // The field 'xxx' is never used @@ -14,7 +16,6 @@ namespace WinRT { - #if EMBED internal #else @@ -22,10 +23,14 @@ namespace WinRT #endif abstract class IObjectReference : IDisposable { - protected bool disposed; + // Flags for the '_disposedFlags' field, see notes in the Dispose() method below + private const int NOT_DISPOSED = 0; + private const int DISPOSE_PENDING = 1; + private const int DISPOSE_COMPLETED = 2; + private readonly IntPtr _thisPtr; - private object _disposedLock = new object(); private IntPtr _referenceTrackerPtr; + private int _disposedFlags; public IntPtr ThisPtr { @@ -36,6 +41,17 @@ public IntPtr ThisPtr } } + public bool IsFreeThreaded => GetContextToken() == IntPtr.Zero; + + public bool IsInCurrentContext + { + get + { + var contextToken = GetContextToken(); + return contextToken == IntPtr.Zero || contextToken == Context.GetContextToken(); + } + } + private protected IntPtr ThisPtrFromOriginalContext { get @@ -50,8 +66,8 @@ private unsafe uint RefCount { get { - VftblIUnknown.AddRef(ThisPtr); - return VftblIUnknown.Release(ThisPtr); + Marshal.AddRef(ThisPtr); + return (uint)Marshal.Release(ThisPtr); } } @@ -76,7 +92,7 @@ internal unsafe IntPtr ReferenceTrackerPtr _referenceTrackerPtr = value; if (_referenceTrackerPtr != IntPtr.Zero) { - ReferenceTracker.IUnknownVftbl.AddRef(_referenceTrackerPtr); + Marshal.AddRef(_referenceTrackerPtr); AddRefFromTrackerSource(); } } @@ -116,24 +132,32 @@ protected IObjectReference(IntPtr thisPtr) throw new ArgumentNullException(nameof(thisPtr)); } _thisPtr = thisPtr; + + // We are holding onto a native object or one of its interfaces. + // This causes for there to be native memory being held onto by + // this that the .NET GC isn't aware of. So we use memory pressure + // to make the .NET GC aware of it. In theory all the interface QIs + // can be holding onto the same native object. Here we are taking the simplified + // approach of having each IObjectReference represent some basic native memory + // pressure rather than tracking all the IObjectReferences that are connected + // to the same object and only releasing the memory pressure once all of them + // have been finalized. + GC.AddMemoryPressure(ComWrappersSupport.GC_PRESSURE_BASE); } ~IObjectReference() { - Dispose(false); + Dispose(); } - public ObjectReference As< -#if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] -#endif - T>() => As(GuidGenerator.GetIID(typeof(T))); - - public unsafe ObjectReference As< #if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] + [RequiresUnreferencedCode(AttributeMessages.GenericRequiresUnreferencedCodeMessage)] + [Obsolete(AttributeMessages.GenericDeprecatedMessage)] + [EditorBrowsable(EditorBrowsableState.Never)] #endif - T>(Guid iid) + public ObjectReference As() => As(GuidGenerator.GetIID(typeof(T))); + + public unsafe ObjectReference As(Guid iid) { Marshal.ThrowExceptionForHR(TryAs(iid, out var objRef)); return objRef; @@ -141,23 +165,28 @@ public unsafe ObjectReference As< public unsafe TInterface AsInterface() { - if (typeof(TInterface).IsDefined(typeof(System.Runtime.InteropServices.ComImportAttribute))) + if (typeof(TInterface).IsDefined(typeof(ComImportAttribute))) { Guid iid = typeof(TInterface).GUID; - IntPtr comPtr = IntPtr.Zero; - Marshal.ThrowExceptionForHR(VftblIUnknown.QueryInterface(ThisPtr, &iid, &comPtr)); + Marshal.ThrowExceptionForHR(Marshal.QueryInterface(ThisPtr, ref iid, out IntPtr comPtr)); try { return (TInterface)Marshal.GetObjectForIUnknown(comPtr); } finally { - var vftblPtr = Unsafe.AsRef(comPtr.ToPointer()); - var vftblIUnknown = Marshal.PtrToStructure(vftblPtr.Vftbl); - vftblIUnknown.Release(comPtr); + _ = Marshal.Release(comPtr); } } + if (!FeatureSwitches.EnableIDynamicInterfaceCastableSupport) + { + throw new NotSupportedException( + "Using 'AsInterface' to cast an RCW to an interface type not present in " + + "metadata relies on 'IDynamicInterfaceCastable' support, which is not currently available. " + + "Make sure the 'EnableIDynamicInterfaceCastableSupport' property is not set to 'false'."); + } + #if !NET return (TInterface)typeof(TInterface).GetHelperType().GetConstructor(new[] { typeof(IObjectReference) }).Invoke(new object[] { this }); #else @@ -165,42 +194,31 @@ public unsafe TInterface AsInterface() #endif } - public int TryAs< #if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] + [RequiresUnreferencedCode(AttributeMessages.GenericRequiresUnreferencedCodeMessage)] + [Obsolete(AttributeMessages.GenericDeprecatedMessage)] + [EditorBrowsable(EditorBrowsableState.Never)] #endif - T>(out ObjectReference objRef) => TryAs(GuidGenerator.GetIID(typeof(T)), out objRef); + public int TryAs(out ObjectReference objRef) => TryAs(GuidGenerator.GetIID(typeof(T)), out objRef); - public virtual unsafe int TryAs< -#if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] -#endif - T>(Guid iid, out ObjectReference objRef) + public unsafe int TryAs(Guid iid, out ObjectReference objRef) { - objRef = null; - ThrowIfDisposed(); - IntPtr thatPtr = IntPtr.Zero; - int hr = VftblIUnknown.QueryInterface(ThisPtr, &iid, &thatPtr); - if (hr >= 0) + // Check the marker interface for ObjectReferenceWithContext. If that is the case, we inline + // the special logic for such objects. This avoids having to use a generic virtual method here, + // which would just explode the binary size on NativeAOT due to combinatorial generics. + if (this is IObjectReferenceWithContext) { - if (IsAggregated) - { - Marshal.Release(thatPtr); - } - AddRefFromTrackerSource(); - - objRef = ObjectReference.Attach(ref thatPtr); - objRef.IsAggregated = IsAggregated; - objRef.PreventReleaseOnDispose = IsAggregated; - objRef.ReferenceTrackerPtr = ReferenceTrackerPtr; + return ObjectReferenceWithContext.TryAs(this, iid, out objRef); } - return hr; + + // Normal path for IObjectReference or any IObjectReference + return ObjectReference.TryAs(this, iid, out objRef); } public virtual unsafe ObjectReference AsKnownPtr(IntPtr ptr) { AddRef(true); - var objRef = ObjectReference.Attach(ref ptr); + var objRef = ObjectReference.Attach(ref ptr, IID.IID_IUnknown); objRef.IsAggregated = IsAggregated; objRef.PreventReleaseOnDispose = IsAggregated; objRef.ReferenceTrackerPtr = ReferenceTrackerPtr; @@ -217,7 +235,7 @@ public virtual unsafe int TryAs(Guid iid, out IntPtr ppv) ppv = IntPtr.Zero; ThrowIfDisposed(); IntPtr thatPtr = IntPtr.Zero; - int hr = VftblIUnknown.QueryInterface(ThisPtr, &iid, &thatPtr); + int hr = Marshal.QueryInterface(ThisPtr, ref iid, out thatPtr); if (hr >= 0) { ppv = thatPtr; @@ -227,15 +245,64 @@ public virtual unsafe int TryAs(Guid iid, out IntPtr ppv) public unsafe IObjectReference As(Guid iid) => As(iid); +#if NET + [Obsolete(AttributeMessages.GenericDeprecatedMessage)] + [EditorBrowsable(EditorBrowsableState.Never)] +#endif public T AsType() { ThrowIfDisposed(); + +#if NET + // Same logic as in 'ComWrappersSupport.CreateFactoryForImplementationType', see notes there + var attribute = typeof(T).GetCustomAttribute(inherit: false); + + if (attribute is not null) + { + return (T)attribute.CreateInstance(new IInspectable(this)); + } + + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException( + $"Cannot create an RCW instance for type '{typeof(T)}', because it doesn't have a " + + "[WinRTImplementationTypeRcwFactory] derived attribute on it. The fallback path for older projections " + + "is not trim-safe, and isn't supported in AOT environments. Make sure to reference updated projections."); + } + + if (TryCreateRcwFallback(this, out object rcwInstance)) + { + return (T)rcwInstance; + } + + [UnconditionalSuppressMessage("Trimming", "IL2090", Justification = "This fallback path is not trim-safe by design (to avoid annotations).")] + static bool TryCreateRcwFallback(IObjectReference objectReference, out object rcwInstance) + { + var constructor = typeof(T).GetConstructor( + bindingAttr: BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.CreateInstance | BindingFlags.Instance, + binder: null, + types: new[] { typeof(IObjectReference) }, + modifiers: null); + + if (constructor is not null) + { + rcwInstance = constructor.Invoke(new[] { objectReference }); + + return true; + } + + rcwInstance = null; + + return false; + } +#else var ctor = typeof(T).GetConstructor(new[] { typeof(IObjectReference) }); - if (ctor != null) + if (ctor is not null) { return (T)ctor.Invoke(new[] { this }); } - throw new InvalidOperationException("Target type is not a projected interface."); +#endif + throw new InvalidOperationException($"Target type '{typeof(T)}' is not a projected type."); } public IntPtr GetRef() @@ -245,31 +312,66 @@ public IntPtr GetRef() return ThisPtr; } + /// + /// Throws an if has already been called on the current instance. + /// + /// + /// Note that calling this method does not protect callers against concurrent threads calling on the + /// same instance, as that behavior is explicitly undefined. Similarly, callers using this to then access the underlying + /// pointers should also make sure to keep the current instance alive until they're done using the pointer (unless they're + /// also incrementing it via AddRef in some way), or the GC could concurrently collect the instance and cause the + /// same problem (ie. the underlying pointer being in use becoming invalid right after retrieving it from the object). + /// + /// Thrown if the current instance is disposed. + [MethodImpl(MethodImplOptions.AggressiveInlining)] protected void ThrowIfDisposed() { - if (disposed) + if (Volatile.Read(ref _disposedFlags) == DISPOSE_COMPLETED) { - lock (_disposedLock) - { - if (disposed) throw new ObjectDisposedException("ObjectReference"); - } + ThrowObjectDisposedException(); + } + + static void ThrowObjectDisposedException() + { + throw new ObjectDisposedException("ObjectReference"); } } + /// public void Dispose() { - Dispose(true); GC.SuppressFinalize(this); - } - protected virtual void Dispose(bool disposing) - { - lock (_disposedLock) + // We swap the disposed flag and only dispose the first time. This is safe with respect to + // different threads concurrently trying to dispose the same object, as only the first one + // will actually dispose it, and the others will just do nothing. + // + // It is not safe when combined with ThrowIfDisposed(), as the following scenario is possible: + // - Thread A calls ProjectedType.Foo() + // - Thread A calls ThisPtr, the dispose check passes and it gets the IntPtr value (not incremented) + // - Thread B calls Dispose(), which releases the object + // - Thread A now uses that IntPtr to invoke some function pointer + // - Thread A goes ka-boom 💥 + // + // However, this is by design, as the ObjectReference owns the 'ThisPtr' property, and disposing it while it + // is still in use can lead to all kinds of things going wrong. This is conceptually the same as calling + // SafeHandle.DangerousGetHandle(). Furthermore, the same exact behavior was already possible with an actual + // lock object guarding all the logic within the Dispose() method. The only difference is that simply using + // a flag this way avoids one object allocation per ObjectReference instance, and also allows making the size + // of the whole object smaller by sizeof(object), when taking into account padding. + // + // Additionally, note that the '_disposedFlags' field has 3 different states: + // - NOT_DISPOSED: the initial state, when the object is alive + // - DISPOSE_PENDING: indicates that a thread is currently executing the Dispose() method and got past the + // first check, and is in the process of releasing the native resources. This state is checked by the + // ThrowIfDisposed() method above, and still treated as if the object can be used normally. This is + // necessary, because the dispose logic still has to access the 'ThisPtr' property and others in order + // to perform the various Release() calls on the native objects being used. If the state was immediately + // set to disposed, that method would just start throwing immediately, and this logic would not work. + // - DISPOSE_COMPLETED: set when all the Dispose() logic has been completed and the object should not be + // used at all anymore. When this is set, the ThrowIfDisposed() method will start throwing exceptions. + if (Interlocked.CompareExchange(ref _disposedFlags, DISPOSE_PENDING, NOT_DISPOSED) == NOT_DISPOSED) { - if (disposed) - { - return; - } #if DEBUG if (BreakOnDispose && System.Diagnostics.Debugger.IsAttached) { @@ -283,30 +385,18 @@ protected virtual void Dispose(bool disposing) } DisposeTrackerSource(); - disposed = true; - } - } - internal bool Resurrect() - { - lock (_disposedLock) - { - if (!disposed) - { - return false; - } - disposed = false; - ResurrectTrackerSource(); - AddRef(); - GC.ReRegisterForFinalize(this); - return true; + // Remove the same memory pressure added in the constructor (see notes there) + GC.RemoveMemoryPressure(ComWrappersSupport.GC_PRESSURE_BASE); + + Volatile.Write(ref _disposedFlags, DISPOSE_COMPLETED); } } protected virtual unsafe void AddRef(bool refFromTrackerSource) { - VftblIUnknown.AddRef(ThisPtr); - if(refFromTrackerSource) + Marshal.AddRef(ThisPtr); + if (refFromTrackerSource) { AddRefFromTrackerSource(); } @@ -320,13 +410,13 @@ protected virtual unsafe void AddRef() protected virtual unsafe void Release() { ReleaseFromTrackerSource(); - VftblIUnknown.Release(ThisPtr); + Marshal.Release(ThisPtr); } private protected unsafe void ReleaseWithoutContext() { ReleaseFromTrackerSource(); - VftblIUnknownFromOriginalContext.Release(ThisPtrFromOriginalContext); + Marshal.Release(ThisPtrFromOriginalContext); } internal unsafe bool IsReferenceToManagedObject @@ -353,18 +443,6 @@ internal unsafe void ReleaseFromTrackerSource() } } - private unsafe void ResurrectTrackerSource() - { - if (ReferenceTrackerPtr != IntPtr.Zero) - { - ReferenceTracker.IUnknownVftbl.AddRef(ReferenceTrackerPtr); - if (!PreventReleaseFromTrackerSourceOnDispose) - { - ReferenceTracker.AddRefFromTrackerSource(ReferenceTrackerPtr); - } - } - } - private unsafe void DisposeTrackerSource() { if (ReferenceTrackerPtr != IntPtr.Zero) @@ -373,7 +451,7 @@ private unsafe void DisposeTrackerSource() { ReferenceTracker.ReleaseFromTrackerSource(ReferenceTrackerPtr); } - ReferenceTracker.IUnknownVftbl.Release(ReferenceTrackerPtr); + Marshal.Release(ReferenceTrackerPtr); } } @@ -382,6 +460,11 @@ private protected virtual IntPtr GetThisPtrForCurrentContext() return ThisPtrFromOriginalContext; } + private protected virtual IntPtr GetContextToken() + { + return IntPtr.Zero; + } + public ObjectReferenceValue AsValue() { // Sharing ptr with objref. @@ -391,7 +474,7 @@ public ObjectReferenceValue AsValue() public unsafe ObjectReferenceValue AsValue(Guid iid) { IntPtr thatPtr = IntPtr.Zero; - Marshal.ThrowExceptionForHR(VftblIUnknown.QueryInterface(ThisPtr, &iid, &thatPtr)); + Marshal.ThrowExceptionForHR(Marshal.QueryInterface(ThisPtr, ref iid, out thatPtr)); if (IsAggregated) { Marshal.Release(thatPtr); @@ -407,11 +490,7 @@ public unsafe ObjectReferenceValue AsValue(Guid iid) #else public #endif - class ObjectReference< -#if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] -#endif - T> : IObjectReference + class ObjectReference : IObjectReference { private readonly T _vftbl; public T Vftbl @@ -423,39 +502,151 @@ public T Vftbl } } + private protected ObjectReference(IntPtr thisPtr, T vftblT) : + base(thisPtr) + { + _vftbl = vftblT; + } + + private protected ObjectReference(IntPtr thisPtr) : + this(thisPtr, GetVtable(thisPtr)) + { + } + +#if NET + [RequiresUnreferencedCode(AttributeMessages.GenericRequiresUnreferencedCodeMessage)] + [Obsolete(AttributeMessages.GenericDeprecatedMessage)] + [EditorBrowsable(EditorBrowsableState.Never)] +#endif public static ObjectReference Attach(ref IntPtr thisPtr) { if (thisPtr == IntPtr.Zero) { return null; } - var obj = new ObjectReference(thisPtr); - thisPtr = IntPtr.Zero; - return obj; + + if (ComWrappersSupport.IsFreeThreaded(thisPtr)) + { + var obj = new ObjectReference(thisPtr); + thisPtr = IntPtr.Zero; + return obj; + } + else + { + var obj = new ObjectReferenceWithContext( + thisPtr, + Context.GetContextCallback(), + Context.GetContextToken()); + thisPtr = IntPtr.Zero; + return obj; + } } - ObjectReference(IntPtr thisPtr, T vftblT) : - base(thisPtr) + /// + /// Creates an instance for a COM pointer to an agile object, without validation. + /// + /// The COM pointer to wrap. + /// The resulting instance. + /// + /// This method will not validate that the target COM object is actually free-threaded. + /// It is the responsibility of callers to ensure only free-threaded objects are used. + /// + internal static ObjectReference AttachFreeThreadedUnsafe(ref IntPtr thisPtr) { - _vftbl = vftblT; + if (thisPtr == IntPtr.Zero) + { + return null; + } + + var obj = new ObjectReference(thisPtr); + thisPtr = IntPtr.Zero; + return obj; } - private protected ObjectReference(IntPtr thisPtr) : - this(thisPtr, GetVtable(thisPtr)) + public static ObjectReference Attach(ref IntPtr thisPtr, Guid iid) { + if (thisPtr == IntPtr.Zero) + { + return null; + } + + if (ComWrappersSupport.IsFreeThreaded(thisPtr)) + { + var obj = new ObjectReference(thisPtr); + thisPtr = IntPtr.Zero; + return obj; + } + else + { + var obj = new ObjectReferenceWithContext( + thisPtr, + Context.GetContextCallback(), + Context.GetContextToken(), + iid); + thisPtr = IntPtr.Zero; + return obj; + } } +#if NET + [RequiresUnreferencedCode(AttributeMessages.GenericRequiresUnreferencedCodeMessage)] + [Obsolete(AttributeMessages.GenericDeprecatedMessage)] + [EditorBrowsable(EditorBrowsableState.Never)] +#endif public static unsafe ObjectReference FromAbi(IntPtr thisPtr, T vftblT) { if (thisPtr == IntPtr.Zero) { return null; } - var obj = new ObjectReference(thisPtr, vftblT); - obj.VftblIUnknown.AddRef(obj.ThisPtr); - return obj; + + Marshal.AddRef(thisPtr); + if (ComWrappersSupport.IsFreeThreaded(thisPtr)) + { + var obj = new ObjectReference(thisPtr, vftblT); + return obj; + } + else + { + var obj = new ObjectReferenceWithContext( + thisPtr, + vftblT, + Context.GetContextCallback(), + Context.GetContextToken()); + return obj; + } } + public static unsafe ObjectReference FromAbi(IntPtr thisPtr, T vftblT, Guid iid) + { + if (thisPtr == IntPtr.Zero) + { + return null; + } + + Marshal.AddRef(thisPtr); + if (ComWrappersSupport.IsFreeThreaded(thisPtr)) + { + var obj = new ObjectReference(thisPtr, vftblT); + return obj; + } + else + { + var obj = new ObjectReferenceWithContext( + thisPtr, + vftblT, + Context.GetContextCallback(), + Context.GetContextToken(), + iid); + return obj; + } + } + +#if NET + [RequiresUnreferencedCode(AttributeMessages.GenericRequiresUnreferencedCodeMessage)] + [Obsolete(AttributeMessages.GenericDeprecatedMessage)] + [EditorBrowsable(EditorBrowsableState.Never)] +#endif public static ObjectReference FromAbi(IntPtr thisPtr) { if (thisPtr == IntPtr.Zero) @@ -466,50 +657,93 @@ public static ObjectReference FromAbi(IntPtr thisPtr) return FromAbi(thisPtr, vftblT); } + public static ObjectReference FromAbi(IntPtr thisPtr, Guid iid) + { + if (thisPtr == IntPtr.Zero) + { + return null; + } + var vftblT = GetVtable(thisPtr); + return FromAbi(thisPtr, vftblT, iid); + } + private static unsafe T GetVtable(IntPtr thisPtr) { - var vftblPtr = Unsafe.AsRef(thisPtr.ToPointer()); - T vftblT; // With our vtable types, the generic vtables will have System.Delegate fields - // and the non-generic types will have only void* fields. - // On .NET 5, we can use RuntimeHelpers.IsReferenceorContainsReferences - // to disambiguate between generic and non-generic vtables since it's a JIT-time constant. - // Since it is a JIT time constant, this function will be branchless on .NET 5. + // and the non-generic types will have only void* fields. On .NET 6+, we can use + // RuntimeHelpers.IsReferenceorContainsReferences to disambiguate between generic + // and non-generic vtables since it's a JIT-time constant. Projections for .NET 6+ + // never use such vtables, so this path will just always throw. This also allows + // dropping the trimming annotations preserving all non public constructors. + // Since it is a JIT time constant, this function will be branchless on .NET 6+. // On .NET Standard 2.0, the IsReferenceOrContainsReferences method does not exist, - // so we instead fall back to typeof(T).IsGenericType, which sadly is not a JIT-time constant. -#if !NET - if (typeof(T).IsGenericType) -#else + // so we instead fall back to typeof(T).IsGenericType, which is not a JIT-time constant. +#if NET if (RuntimeHelpers.IsReferenceOrContainsReferences()) -#endif - { - vftblT = (T)typeof(T).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance, null, new[] { typeof(IntPtr) }, null).Invoke(new object[] { thisPtr }); + { + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException("Managed vtable types (ie. containing any reference types) are not supported."); + } + + return GetVtableForJitEnvironment(thisPtr); + + [UnconditionalSuppressMessage("Trimming", "IL2090", Justification = "Fallback method for JIT environments that is not trim-safe by design.")] + [MethodImpl(MethodImplOptions.NoInlining)] + static T GetVtableForJitEnvironment(IntPtr thisPtr) + { + return (T)typeof(T).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance, null, new[] { typeof(IntPtr) }, null).Invoke(new object[] { thisPtr }); + } } - else +#else + if (typeof(T).IsGenericType) { - vftblT = Unsafe.AsRef(vftblPtr.Vftbl.ToPointer()); + return (T)typeof(T).GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.CreateInstance, null, new[] { typeof(IntPtr) }, null).Invoke(new object[] { thisPtr }); } - return vftblT; +#endif + + // Blittable vtables can just be read directly from the input pointer + return Unsafe.Read(*(void***)thisPtr); } private protected virtual T GetVftblForCurrentContext() { return _vftbl; } + + internal static int TryAs(IObjectReference sourceRef, Guid iid, out ObjectReference objRef) + { + objRef = null; + + int hr = Marshal.QueryInterface(sourceRef.ThisPtr, ref iid, out IntPtr thatPtr); + + if (hr >= 0) + { + if (sourceRef.IsAggregated) + { + Marshal.Release(thatPtr); + } + + sourceRef.AddRefFromTrackerSource(); + + objRef = Attach(ref thatPtr, iid); + objRef.IsAggregated = sourceRef.IsAggregated; + objRef.PreventReleaseOnDispose = sourceRef.IsAggregated; + objRef.ReferenceTrackerPtr = sourceRef.ReferenceTrackerPtr; + } + + return hr; + } } - internal sealed class ObjectReferenceWithContext< -#if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] -#endif - T> : ObjectReference + internal sealed class ObjectReferenceWithContext : ObjectReference, IObjectReferenceWithContext { private readonly IntPtr _contextCallbackPtr; private readonly IntPtr _contextToken; - private volatile ConcurrentDictionary> __cachedContext; - private ConcurrentDictionary> CachedContext => __cachedContext ?? Make_CachedContext(); - private ConcurrentDictionary> Make_CachedContext() + private volatile ConcurrentDictionary __cachedContext; + private ConcurrentDictionary CachedContext => __cachedContext ?? Make_CachedContext(); + private ConcurrentDictionary Make_CachedContext() { global::System.Threading.Interlocked.CompareExchange(ref __cachedContext, new(), null); return __cachedContext; @@ -519,32 +753,83 @@ private ConcurrentDictionary> Make_CachedContext() private volatile bool _isAgileReferenceSet; private volatile AgileReference __agileReference; private AgileReference AgileReference => _isAgileReferenceSet ? __agileReference : Make_AgileReference(); - private AgileReference Make_AgileReference() - { - Context.CallInContext(_contextCallbackPtr, _contextToken, InitAgileReference, null); + private unsafe AgileReference Make_AgileReference() + { + Context.CallInContext( + _contextCallbackPtr, + _contextToken, +#if NET && CsWinRT_LANG_11_FEATURES + &InitAgileReference, +#else + InitAgileReference, +#endif + null, + this); // Set after CallInContext callback given callback can fail to occur. _isAgileReferenceSet = true; + return __agileReference; - void InitAgileReference() + static void InitAgileReference(object state) { - global::System.Threading.Interlocked.CompareExchange(ref __agileReference, new AgileReference(this), null); + ObjectReferenceWithContext @this = Unsafe.As>(state); + + global::System.Threading.Interlocked.CompareExchange(ref @this.__agileReference, new AgileReference(@this), null); } } private readonly Guid _iid; +#if NET + [RequiresUnreferencedCode(AttributeMessages.GenericRequiresUnreferencedCodeMessage)] + [Obsolete(AttributeMessages.GenericDeprecatedMessage)] + [EditorBrowsable(EditorBrowsableState.Never)] +#endif internal ObjectReferenceWithContext(IntPtr thisPtr, IntPtr contextCallbackPtr, IntPtr contextToken) - :base(thisPtr) + : base(thisPtr) { _contextCallbackPtr = contextCallbackPtr; _contextToken = contextToken; } +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "This constructor is setting the '_iid' field directly.")] +#endif internal ObjectReferenceWithContext(IntPtr thisPtr, IntPtr contextCallbackPtr, IntPtr contextToken, Guid iid) : this(thisPtr, contextCallbackPtr, contextToken) { + if (iid == default) + { + ObjectReferenceWithContextHelper.ThrowArgumentExceptionForEmptyIid(); + } + + _iid = iid; + } + +#if NET + [RequiresUnreferencedCode(AttributeMessages.GenericRequiresUnreferencedCodeMessage)] + [Obsolete(AttributeMessages.GenericDeprecatedMessage)] + [EditorBrowsable(EditorBrowsableState.Never)] +#endif + internal ObjectReferenceWithContext(IntPtr thisPtr, T vftblT, IntPtr contextCallbackPtr, IntPtr contextToken) + : base(thisPtr, vftblT) + { + _contextCallbackPtr = contextCallbackPtr; + _contextToken = contextToken; + } + +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "This constructor is setting the '_iid' field directly.")] +#endif + internal ObjectReferenceWithContext(IntPtr thisPtr, T vftblT, IntPtr contextCallbackPtr, IntPtr contextToken, Guid iid) + : this(thisPtr, vftblT, contextCallbackPtr, contextToken) + { + if (iid == default) + { + ObjectReferenceWithContextHelper.ThrowArgumentExceptionForEmptyIid(); + } + _iid = iid; } @@ -559,6 +844,11 @@ private protected override IntPtr GetThisPtrForCurrentContext() return cachedObjRef.ThisPtr; } + private protected override IntPtr GetContextToken() + { + return this._contextToken; + } + private protected override T GetVftblForCurrentContext() { ObjectReference cachedObjRef = GetCurrentContext(); @@ -582,11 +872,36 @@ private ObjectReference GetCurrentContext() return null; } - return CachedContext.GetOrAdd(currentContext, CreateForCurrentContext); + // We use a non-generic map of just values, to avoid all generic instantiations + // of ConcurrentDictionary<,> and transitively dependent types for every vtable type T, since it's not + // something we actually need. Because the cache is private and we're the only ones using it, we can + // just store the per-context agile references as IObjectReference values, and then cast them on return. +#if NET + IObjectReference objectReference = CachedContext.GetOrAdd(currentContext, ContextCallbackHolder.Value, this); +#else + IObjectReference objectReference = CachedContext.GetOrAdd(currentContext, ptr => ContextCallbackHolder.Value(ptr, this)); +#endif + + return Unsafe.As>(objectReference); + } + + private static class ContextCallbackHolder + { + // We have a single lambda expression in this type, so we can manually rewrite it to a 'static readonly' + // field. This avoids the extra logic to lazily initialized it (it's already lazily initialized because + // it's in a 'beforefieldinit' type which is only used when the lambda is actually needed), and also it + // allows storing the entire delegate in the Frozen Object Heap (FOH) on modern runtimes. + public static readonly Func Value = CreateForCurrentContext; - ObjectReference CreateForCurrentContext(IntPtr _) +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2087", Justification = "The '_iid' field is only empty when using annotated APIs not trim-safe.")] +#endif + private static IObjectReference CreateForCurrentContext(IntPtr _, IObjectReference state) { - var agileReference = AgileReference; + ObjectReferenceWithContext @this = Unsafe.As>(state); + + var agileReference = @this.AgileReference; + // We may fail to switch context and thereby not get an agile reference. // In these cases, fallback to using the current context. if (agileReference == null) @@ -596,13 +911,22 @@ ObjectReference CreateForCurrentContext(IntPtr _) try { - if (_iid == Guid.Empty) +#if NET + // On NAOT, we can always assume the IID will not be empty, as the only way to reach that path is by + // going through a trim-unsafe constructor, which is explicitly not supported in this configuration. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return agileReference.Get(@this._iid); + } +#endif + + if (@this._iid == Guid.Empty) { return agileReference.Get(GuidGenerator.GetIID(typeof(T))); } else { - return agileReference.Get(_iid); + return agileReference.Get(@this._iid); } } catch (Exception) @@ -621,14 +945,48 @@ protected override unsafe void Release() CachedContext.Clear(); } - Context.CallInContext(_contextCallbackPtr, _contextToken, base.Release, ReleaseWithoutContext); + Context.CallInContext( + _contextCallbackPtr, + _contextToken, +#if NET && CsWinRT_LANG_11_FEATURES + &Release, + &ReleaseWithoutContext, +#else + Release, + ReleaseWithoutContext, +#endif + this); + Context.DisposeContextCallback(_contextCallbackPtr); + + static void Release(object state) + { + ObjectReferenceWithContext @this = Unsafe.As>(state); + + @this.ReleaseFromBase(); + } + + static void ReleaseWithoutContext(object state) + { + ObjectReferenceWithContext @this = Unsafe.As>(state); + + @this.ReleaseWithoutContext(); + } + } + + // Helper stub to invoke 'base.Release()' on a given 'ObjectReferenceWithContext' input parameter. + // We can't just do 'param.base.Release()' (or something like that), so the only way to specifically + // invoke the base implementation of an overridden method on that object is to go through a helper + // instance method invoked on it that just calls the base implementation of the method we want. + private void ReleaseFromBase() + { + base.Release(); } public override ObjectReference AsKnownPtr(IntPtr ptr) { AddRef(true); - var objRef = new ObjectReferenceWithContext(ptr, Context.GetContextCallback(), Context.GetContextToken()) + var objRef = new ObjectReferenceWithContext(ptr, Context.GetContextCallback(), Context.GetContextToken(), IID.IID_IUnknown) { IsAggregated = IsAggregated, PreventReleaseOnDispose = IsAggregated, @@ -637,29 +995,26 @@ public override ObjectReference AsKnownPtr(IntPtr ptr) return objRef; } - public override unsafe int TryAs< -#if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] -#endif - U>(Guid iid, out ObjectReference objRef) + internal static new int TryAs(IObjectReference sourceRef, Guid iid, out ObjectReference objRef) { objRef = null; - IntPtr thatPtr = IntPtr.Zero; - int hr = VftblIUnknown.QueryInterface(ThisPtr, &iid, &thatPtr); + int hr = Marshal.QueryInterface(sourceRef.ThisPtr, ref iid, out IntPtr thatPtr); + if (hr >= 0) { - if (IsAggregated) + if (sourceRef.IsAggregated) { Marshal.Release(thatPtr); } - AddRefFromTrackerSource(); - objRef = new ObjectReferenceWithContext(thatPtr, Context.GetContextCallback(), Context.GetContextToken(), iid) + sourceRef.AddRefFromTrackerSource(); + + objRef = new ObjectReferenceWithContext(thatPtr, Context.GetContextCallback(), Context.GetContextToken(), iid) { - IsAggregated = IsAggregated, - PreventReleaseOnDispose = IsAggregated, - ReferenceTrackerPtr = ReferenceTrackerPtr + IsAggregated = sourceRef.IsAggregated, + PreventReleaseOnDispose = sourceRef.IsAggregated, + ReferenceTrackerPtr = sourceRef.ReferenceTrackerPtr }; } @@ -667,6 +1022,18 @@ public override unsafe int TryAs< } } + internal static class ObjectReferenceWithContextHelper + { + public static void ThrowArgumentExceptionForEmptyIid() + { + throw new ArgumentException("The input argument 'iid' cannot be empty and must be set to a valid IID."); + } + } + + internal interface IObjectReferenceWithContext + { + } + #if EMBED internal #else @@ -710,7 +1077,7 @@ public unsafe readonly IntPtr Detach() // If the ptr is not owned by this instance, do an AddRef. if (preventReleaseOnDispose && ptr != IntPtr.Zero) { - (**(IUnknownVftbl**)ptr).AddRef(ptr); + Marshal.AddRef(ptr); } // Release tracker source reference as it is no longer a managed ref maintained by RCW. @@ -731,7 +1098,7 @@ public unsafe readonly void Dispose() if (!preventReleaseOnDispose && ptr != IntPtr.Zero) { - (**(IUnknownVftbl**)ptr).Release(ptr); + Marshal.Release(ptr); } } } diff --git a/src/WinRT.Runtime/Platform.cs b/src/WinRT.Runtime/Platform.cs new file mode 100644 index 000000000..9bb619d85 --- /dev/null +++ b/src/WinRT.Runtime/Platform.cs @@ -0,0 +1,181 @@ +using System; +using System.Runtime.InteropServices; + +namespace WinRT.Interop +{ + // Direct P/Invoke for platform helpers + internal static partial class Platform + { + [DllImport("api-ms-win-core-com-l1-1-0.dll")] + public static extern unsafe int CoCreateInstance(Guid* clsid, IntPtr outer, uint clsContext, Guid* iid, IntPtr* instance); + + [DllImport("api-ms-win-core-com-l1-1-0.dll")] + public static extern int CoDecrementMTAUsage(IntPtr cookie); + + [DllImport("api-ms-win-core-com-l1-1-0.dll")] + public static extern unsafe int CoIncrementMTAUsage(IntPtr* cookie); + + [DllImport("api-ms-win-core-winrt-l1-1-0.dll")] + public static extern unsafe int RoGetActivationFactory(IntPtr runtimeClassId, Guid* iid, IntPtr* factory); + + [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] + public static extern unsafe int WindowsCreateString(ushort* sourceString, int length, IntPtr* hstring); + + [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] + public static extern unsafe int WindowsCreateStringReference( + ushort* sourceString, + int length, + IntPtr* hstring_header, + IntPtr* hstring); + + [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] + public static extern int WindowsDeleteString(IntPtr hstring); + + [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] + public static extern unsafe char* WindowsGetStringRawBuffer(IntPtr hstring, uint* length); + + [DllImport("api-ms-win-core-com-l1-1-1.dll", CallingConvention = CallingConvention.StdCall)] + public static extern unsafe int RoGetAgileReference(uint options, Guid* iid, IntPtr unknown, IntPtr* agileReference); + + [DllImport("api-ms-win-core-com-l1-1-0.dll")] + public static extern unsafe int CoGetContextToken(IntPtr* contextToken); + + [DllImport("api-ms-win-core-com-l1-1-0.dll")] + public static extern unsafe int CoGetObjectContext(Guid* riid, IntPtr* ppv); + + [DllImport("oleaut32.dll")] + public static extern int SetErrorInfo(uint dwReserved, IntPtr perrinfo); + + [DllImport("api-ms-win-core-com-l1-1-0.dll")] + public static extern unsafe int CoCreateFreeThreadedMarshaler(IntPtr outer, IntPtr* marshalerPtr); + + [DllImport("kernel32.dll")] + public static extern unsafe uint FormatMessageW(uint dwFlags, void* lpSource, uint dwMessageId, uint dwLanguageId, char** lpBuffer, uint nSize, void* pArguments); + + [DllImport("kernel32.dll")] + public static extern unsafe void* LocalFree(void* hMem); + } + + // Handcrafted P/Invoke with TFM-specific handling, or thin high-level abstractions (eg. 'TryGetProcAddress'/'GetProcAddress') + partial class Platform + { + public static bool FreeLibrary(IntPtr moduleHandle) + { +#if NET8_0_OR_GREATER + return LibraryImportStubs.FreeLibrary(moduleHandle); +#else + return FreeLibrary(moduleHandle); + + [DllImport("kernel32.dll", SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + static extern bool FreeLibrary(IntPtr moduleHandle); +#endif + } + + public static unsafe IntPtr TryGetProcAddress(IntPtr moduleHandle, ReadOnlySpan functionName) + { + fixed (byte* lpFunctionName = functionName) + { +#if NET8_0_OR_GREATER + return LibraryImportStubs.GetProcAddress(moduleHandle, (sbyte*)lpFunctionName); +#else + return GetProcAddress(moduleHandle, (sbyte*)lpFunctionName); + + [DllImport("kernel32.dll", SetLastError = true)] + static extern unsafe IntPtr GetProcAddress(IntPtr nativeModuleHandle, sbyte* nativeFunctionName); +#endif + } + } + + public static IntPtr GetProcAddress(IntPtr moduleHandle, ReadOnlySpan functionName) + { + IntPtr functionPtr = TryGetProcAddress(moduleHandle, functionName); + + if (functionPtr == IntPtr.Zero) + { + Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error(), new IntPtr(-1)); + } + + return functionPtr; + } + + public static unsafe IntPtr LoadLibraryExW(string fileName, IntPtr fileHandle, uint flags) + { + fixed (char* lpFileName = fileName) + { +#if NET8_0_OR_GREATER + return LibraryImportStubs.LoadLibraryExW((ushort*)lpFileName, fileHandle, flags); +#else + return LoadLibraryExW((ushort*)lpFileName, fileHandle, flags); + + [DllImport("kernel32.dll", SetLastError = true)] + static unsafe extern IntPtr LoadLibraryExW(ushort* fileName, IntPtr fileHandle, uint flags); +#endif + } + } + } + +#if NET8_0_OR_GREATER + // Marshalling stubs from [LibraryImport], which are used to get the same semantics (eg. for setting + // the last P/Invoke errors, etc.) on .NET 6 as well ([LibraryImport] was only introduced in .NET 7). + internal static class LibraryImportStubs + { + public static bool FreeLibrary(IntPtr moduleHandle) + { + int lastError; + bool returnValue; + int nativeReturnValue; + { + Marshal.SetLastSystemError(0); + nativeReturnValue = PInvoke(moduleHandle); + lastError = Marshal.GetLastSystemError(); + } + + // Unmarshal - Convert native data to managed data. + returnValue = nativeReturnValue != 0; + Marshal.SetLastPInvokeError(lastError); + return returnValue; + + // Local P/Invoke + [DllImport("kernel32.dll", EntryPoint = "FreeLibrary", ExactSpelling = true)] + static extern unsafe int PInvoke(IntPtr nativeModuleHandle); + } + + public static unsafe IntPtr GetProcAddress(IntPtr moduleHandle, sbyte* functionName) + { + int lastError; + IntPtr returnValue; + { + Marshal.SetLastSystemError(0); + returnValue = PInvoke(moduleHandle, functionName); + lastError = Marshal.GetLastSystemError(); + } + + Marshal.SetLastPInvokeError(lastError); + return returnValue; + + // Local P/Invoke + [DllImport("kernel32.dll", EntryPoint = "GetProcAddress", ExactSpelling = true)] + static extern unsafe IntPtr PInvoke(IntPtr nativeModuleHandle, sbyte* nativeFunctionName); + } + + public static unsafe IntPtr LoadLibraryExW(ushort* fileName, IntPtr fileHandle, uint flags) + { + int lastError; + IntPtr returnValue; + { + Marshal.SetLastSystemError(0); + returnValue = PInvoke(fileName, fileHandle, flags); + lastError = Marshal.GetLastSystemError(); + } + + Marshal.SetLastPInvokeError(lastError); + return returnValue; + + // Local P/Invoke + [DllImport("kernel32.dll", EntryPoint = "LoadLibraryExW", ExactSpelling = true)] + static extern unsafe IntPtr PInvoke(ushort* nativeFileName, IntPtr nativeFileHandle, uint nativeFlags); + } + } +#endif +} diff --git a/src/WinRT.Runtime/Projections.CustomTypeMappings.EventHandler.cs b/src/WinRT.Runtime/Projections.CustomTypeMappings.EventHandler.cs new file mode 100644 index 000000000..1866dd509 --- /dev/null +++ b/src/WinRT.Runtime/Projections.CustomTypeMappings.EventHandler.cs @@ -0,0 +1,211 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#if NET + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Numerics; +using System.Threading; +using System.Windows.Input; +using Microsoft.UI.Xaml.Interop; +using Windows.Foundation.Collections; +using static System.Runtime.InteropServices.ComWrappers; + +#nullable enable + +namespace WinRT +{ + /// + partial class Projections + { + private static int _EventHandler; + private static int _NotifyCollectionChangedEventHandler; + private static int _PropertyChangedEventHandler; + + /// + /// ABI interfaces for , conditionally set from + /// to avoid rooting when the type mapping for this type isn't needed. + /// + private static ComInterfaceEntry[]? _AbiEventHandlerExposedInterfaces; + + /// + /// ABI interfaces for , conditionally set from + /// to avoid rooting when the type mapping for this type isn't needed. + /// + private static ComInterfaceEntry[]? _AbiNotifyCollectionChangedEventHandlerExposedInterfaces; + + /// + /// ABI interfaces for , conditionally set from + /// to avoid rooting when the type mapping for this type isn't needed. + /// + private static ComInterfaceEntry[]? _AbiPropertyChangedEventHandlerExposedInterfaces; + + /// Registers the custom ABI type mapping for the type. + public static void RegisterEventHandlerMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _EventHandler, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(EventHandler), + typeof(ABI.System.EventHandler)); + + _AbiEventHandlerExposedInterfaces = ABI.System.EventHandler.GetExposedInterfaces(); + } + + /// + /// Gets the ABI interfaces for . + /// + /// The ABI interfaces for . + /// Thrown if the type mapping is disabled. + internal static ComInterfaceEntry[] GetAbiEventHandlerExposedInterfaces() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return ABI.System.EventHandler.GetExposedInterfaces(); + } + + ComInterfaceEntry[]? interfaces = _AbiEventHandlerExposedInterfaces; + + if (interfaces is null) + { + throw new NotSupportedException( + "Support for type mapping for the 'EventHandler' type is currently disabled. " + + "To enable it, either make sure to not set the 'CsWinRTEnableCustomTypeMappings' property to 'false', " + + "or manually enable support for this specific type by calling 'Projections.RegisterEventHandlerMapping()'."); + } + + return interfaces; + } + + /// Registers the custom ABI type mapping for the "Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventHandler" WinRT type. + public static void RegisterNotifyCollectionChangedEventHandlerMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _NotifyCollectionChangedEventHandler, 1, 0) == 1) + { + return; + } + + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + RegisterCustomAbiTypeMapping( + typeof(NotifyCollectionChangedEventHandler), + typeof(ABI.System.Collections.Specialized.NotifyCollectionChangedEventHandler), + "Windows.UI.Xaml.Interop.NotifyCollectionChangedEventHandler", + isRuntimeClass: false); + } + else + { + RegisterCustomAbiTypeMapping( + typeof(NotifyCollectionChangedEventHandler), + typeof(ABI.System.Collections.Specialized.NotifyCollectionChangedEventHandler), + "Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventHandler", + isRuntimeClass: false); + } + + _AbiNotifyCollectionChangedEventHandlerExposedInterfaces = ABI.System.Collections.Specialized.NotifyCollectionChangedEventHandler.GetExposedInterfaces(); + } + + /// + /// Gets the ABI interfaces for . + /// + /// The ABI interfaces for . + /// Thrown if the type mapping is disabled. + internal static ComInterfaceEntry[] GetAbiNotifyCollectionChangedEventHandlerExposedInterfaces() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return ABI.System.Collections.Specialized.NotifyCollectionChangedEventHandler.GetExposedInterfaces(); + } + + ComInterfaceEntry[]? interfaces = _AbiNotifyCollectionChangedEventHandlerExposedInterfaces; + + if (interfaces is null) + { + throw new NotSupportedException( + "Support for type mapping for the 'NotifyCollectionChangedEventHandler' type is currently disabled. " + + "To enable it, either make sure to not set the 'CsWinRTEnableCustomTypeMappings' property to 'false', " + + "or manually enable support for this specific type by calling 'Projections.RegisterNotifyCollectionChangedEventHandlerMapping()'."); + } + + return interfaces; + } + + /// Registers the custom ABI type mapping for the "Microsoft.UI.Xaml.Data.PropertyChangedEventHandler" WinRT type. + public static void RegisterPropertyChangedEventHandlerMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _PropertyChangedEventHandler, 1, 0) == 1) + { + return; + } + + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + RegisterCustomAbiTypeMapping( + typeof(PropertyChangedEventHandler), + typeof(ABI.System.ComponentModel.PropertyChangedEventHandler), + "Windows.UI.Xaml.Data.PropertyChangedEventHandler", + isRuntimeClass: false); + } + else + { + RegisterCustomAbiTypeMapping( + typeof(PropertyChangedEventHandler), + typeof(ABI.System.ComponentModel.PropertyChangedEventHandler), + "Microsoft.UI.Xaml.Data.PropertyChangedEventHandler", + isRuntimeClass: false); + } + + _AbiPropertyChangedEventHandlerExposedInterfaces = ABI.System.ComponentModel.PropertyChangedEventHandler.GetExposedInterfaces(); + } + + /// + /// Gets the ABI interfaces for . + /// + /// The ABI interfaces for . + /// Thrown if the type mapping is disabled. + internal static ComInterfaceEntry[] GetAbiPropertyChangedEventHandlerExposedInterfaces() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return ABI.System.ComponentModel.PropertyChangedEventHandler.GetExposedInterfaces(); + } + + ComInterfaceEntry[]? interfaces = _AbiPropertyChangedEventHandlerExposedInterfaces; + + if (interfaces is null) + { + throw new NotSupportedException( + "Support for type mapping for the 'PropertyChangedEventHandler' type is currently disabled. " + + "To enable it, either make sure to not set the 'CsWinRTEnableCustomTypeMappings' property to 'false', " + + "or manually enable support for this specific type by calling 'Projections.RegisterPropertyChangedEventHandlerMapping()'."); + } + + return interfaces; + } + } +} + +#endif \ No newline at end of file diff --git a/src/WinRT.Runtime/Projections.CustomTypeMappings.g.cs b/src/WinRT.Runtime/Projections.CustomTypeMappings.g.cs new file mode 100644 index 000000000..9f23288fd --- /dev/null +++ b/src/WinRT.Runtime/Projections.CustomTypeMappings.g.cs @@ -0,0 +1,1288 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#if NET + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Numerics; +using System.Threading; +using System.Windows.Input; +using Microsoft.UI.Xaml.Interop; +using Windows.Foundation.Collections; + +namespace WinRT +{ + /// + partial class Projections + { + private static int _EventRegistrationToken; + private static int _Nullable__; + private static int _int_; + private static int _byte_; + private static int _sbyte_; + private static int _short_; + private static int _ushort_; + private static int _uint_; + private static int _long_; + private static int _ulong_; + private static int _float_; + private static int _double_; + private static int _char_; + private static int _bool_; + private static int _Guid_; + private static int _DateTimeOffset_; + private static int _TimeSpan_; + private static int _DateTimeOffset; + private static int _Exception; + private static int _TimeSpan; + private static int _Uri; + private static int _DataErrorsChangedEventArgs; + private static int _PropertyChangedEventArgs; + private static int _INotifyDataErrorInfo; + private static int _INotifyPropertyChanged; + private static int _ICommand; + private static int _IServiceProvider; + private static int _EventHandler__; + private static int _KeyValuePair___; + private static int _IEnumerable__; + private static int _IEnumerator__; + private static int _IList__; + private static int _IReadOnlyList__; + private static int _IDictionary___; + private static int _IReadOnlyDictionary___; + private static int _IDisposable; + private static int _IEnumerable; + private static int _IList; + private static int _INotifyCollectionChanged; + private static int _NotifyCollectionChangedAction; + private static int _NotifyCollectionChangedEventArgs; + private static int _Matrix3x2; + private static int _Matrix4x4; + private static int _Plane; + private static int _Quaternion; + private static int _Vector2; + private static int _Vector3; + private static int _Vector4; + private static int _IMap___; + private static int _IVector__; + private static int _IMapView___; + private static int _IVectorView__; + private static int _IBindableVector; + private static int _ICollection__; + private static int _IReadOnlyCollection__; + private static int _ICollection; + + /// Registers the custom ABI type mapping for the "Windows.Foundation.EventRegistrationToken" WinRT type. + public static void RegisterEventRegistrationTokenMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _EventRegistrationToken, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(EventRegistrationToken), + typeof(ABI.WinRT.EventRegistrationToken), + "Windows.Foundation.EventRegistrationToken", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IReference`1" WinRT type. + public static void RegisterNullableOpenGenericMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _Nullable__, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(Nullable<>), + typeof(ABI.System.Nullable<>), + "Windows.Foundation.IReference`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IReference`1" WinRT type. + public static void RegisterNullableIntMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _int_, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(int?), + typeof(ABI.System.Nullable_int), + "Windows.Foundation.IReference`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IReference`1" WinRT type. + public static void RegisterNullableByteMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _byte_, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(byte?), + typeof(ABI.System.Nullable_byte), + "Windows.Foundation.IReference`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IReference`1" WinRT type. + public static void RegisterNullableSByteMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _sbyte_, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(sbyte?), + typeof(ABI.System.Nullable_sbyte), + "Windows.Foundation.IReference`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IReference`1" WinRT type. + public static void RegisterNullableShortMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _short_, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(short?), + typeof(ABI.System.Nullable_short), + "Windows.Foundation.IReference`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IReference`1" WinRT type. + public static void RegisterNullableUShortMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _ushort_, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(ushort?), + typeof(ABI.System.Nullable_ushort), + "Windows.Foundation.IReference`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IReference`1" WinRT type. + public static void RegisterNullableUIntMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _uint_, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(uint?), + typeof(ABI.System.Nullable_uint), + "Windows.Foundation.IReference`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IReference`1" WinRT type. + public static void RegisterNullableLongMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _long_, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(long?), + typeof(ABI.System.Nullable_long), + "Windows.Foundation.IReference`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IReference`1" WinRT type. + public static void RegisterNullableULongMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _ulong_, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(ulong?), + typeof(ABI.System.Nullable_ulong), + "Windows.Foundation.IReference`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IReference`1" WinRT type. + public static void RegisterNullableFloatMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _float_, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(float?), + typeof(ABI.System.Nullable_float), + "Windows.Foundation.IReference`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IReference`1" WinRT type. + public static void RegisterNullableDoubleMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _double_, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(double?), + typeof(ABI.System.Nullable_double), + "Windows.Foundation.IReference`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IReference`1" WinRT type. + public static void RegisterNullableCharMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _char_, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(char?), + typeof(ABI.System.Nullable_char), + "Windows.Foundation.IReference`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IReference`1" WinRT type. + public static void RegisterNullableBoolMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _bool_, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(bool?), + typeof(ABI.System.Nullable_bool), + "Windows.Foundation.IReference`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IReference`1" WinRT type. + public static void RegisterNullableGuidMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _Guid_, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(Guid?), + typeof(ABI.System.Nullable_guid), + "Windows.Foundation.IReference`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IReference`1" WinRT type. + public static void RegisterNullableDateTimeOffsetMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _DateTimeOffset_, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(DateTimeOffset?), + typeof(ABI.System.Nullable_DateTimeOffset), + "Windows.Foundation.IReference`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IReference`1" WinRT type. + public static void RegisterNullableTimeSpanMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _TimeSpan_, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(TimeSpan?), + typeof(ABI.System.Nullable_TimeSpan), + "Windows.Foundation.IReference`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.DateTime" WinRT type. + public static void RegisterDateTimeOffsetMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _DateTimeOffset, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(DateTimeOffset), + typeof(ABI.System.DateTimeOffset), + "Windows.Foundation.DateTime", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.HResult" WinRT type. + public static void RegisterExceptionMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _Exception, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(Exception), + typeof(ABI.System.Exception), + "Windows.Foundation.HResult", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.TimeSpan" WinRT type. + public static void RegisterTimeSpanMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _TimeSpan, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(TimeSpan), + typeof(ABI.System.TimeSpan), + "Windows.Foundation.TimeSpan", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.Uri" WinRT type. + public static void RegisterUriMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _Uri, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(Uri), + typeof(ABI.System.Uri), + "Windows.Foundation.Uri", + isRuntimeClass: true); + } + + /// Registers the custom ABI type mapping for the "Microsoft.UI.Xaml.Data.DataErrorsChangedEventArgs" WinRT type. + public static void RegisterDataErrorsChangedEventArgsMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + throw new NotSupportedException("The 'DataErrorsChangedEventArgs' type is only supported for WinUI, and not when using System XAML projections (make sure the 'CsWinRTUseWindowsUIXamlProjections' property is not set to 'true')."); + } + + if (Interlocked.CompareExchange(ref _DataErrorsChangedEventArgs, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(DataErrorsChangedEventArgs), + typeof(ABI.System.ComponentModel.DataErrorsChangedEventArgs), + "Microsoft.UI.Xaml.Data.DataErrorsChangedEventArgs", + isRuntimeClass: true); + } + + /// Registers the custom ABI type mapping for the "Microsoft.UI.Xaml.Data.PropertyChangedEventArgs" WinRT type. + public static void RegisterPropertyChangedEventArgsMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _PropertyChangedEventArgs, 1, 0) == 1) + { + return; + } + + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + RegisterCustomAbiTypeMapping( + typeof(PropertyChangedEventArgs), + typeof(ABI.System.ComponentModel.PropertyChangedEventArgs), + "Windows.UI.Xaml.Data.PropertyChangedEventArgs", + isRuntimeClass: true); + } + else + { + RegisterCustomAbiTypeMapping( + typeof(PropertyChangedEventArgs), + typeof(ABI.System.ComponentModel.PropertyChangedEventArgs), + "Microsoft.UI.Xaml.Data.PropertyChangedEventArgs", + isRuntimeClass: true); + } + } + + /// Registers the custom ABI type mapping for the "Microsoft.UI.Xaml.Data.INotifyDataErrorInfo" WinRT type. + public static void RegisterINotifyDataErrorInfoMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + throw new NotSupportedException("The 'INotifyDataErrorInfo' type is only supported for WinUI, and not when using System XAML projections (make sure the 'CsWinRTUseWindowsUIXamlProjections' property is not set to 'true')."); + } + + if (Interlocked.CompareExchange(ref _INotifyDataErrorInfo, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(INotifyDataErrorInfo), + typeof(ABI.System.ComponentModel.INotifyDataErrorInfo), + "Microsoft.UI.Xaml.Data.INotifyDataErrorInfo", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Microsoft.UI.Xaml.Data.INotifyPropertyChanged" WinRT type. + public static void RegisterINotifyPropertyChangedMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _INotifyPropertyChanged, 1, 0) == 1) + { + return; + } + + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + RegisterCustomAbiTypeMapping( + typeof(INotifyPropertyChanged), + typeof(ABI.System.ComponentModel.INotifyPropertyChanged), + "Windows.UI.Xaml.Data.INotifyPropertyChanged", + isRuntimeClass: false); + } + else + { + RegisterCustomAbiTypeMapping( + typeof(INotifyPropertyChanged), + typeof(ABI.System.ComponentModel.INotifyPropertyChanged), + "Microsoft.UI.Xaml.Data.INotifyPropertyChanged", + isRuntimeClass: false); + } + } + + /// Registers the custom ABI type mapping for the "Microsoft.UI.Xaml.Interop.ICommand" WinRT type. + public static void RegisterICommandMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _ICommand, 1, 0) == 1) + { + return; + } + + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + RegisterCustomAbiTypeMapping( + typeof(ICommand), + typeof(ABI.System.Windows.Input.ICommand), + "Windows.UI.Xaml.Interop.ICommand", + isRuntimeClass: false); + } + else + { + RegisterCustomAbiTypeMapping( + typeof(ICommand), + typeof(ABI.System.Windows.Input.ICommand), + "Microsoft.UI.Xaml.Interop.ICommand", + isRuntimeClass: false); + } + } + + /// Registers the custom ABI type mapping for the "Microsoft.UI.Xaml.IXamlServiceProvider" WinRT type. + public static void RegisterIServiceProviderMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + throw new NotSupportedException("The 'IServiceProvider' type is only supported for WinUI, and not when using System XAML projections (make sure the 'CsWinRTUseWindowsUIXamlProjections' property is not set to 'true')."); + } + + if (Interlocked.CompareExchange(ref _IServiceProvider, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(IServiceProvider), + typeof(ABI.System.IServiceProvider), + "Microsoft.UI.Xaml.IXamlServiceProvider", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.EventHandler`1" WinRT type. + public static void RegisterEventHandlerOpenGenericMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _EventHandler__, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(EventHandler<>), + typeof(ABI.System.EventHandler<>), + "Windows.Foundation.EventHandler`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.Collections.IKeyValuePair`2" WinRT type. + public static void RegisterKeyValuePairOpenGenericMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _KeyValuePair___, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(KeyValuePair<,>), + typeof(ABI.System.Collections.Generic.KeyValuePair<,>), + "Windows.Foundation.Collections.IKeyValuePair`2", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.Collections.IIterable`1" WinRT type. + public static void RegisterIEnumerableOpenGenericMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _IEnumerable__, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(IEnumerable<>), + typeof(ABI.System.Collections.Generic.IEnumerable<>), + "Windows.Foundation.Collections.IIterable`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.Collections.IIterator`1" WinRT type. + public static void RegisterIEnumeratorOpenGenericMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _IEnumerator__, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(IEnumerator<>), + typeof(ABI.System.Collections.Generic.IEnumerator<>), + "Windows.Foundation.Collections.IIterator`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.Collections.IVector`1" WinRT type. + public static void RegisterIListOpenGenericMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _IList__, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(IList<>), + typeof(ABI.System.Collections.Generic.IList<>), + "Windows.Foundation.Collections.IVector`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.Collections.IVectorView`1" WinRT type. + public static void RegisterIReadOnlyListOpenGenericMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _IReadOnlyList__, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(IReadOnlyList<>), + typeof(ABI.System.Collections.Generic.IReadOnlyList<>), + "Windows.Foundation.Collections.IVectorView`1", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.Collections.IMap`2" WinRT type. + public static void RegisterIDictionaryOpenGenericMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _IDictionary___, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(IDictionary<,>), + typeof(ABI.System.Collections.Generic.IDictionary<,>), + "Windows.Foundation.Collections.IMap`2", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.Collections.IMapView`2" WinRT type. + public static void RegisterIReadOnlyDictionaryOpenGenericMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _IReadOnlyDictionary___, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(IReadOnlyDictionary<,>), + typeof(ABI.System.Collections.Generic.IReadOnlyDictionary<,>), + "Windows.Foundation.Collections.IMapView`2", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.IClosable" WinRT type. + public static void RegisterIDisposableMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _IDisposable, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(IDisposable), + typeof(ABI.System.IDisposable), + "Windows.Foundation.IClosable", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Microsoft.UI.Xaml.Interop.IBindableIterable" WinRT type. + public static void RegisterIEnumerableMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _IEnumerable, 1, 0) == 1) + { + return; + } + + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + RegisterCustomAbiTypeMapping( + typeof(IEnumerable), + typeof(ABI.System.Collections.IEnumerable), + "Windows.UI.Xaml.Interop.IBindableIterable", + isRuntimeClass: false); + } + else + { + RegisterCustomAbiTypeMapping( + typeof(IEnumerable), + typeof(ABI.System.Collections.IEnumerable), + "Microsoft.UI.Xaml.Interop.IBindableIterable", + isRuntimeClass: false); + } + } + + /// Registers the custom ABI type mapping for the "Microsoft.UI.Xaml.Interop.IBindableVector" WinRT type. + public static void RegisterIListMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _IList, 1, 0) == 1) + { + return; + } + + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + RegisterCustomAbiTypeMapping( + typeof(IList), + typeof(ABI.System.Collections.IList), + "Windows.UI.Xaml.Interop.IBindableVector", + isRuntimeClass: false); + } + else + { + RegisterCustomAbiTypeMapping( + typeof(IList), + typeof(ABI.System.Collections.IList), + "Microsoft.UI.Xaml.Interop.IBindableVector", + isRuntimeClass: false); + } + } + + /// Registers the custom ABI type mapping for the "Microsoft.UI.Xaml.Interop.INotifyCollectionChanged" WinRT type. + public static void RegisterINotifyCollectionChangedMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _INotifyCollectionChanged, 1, 0) == 1) + { + return; + } + + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + RegisterCustomAbiTypeMapping( + typeof(INotifyCollectionChanged), + typeof(ABI.System.Collections.Specialized.INotifyCollectionChanged), + "Windows.UI.Xaml.Interop.INotifyCollectionChanged", + isRuntimeClass: false); + } + else + { + RegisterCustomAbiTypeMapping( + typeof(INotifyCollectionChanged), + typeof(ABI.System.Collections.Specialized.INotifyCollectionChanged), + "Microsoft.UI.Xaml.Interop.INotifyCollectionChanged", + isRuntimeClass: false); + } + } + + /// Registers the custom ABI type mapping for the "Microsoft.UI.Xaml.Interop.NotifyCollectionChangedAction" WinRT type. + public static void RegisterNotifyCollectionChangedActionMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _NotifyCollectionChangedAction, 1, 0) == 1) + { + return; + } + + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + RegisterCustomAbiTypeMapping( + typeof(NotifyCollectionChangedAction), + typeof(ABI.System.Collections.Specialized.NotifyCollectionChangedAction), + "Windows.UI.Xaml.Interop.NotifyCollectionChangedAction", + isRuntimeClass: false); + } + else + { + RegisterCustomAbiTypeMapping( + typeof(NotifyCollectionChangedAction), + typeof(ABI.System.Collections.Specialized.NotifyCollectionChangedAction), + "Microsoft.UI.Xaml.Interop.NotifyCollectionChangedAction", + isRuntimeClass: false); + } + } + + /// Registers the custom ABI type mapping for the "Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventArgs" WinRT type. + public static void RegisterNotifyCollectionChangedEventArgsMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _NotifyCollectionChangedEventArgs, 1, 0) == 1) + { + return; + } + + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + RegisterCustomAbiTypeMapping( + typeof(NotifyCollectionChangedEventArgs), + typeof(ABI.System.Collections.Specialized.NotifyCollectionChangedEventArgs), + "Windows.UI.Xaml.Interop.NotifyCollectionChangedEventArgs", + isRuntimeClass: true); + } + else + { + RegisterCustomAbiTypeMapping( + typeof(NotifyCollectionChangedEventArgs), + typeof(ABI.System.Collections.Specialized.NotifyCollectionChangedEventArgs), + "Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventArgs", + isRuntimeClass: true); + } + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.Numerics.Matrix3x2" WinRT type. + public static void RegisterMatrix3x2Mapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _Matrix3x2, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(Matrix3x2), + typeof(ABI.System.Numerics.Matrix3x2), + "Windows.Foundation.Numerics.Matrix3x2", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.Numerics.Matrix4x4" WinRT type. + public static void RegisterMatrix4x4Mapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _Matrix4x4, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(Matrix4x4), + typeof(ABI.System.Numerics.Matrix4x4), + "Windows.Foundation.Numerics.Matrix4x4", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.Numerics.Plane" WinRT type. + public static void RegisterPlaneMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _Plane, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(Plane), + typeof(ABI.System.Numerics.Plane), + "Windows.Foundation.Numerics.Plane", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.Numerics.Quaternion" WinRT type. + public static void RegisterQuaternionMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _Quaternion, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(Quaternion), + typeof(ABI.System.Numerics.Quaternion), + "Windows.Foundation.Numerics.Quaternion", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.Numerics.Vector2" WinRT type. + public static void RegisterVector2Mapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _Vector2, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(Vector2), + typeof(ABI.System.Numerics.Vector2), + "Windows.Foundation.Numerics.Vector2", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.Numerics.Vector3" WinRT type. + public static void RegisterVector3Mapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _Vector3, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(Vector3), + typeof(ABI.System.Numerics.Vector3), + "Windows.Foundation.Numerics.Vector3", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the "Windows.Foundation.Numerics.Vector4" WinRT type. + public static void RegisterVector4Mapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _Vector4, 1, 0) == 1) + { + return; + } + + RegisterCustomAbiTypeMapping( + typeof(Vector4), + typeof(ABI.System.Numerics.Vector4), + "Windows.Foundation.Numerics.Vector4", + isRuntimeClass: false); + } + + /// Registers the custom ABI type mapping for the type. + public static void RegisterIMapOpenGenericMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _IMap___, 1, 0) == 1) + { + return; + } + + RegisterCustomTypeToHelperTypeMapping( + typeof(IMap<,>), + typeof(ABI.System.Collections.Generic.IDictionary<,>)); + } + + /// Registers the custom ABI type mapping for the type. + public static void RegisterIVectorOpenGenericMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _IVector__, 1, 0) == 1) + { + return; + } + + RegisterCustomTypeToHelperTypeMapping( + typeof(IVector<>), + typeof(ABI.System.Collections.Generic.IList<>)); + } + + /// Registers the custom ABI type mapping for the type. + public static void RegisterIMapViewOpenGenericMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _IMapView___, 1, 0) == 1) + { + return; + } + + RegisterCustomTypeToHelperTypeMapping( + typeof(IMapView<,>), + typeof(ABI.System.Collections.Generic.IReadOnlyDictionary<,>)); + } + + /// Registers the custom ABI type mapping for the type. + public static void RegisterIVectorViewOpenGenericMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _IVectorView__, 1, 0) == 1) + { + return; + } + + RegisterCustomTypeToHelperTypeMapping( + typeof(IVectorView<>), + typeof(ABI.System.Collections.Generic.IReadOnlyList<>)); + } + + /// Registers the custom ABI type mapping for the type. + public static void RegisterIBindableVectorMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _IBindableVector, 1, 0) == 1) + { + return; + } + + RegisterCustomTypeToHelperTypeMapping( + typeof(IBindableVector), + typeof(ABI.System.Collections.IList)); + } + + /// Registers the custom ABI type mapping for the type. + public static void RegisterICollectionOpenGenericMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _ICollection__, 1, 0) == 1) + { + return; + } + + RegisterCustomTypeToHelperTypeMapping( + typeof(ICollection<>), + typeof(ABI.System.Collections.Generic.ICollection<>)); + } + + /// Registers the custom ABI type mapping for the type. + public static void RegisterIReadOnlyCollectionOpenGenericMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _IReadOnlyCollection__, 1, 0) == 1) + { + return; + } + + RegisterCustomTypeToHelperTypeMapping( + typeof(IReadOnlyCollection<>), + typeof(ABI.System.Collections.Generic.IReadOnlyCollection<>)); + } + + /// Registers the custom ABI type mapping for the type. + public static void RegisterICollectionMapping() + { + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } + + if (Interlocked.CompareExchange(ref _ICollection, 1, 0) == 1) + { + return; + } + + RegisterCustomTypeToHelperTypeMapping( + typeof(ICollection), + typeof(ABI.System.Collections.ICollection)); + } + } +} + +#endif \ No newline at end of file diff --git a/src/WinRT.Runtime/Projections.CustomTypeMappings.tt b/src/WinRT.Runtime/Projections.CustomTypeMappings.tt new file mode 100644 index 000000000..a4e74fbbd --- /dev/null +++ b/src/WinRT.Runtime/Projections.CustomTypeMappings.tt @@ -0,0 +1,249 @@ +<#@ template debug="false" hostspecific="false" language="C#" #> +<#@ assembly name="System.Core" #> +<#@ import namespace="System.Linq" #> +<#@ import namespace="System.Text.RegularExpressions" #> +<#@ output extension=".g.cs"#> +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#if NET + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.ComponentModel; +using System.Numerics; +using System.Threading; +using System.Windows.Input; +using Microsoft.UI.Xaml.Interop; +using Windows.Foundation.Collections; + +namespace WinRT +{ + /// + partial class Projections + { +<# +// Helper method to get the generated method name +static string GetMethodName(string name, string hint) +{ + var methodTypeName = hint ?? name; + + // If the hint isn't available, automatically derive the name. For generic types, + // we strip the '<>' from the name and append "OpenGeneric" as a suffix. + if (hint == null) + { + if (name.Contains("<")) methodTypeName = methodTypeName.Substring(0, methodTypeName.IndexOf('<')); + if (name.Contains("<")) methodTypeName += "OpenGeneric"; + } + + return $"Register{methodTypeName}Mapping"; +} + +// Helper to write the guard statements for a given method. +// We need two extra indents as these are inside each method. +void WriteGuardStatements(string name) +{ +#> + if (FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } +<# + + if (name == "DataErrorsChangedEventArgs" || + name == "INotifyDataErrorInfo" || + name == "IServiceProvider") + { + WriteLine(""); +#> + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + throw new NotSupportedException("The '<#=name#>' type is only supported for WinUI, and not when using System XAML projections (make sure the 'CsWinRTUseWindowsUIXamlProjections' property is not set to 'true')."); + } +<# + } +#> + + if (Interlocked.CompareExchange(ref _<#=Regex.Replace(name, "[?<>,]", "_")#>, 1, 0) == 1) + { + return; + } +<# +} + +// Types for 'RegisterCustomAbiTypeMapping' +var registerCustomAbiTypeMappings = new (string Public, string Abi, string Name, string Hint, bool IsRuntimeClass)[] +{ + ("EventRegistrationToken", "ABI.WinRT.EventRegistrationToken", "Windows.Foundation.EventRegistrationToken", null, false), + ("Nullable<>", "ABI.System.Nullable<>", "Windows.Foundation.IReference`1", null, false), + ("int?", "ABI.System.Nullable_int", "Windows.Foundation.IReference`1", "NullableInt", false), + ("byte?", "ABI.System.Nullable_byte", "Windows.Foundation.IReference`1", "NullableByte", false), + ("sbyte?", "ABI.System.Nullable_sbyte", "Windows.Foundation.IReference`1", "NullableSByte", false), + ("short?", "ABI.System.Nullable_short", "Windows.Foundation.IReference`1", "NullableShort", false), + ("ushort?", "ABI.System.Nullable_ushort", "Windows.Foundation.IReference`1", "NullableUShort", false), + ("uint?", "ABI.System.Nullable_uint", "Windows.Foundation.IReference`1", "NullableUInt", false), + ("long?", "ABI.System.Nullable_long", "Windows.Foundation.IReference`1", "NullableLong", false), + ("ulong?", "ABI.System.Nullable_ulong", "Windows.Foundation.IReference`1", "NullableULong", false), + ("float?", "ABI.System.Nullable_float", "Windows.Foundation.IReference`1", "NullableFloat", false), + ("double?", "ABI.System.Nullable_double", "Windows.Foundation.IReference`1", "NullableDouble", false), + ("char?", "ABI.System.Nullable_char", "Windows.Foundation.IReference`1", "NullableChar", false), + ("bool?", "ABI.System.Nullable_bool", "Windows.Foundation.IReference`1", "NullableBool", false), + ("Guid?", "ABI.System.Nullable_guid", "Windows.Foundation.IReference`1", "NullableGuid", false), + ("DateTimeOffset?", "ABI.System.Nullable_DateTimeOffset", "Windows.Foundation.IReference`1", "NullableDateTimeOffset", false), + ("TimeSpan?", "ABI.System.Nullable_TimeSpan", "Windows.Foundation.IReference`1", "NullableTimeSpan", false), + ("DateTimeOffset", "ABI.System.DateTimeOffset", "Windows.Foundation.DateTime", null, false), + ("Exception", "ABI.System.Exception", "Windows.Foundation.HResult", null, false), + ("TimeSpan", "ABI.System.TimeSpan", "Windows.Foundation.TimeSpan", null, false), + ("Uri", "ABI.System.Uri", "Windows.Foundation.Uri", null, true), + ("DataErrorsChangedEventArgs", "ABI.System.ComponentModel.DataErrorsChangedEventArgs", "Microsoft.UI.Xaml.Data.DataErrorsChangedEventArgs", null, true), + ("PropertyChangedEventArgs", "ABI.System.ComponentModel.PropertyChangedEventArgs", "Microsoft.UI.Xaml.Data.PropertyChangedEventArgs", null, true), + ("INotifyDataErrorInfo", "ABI.System.ComponentModel.INotifyDataErrorInfo", "Microsoft.UI.Xaml.Data.INotifyDataErrorInfo", null, false), + ("INotifyPropertyChanged", "ABI.System.ComponentModel.INotifyPropertyChanged", "Microsoft.UI.Xaml.Data.INotifyPropertyChanged", null, false), + ("ICommand", "ABI.System.Windows.Input.ICommand", "Microsoft.UI.Xaml.Interop.ICommand", null, false), + ("IServiceProvider", "ABI.System.IServiceProvider", "Microsoft.UI.Xaml.IXamlServiceProvider", null, false), + ("EventHandler<>", "ABI.System.EventHandler<>", "Windows.Foundation.EventHandler`1", null, false), + ("KeyValuePair<,>", "ABI.System.Collections.Generic.KeyValuePair<,>", "Windows.Foundation.Collections.IKeyValuePair`2", null, false), + ("IEnumerable<>", "ABI.System.Collections.Generic.IEnumerable<>", "Windows.Foundation.Collections.IIterable`1", null, false), + ("IEnumerator<>", "ABI.System.Collections.Generic.IEnumerator<>", "Windows.Foundation.Collections.IIterator`1", null, false), + ("IList<>", "ABI.System.Collections.Generic.IList<>", "Windows.Foundation.Collections.IVector`1", null, false), + ("IReadOnlyList<>", "ABI.System.Collections.Generic.IReadOnlyList<>", "Windows.Foundation.Collections.IVectorView`1", null, false), + ("IDictionary<,>", "ABI.System.Collections.Generic.IDictionary<,>", "Windows.Foundation.Collections.IMap`2", null, false), + ("IReadOnlyDictionary<,>", "ABI.System.Collections.Generic.IReadOnlyDictionary<,>", "Windows.Foundation.Collections.IMapView`2", null, false), + ("IDisposable", "ABI.System.IDisposable", "Windows.Foundation.IClosable", null, false), + ("IEnumerable", "ABI.System.Collections.IEnumerable", "Microsoft.UI.Xaml.Interop.IBindableIterable", null, false), + ("IList", "ABI.System.Collections.IList", "Microsoft.UI.Xaml.Interop.IBindableVector", null, false), + ("INotifyCollectionChanged", "ABI.System.Collections.Specialized.INotifyCollectionChanged", "Microsoft.UI.Xaml.Interop.INotifyCollectionChanged", null, false), + ("NotifyCollectionChangedAction", "ABI.System.Collections.Specialized.NotifyCollectionChangedAction", "Microsoft.UI.Xaml.Interop.NotifyCollectionChangedAction", null, false), + ("NotifyCollectionChangedEventArgs", "ABI.System.Collections.Specialized.NotifyCollectionChangedEventArgs", "Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventArgs", null, true), + ("Matrix3x2", "ABI.System.Numerics.Matrix3x2", "Windows.Foundation.Numerics.Matrix3x2", null, false), + ("Matrix4x4", "ABI.System.Numerics.Matrix4x4", "Windows.Foundation.Numerics.Matrix4x4", null, false), + ("Plane", "ABI.System.Numerics.Plane", "Windows.Foundation.Numerics.Plane", null, false), + ("Quaternion", "ABI.System.Numerics.Quaternion", "Windows.Foundation.Numerics.Quaternion", null, false), + ("Vector2", "ABI.System.Numerics.Vector2", "Windows.Foundation.Numerics.Vector2", null, false), + ("Vector3", "ABI.System.Numerics.Vector3", "Windows.Foundation.Numerics.Vector3", null, false), + ("Vector4", "ABI.System.Numerics.Vector4", "Windows.Foundation.Numerics.Vector4", null, false) +}; + +// Types for 'RegisterCustomTypeToHelperTypeMapping' +var registerCustomTypeToHelperTypeMapping = new (string Public, string Helper)[] +{ + ("IMap<,>", "ABI.System.Collections.Generic.IDictionary<,>"), + ("IVector<>", "ABI.System.Collections.Generic.IList<>"), + ("IMapView<,>", "ABI.System.Collections.Generic.IReadOnlyDictionary<,>"), + ("IVectorView<>", "ABI.System.Collections.Generic.IReadOnlyList<>"), + ("IBindableVector", "ABI.System.Collections.IList"), + ("ICollection<>", "ABI.System.Collections.Generic.ICollection<>"), + ("IReadOnlyCollection<>", "ABI.System.Collections.Generic.IReadOnlyCollection<>"), + ("ICollection", "ABI.System.Collections.ICollection") +}; + +// Types that have different projections for System XAML +var systemXamlProjectionTypeMapping = new(string Public, string Name)[] +{ + ("PropertyChangedEventArgs", "Windows.UI.Xaml.Data.PropertyChangedEventArgs"), + ("PropertyChangedEventHandler", "Windows.UI.Xaml.Data.PropertyChangedEventHandler"), + ("INotifyPropertyChanged", "Windows.UI.Xaml.Data.INotifyPropertyChanged"), + ("ICommand", "Windows.UI.Xaml.Interop.ICommand"), + ("IEnumerable", "Windows.UI.Xaml.Interop.IBindableIterable"), + ("IList", "Windows.UI.Xaml.Interop.IBindableVector"), + ("INotifyCollectionChanged", "Windows.UI.Xaml.Interop.INotifyCollectionChanged"), + ("NotifyCollectionChangedAction", "Windows.UI.Xaml.Interop.NotifyCollectionChangedAction"), + ("NotifyCollectionChangedEventArgs", "Windows.UI.Xaml.Interop.NotifyCollectionChangedEventArgs"), + ("NotifyCollectionChangedEventHandler", "Windows.UI.Xaml.Interop.NotifyCollectionChangedEventHandler") +}; + +// Declare all fields +foreach (string fieldName in + registerCustomAbiTypeMappings + .Select(t => t.Public) + .Concat( + registerCustomTypeToHelperTypeMapping + .Select(t => t.Public)) + .Select(s => Regex.Replace(s, "[?<>,]", "_"))) +{ +#> + private static int _<#=fieldName#>; +<# +} + +// 'RegisterCustomAbiTypeMapping' methods +foreach (var type in registerCustomAbiTypeMappings) +{ + WriteLine(""); + +#> + /// Registers the custom ABI type mapping for the "<#=type.Name#>" WinRT type. + public static void <#=GetMethodName(type.Public, type.Hint)#>() + { +<# + WriteGuardStatements(type.Public); + WriteLine(""); + + int indexOfSystemXamlTypeMapping = Array.FindIndex(systemXamlProjectionTypeMapping, t => t.Public == type.Public); + + if (indexOfSystemXamlTypeMapping != -1) + { +#> + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + RegisterCustomAbiTypeMapping( + typeof(<#=type.Public#>), + typeof(<#=type.Abi#>), + "<#=systemXamlProjectionTypeMapping[indexOfSystemXamlTypeMapping].Name#>", + isRuntimeClass: <#=type.IsRuntimeClass.ToString().ToLowerInvariant()#>); + } + else + { + RegisterCustomAbiTypeMapping( + typeof(<#=type.Public#>), + typeof(<#=type.Abi#>), + "<#=type.Name#>", + isRuntimeClass: <#=type.IsRuntimeClass.ToString().ToLowerInvariant()#>); + } +<# + } + else + { +#> + RegisterCustomAbiTypeMapping( + typeof(<#=type.Public#>), + typeof(<#=type.Abi#>), + "<#=type.Name#>", + isRuntimeClass: <#=type.IsRuntimeClass.ToString().ToLowerInvariant()#>); +<# + } +#> + } +<# +} + +// 'RegisterCustomTypeToHelperTypeMapping' methods +foreach (var type in registerCustomTypeToHelperTypeMapping) +{ + string xmlName = type.Public; + + if (type.Public.EndsWith("<>")) xmlName = $"{xmlName.Substring(0, xmlName.IndexOf('<'))}{{T}}"; + if (type.Public.EndsWith("<,>")) xmlName = $"{xmlName.Substring(0, xmlName.IndexOf('<'))}{{K, V}}"; + + WriteLine(""); +#> + /// Registers the custom ABI type mapping for the type. + public static void <#=GetMethodName(type.Public, null)#>() + { +<# + WriteGuardStatements(type.Public); +#> + + RegisterCustomTypeToHelperTypeMapping( + typeof(<#=type.Public#>), + typeof(<#=type.Helper#>)); + } +<# +} +#> + } +} + +#endif \ No newline at end of file diff --git a/src/WinRT.Runtime/Projections.cs b/src/WinRT.Runtime/Projections.cs index 7c7fef288..883ac9656 100644 --- a/src/WinRT.Runtime/Projections.cs +++ b/src/WinRT.Runtime/Projections.cs @@ -9,20 +9,27 @@ using System.ComponentModel; using System.Diagnostics.CodeAnalysis; using System.Linq; +using System.Linq.Expressions; using System.Numerics; using System.Reflection; +using System.Runtime.CompilerServices; using System.Threading; using System.Windows.Input; using Windows.Foundation.Collections; namespace WinRT { + // Marker type to indicate the helper type cannot be constructed on AOT. + internal sealed class HelperTypeMetadataNotAvailableOnAot + { + } + #if EMBED internal #else public #endif - static class Projections + static partial class Projections { private static readonly ReaderWriterLockSlim rwlock = new ReaderWriterLockSlim(); @@ -35,39 +42,80 @@ static class Projections static Projections() { - // This should be in sync with cswinrt/helpers.h and the reverse mapping from WinRT.SourceGenerator/WinRTTypeWriter.cs. + // We always register mappings for 'bool' and 'char' as they're primitive types. + // They're also very cheap anyway and commonly used, so this keeps things simpler. + // This should be in sync with cswinrt/helpers.h and the reverse mapping from WinRT.SourceGenerator/TypeMapper.cs. RegisterCustomAbiTypeMappingNoLock(typeof(bool), typeof(ABI.System.Boolean), "Boolean"); RegisterCustomAbiTypeMappingNoLock(typeof(char), typeof(ABI.System.Char), "Char"); + + // Also always register Type, since it's "free" (no associated ABI type to root) + // given Type is special-cased in all relevant areas including Marshaler. + CustomTypeToAbiTypeNameMappings.Add(typeof(Type), "Windows.UI.Xaml.Interop.TypeName"); + CustomAbiTypeNameToTypeMappings.Add("Windows.UI.Xaml.Interop.TypeName", typeof(Type)); + +#if NET + // If default mappings are disabled, we avoid rooting everything by default. + // Developers will have to optionally opt-in into individual mappings later. + // Only do this on modern .NET, because trimming isn't supported downlevel + // anyway. This also makes it simpler to expose all 'Register' methods. + if (!FeatureSwitches.EnableDefaultCustomTypeMappings) + { + return; + } +#endif + +#if !NET + // Sanity check: always throw downlevel if System XAML projections are used, as this scenario is not supported. + // This also allows other code in WinRT.Runtime to simplify downlevel paths by just assuming this configuration. + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + throw new NotSupportedException("Using System XAML projections is only supported on modern .NET (make sure the 'CsWinRTUseWindowsUIXamlProjections' property is not set to 'true')."); + } +#endif + + // This should be in sync with cswinrt/helpers.h and the reverse mapping from WinRT.SourceGenerator/WinRTTypeWriter.cs. RegisterCustomAbiTypeMappingNoLock(typeof(EventRegistrationToken), typeof(ABI.WinRT.EventRegistrationToken), "Windows.Foundation.EventRegistrationToken"); RegisterCustomAbiTypeMappingNoLock(typeof(Nullable<>), typeof(ABI.System.Nullable<>), "Windows.Foundation.IReference`1"); - RegisterCustomAbiTypeMappingNoLock(typeof(Nullable), typeof(ABI.System.Nullable_int), "Windows.Foundation.IReference`1"); - RegisterCustomAbiTypeMappingNoLock(typeof(Nullable), typeof(ABI.System.Nullable_byte), "Windows.Foundation.IReference`1"); - RegisterCustomAbiTypeMappingNoLock(typeof(Nullable), typeof(ABI.System.Nullable_sbyte), "Windows.Foundation.IReference`1"); - RegisterCustomAbiTypeMappingNoLock(typeof(Nullable), typeof(ABI.System.Nullable_short), "Windows.Foundation.IReference`1"); - RegisterCustomAbiTypeMappingNoLock(typeof(Nullable), typeof(ABI.System.Nullable_ushort), "Windows.Foundation.IReference`1"); - RegisterCustomAbiTypeMappingNoLock(typeof(Nullable), typeof(ABI.System.Nullable_uint), "Windows.Foundation.IReference`1"); - RegisterCustomAbiTypeMappingNoLock(typeof(Nullable), typeof(ABI.System.Nullable_long), "Windows.Foundation.IReference`1"); - RegisterCustomAbiTypeMappingNoLock(typeof(Nullable), typeof(ABI.System.Nullable_ulong), "Windows.Foundation.IReference`1"); - RegisterCustomAbiTypeMappingNoLock(typeof(Nullable), typeof(ABI.System.Nullable_float), "Windows.Foundation.IReference`1"); - RegisterCustomAbiTypeMappingNoLock(typeof(Nullable), typeof(ABI.System.Nullable_double), "Windows.Foundation.IReference`1"); - RegisterCustomAbiTypeMappingNoLock(typeof(Nullable), typeof(ABI.System.Nullable_char), "Windows.Foundation.IReference`1"); - RegisterCustomAbiTypeMappingNoLock(typeof(Nullable), typeof(ABI.System.Nullable_bool), "Windows.Foundation.IReference`1"); - RegisterCustomAbiTypeMappingNoLock(typeof(Nullable), typeof(ABI.System.Nullable_guid), "Windows.Foundation.IReference`1"); - RegisterCustomAbiTypeMappingNoLock(typeof(Nullable), typeof(ABI.System.Nullable_DateTimeOffset), "Windows.Foundation.IReference`1"); - RegisterCustomAbiTypeMappingNoLock(typeof(Nullable), typeof(ABI.System.Nullable_TimeSpan), "Windows.Foundation.IReference`1"); + + // We only use IReferenceArray on AOT for runtime class name to projected type lookup, so avoiding the ABI type to not root that. + CustomAbiTypeNameToTypeMappings.Add("Windows.Foundation.IReferenceArray`1", typeof(Windows.Foundation.IReferenceArray<>)); RegisterCustomAbiTypeMappingNoLock(typeof(DateTimeOffset), typeof(ABI.System.DateTimeOffset), "Windows.Foundation.DateTime"); RegisterCustomAbiTypeMappingNoLock(typeof(Exception), typeof(ABI.System.Exception), "Windows.Foundation.HResult"); RegisterCustomAbiTypeMappingNoLock(typeof(TimeSpan), typeof(ABI.System.TimeSpan), "Windows.Foundation.TimeSpan"); RegisterCustomAbiTypeMappingNoLock(typeof(Uri), typeof(ABI.System.Uri), "Windows.Foundation.Uri", isRuntimeClass: true); - RegisterCustomAbiTypeMappingNoLock(typeof(DataErrorsChangedEventArgs), typeof(ABI.System.ComponentModel.DataErrorsChangedEventArgs), "Microsoft.UI.Xaml.Data.DataErrorsChangedEventArgs", isRuntimeClass: true); - RegisterCustomAbiTypeMappingNoLock(typeof(PropertyChangedEventArgs), typeof(ABI.System.ComponentModel.PropertyChangedEventArgs), "Microsoft.UI.Xaml.Data.PropertyChangedEventArgs", isRuntimeClass: true); - RegisterCustomAbiTypeMappingNoLock(typeof(PropertyChangedEventHandler), typeof(ABI.System.ComponentModel.PropertyChangedEventHandler), "Microsoft.UI.Xaml.Data.PropertyChangedEventHandler"); - RegisterCustomAbiTypeMappingNoLock(typeof(INotifyDataErrorInfo), typeof(ABI.System.ComponentModel.INotifyDataErrorInfo), "Microsoft.UI.Xaml.Data.INotifyDataErrorInfo"); - RegisterCustomAbiTypeMappingNoLock(typeof(INotifyPropertyChanged), typeof(ABI.System.ComponentModel.INotifyPropertyChanged), "Microsoft.UI.Xaml.Data.INotifyPropertyChanged"); - RegisterCustomAbiTypeMappingNoLock(typeof(ICommand), typeof(ABI.System.Windows.Input.ICommand), "Microsoft.UI.Xaml.Interop.ICommand"); - RegisterCustomAbiTypeMappingNoLock(typeof(IServiceProvider), typeof(ABI.System.IServiceProvider), "Microsoft.UI.Xaml.IXamlServiceProvider"); + + if (!FeatureSwitches.UseWindowsUIXamlProjections) + { + RegisterCustomAbiTypeMappingNoLock(typeof(DataErrorsChangedEventArgs), typeof(ABI.System.ComponentModel.DataErrorsChangedEventArgs), "Microsoft.UI.Xaml.Data.DataErrorsChangedEventArgs", isRuntimeClass: true); + RegisterCustomAbiTypeMappingNoLock(typeof(PropertyChangedEventArgs), typeof(ABI.System.ComponentModel.PropertyChangedEventArgs), "Microsoft.UI.Xaml.Data.PropertyChangedEventArgs", isRuntimeClass: true); + RegisterCustomAbiTypeMappingNoLock(typeof(PropertyChangedEventHandler), typeof(ABI.System.ComponentModel.PropertyChangedEventHandler), "Microsoft.UI.Xaml.Data.PropertyChangedEventHandler"); + RegisterCustomAbiTypeMappingNoLock(typeof(INotifyDataErrorInfo), typeof(ABI.System.ComponentModel.INotifyDataErrorInfo), "Microsoft.UI.Xaml.Data.INotifyDataErrorInfo"); + RegisterCustomAbiTypeMappingNoLock(typeof(INotifyPropertyChanged), typeof(ABI.System.ComponentModel.INotifyPropertyChanged), "Microsoft.UI.Xaml.Data.INotifyPropertyChanged"); + RegisterCustomAbiTypeMappingNoLock(typeof(ICommand), typeof(ABI.System.Windows.Input.ICommand), "Microsoft.UI.Xaml.Input.ICommand"); + RegisterCustomAbiTypeMappingNoLock(typeof(IServiceProvider), typeof(ABI.System.IServiceProvider), "Microsoft.UI.Xaml.IXamlServiceProvider"); + RegisterCustomAbiTypeMappingNoLock(typeof(IEnumerable), typeof(ABI.System.Collections.IEnumerable), "Microsoft.UI.Xaml.Interop.IBindableIterable"); + RegisterCustomAbiTypeMappingNoLock(typeof(IList), typeof(ABI.System.Collections.IList), "Microsoft.UI.Xaml.Interop.IBindableVector"); + RegisterCustomAbiTypeMappingNoLock(typeof(INotifyCollectionChanged), typeof(ABI.System.Collections.Specialized.INotifyCollectionChanged), "Microsoft.UI.Xaml.Interop.INotifyCollectionChanged"); + RegisterCustomAbiTypeMappingNoLock(typeof(NotifyCollectionChangedAction), typeof(ABI.System.Collections.Specialized.NotifyCollectionChangedAction), "Microsoft.UI.Xaml.Interop.NotifyCollectionChangedAction"); + RegisterCustomAbiTypeMappingNoLock(typeof(NotifyCollectionChangedEventArgs), typeof(ABI.System.Collections.Specialized.NotifyCollectionChangedEventArgs), "Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventArgs", isRuntimeClass: true); + RegisterCustomAbiTypeMappingNoLock(typeof(NotifyCollectionChangedEventHandler), typeof(ABI.System.Collections.Specialized.NotifyCollectionChangedEventHandler), "Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventHandler"); + } + else + { + RegisterCustomAbiTypeMappingNoLock(typeof(PropertyChangedEventArgs), typeof(ABI.System.ComponentModel.PropertyChangedEventArgs), "Windows.UI.Xaml.Data.PropertyChangedEventArgs", isRuntimeClass: true); + RegisterCustomAbiTypeMappingNoLock(typeof(PropertyChangedEventHandler), typeof(ABI.System.ComponentModel.PropertyChangedEventHandler), "Windows.UI.Xaml.Data.PropertyChangedEventHandler"); + RegisterCustomAbiTypeMappingNoLock(typeof(INotifyPropertyChanged), typeof(ABI.System.ComponentModel.INotifyPropertyChanged), "Windows.UI.Xaml.Data.INotifyPropertyChanged"); + RegisterCustomAbiTypeMappingNoLock(typeof(ICommand), typeof(ABI.System.Windows.Input.ICommand), "Windows.UI.Xaml.Interop.ICommand"); + RegisterCustomAbiTypeMappingNoLock(typeof(IEnumerable), typeof(ABI.System.Collections.IEnumerable), "Windows.UI.Xaml.Interop.IBindableIterable"); + RegisterCustomAbiTypeMappingNoLock(typeof(IList), typeof(ABI.System.Collections.IList), "Windows.UI.Xaml.Interop.IBindableVector"); + RegisterCustomAbiTypeMappingNoLock(typeof(INotifyCollectionChanged), typeof(ABI.System.Collections.Specialized.INotifyCollectionChanged), "Windows.UI.Xaml.Interop.INotifyCollectionChanged"); + RegisterCustomAbiTypeMappingNoLock(typeof(NotifyCollectionChangedAction), typeof(ABI.System.Collections.Specialized.NotifyCollectionChangedAction), "Windows.UI.Xaml.Interop.NotifyCollectionChangedAction"); + RegisterCustomAbiTypeMappingNoLock(typeof(NotifyCollectionChangedEventArgs), typeof(ABI.System.Collections.Specialized.NotifyCollectionChangedEventArgs), "Windows.UI.Xaml.Interop.NotifyCollectionChangedEventArgs", isRuntimeClass: true); + RegisterCustomAbiTypeMappingNoLock(typeof(NotifyCollectionChangedEventHandler), typeof(ABI.System.Collections.Specialized.NotifyCollectionChangedEventHandler), "Windows.UI.Xaml.Interop.NotifyCollectionChangedEventHandler"); + } + RegisterCustomAbiTypeMappingNoLock(typeof(EventHandler<>), typeof(ABI.System.EventHandler<>), "Windows.Foundation.EventHandler`1"); RegisterCustomAbiTypeMappingNoLock(typeof(KeyValuePair<,>), typeof(ABI.System.Collections.Generic.KeyValuePair<,>), "Windows.Foundation.Collections.IKeyValuePair`2"); @@ -79,12 +127,6 @@ static Projections() RegisterCustomAbiTypeMappingNoLock(typeof(IReadOnlyDictionary<,>), typeof(ABI.System.Collections.Generic.IReadOnlyDictionary<,>), "Windows.Foundation.Collections.IMapView`2"); RegisterCustomAbiTypeMappingNoLock(typeof(IDisposable), typeof(ABI.System.IDisposable), "Windows.Foundation.IClosable"); - RegisterCustomAbiTypeMappingNoLock(typeof(IEnumerable), typeof(ABI.System.Collections.IEnumerable), "Microsoft.UI.Xaml.Interop.IBindableIterable"); - RegisterCustomAbiTypeMappingNoLock(typeof(IList), typeof(ABI.System.Collections.IList), "Microsoft.UI.Xaml.Interop.IBindableVector"); - RegisterCustomAbiTypeMappingNoLock(typeof(INotifyCollectionChanged), typeof(ABI.System.Collections.Specialized.INotifyCollectionChanged), "Microsoft.UI.Xaml.Interop.INotifyCollectionChanged"); - RegisterCustomAbiTypeMappingNoLock(typeof(NotifyCollectionChangedAction), typeof(ABI.System.Collections.Specialized.NotifyCollectionChangedAction), "Microsoft.UI.Xaml.Interop.NotifyCollectionChangedAction"); - RegisterCustomAbiTypeMappingNoLock(typeof(NotifyCollectionChangedEventArgs), typeof(ABI.System.Collections.Specialized.NotifyCollectionChangedEventArgs), "Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventArgs", isRuntimeClass: true); - RegisterCustomAbiTypeMappingNoLock(typeof(NotifyCollectionChangedEventHandler), typeof(ABI.System.Collections.Specialized.NotifyCollectionChangedEventHandler), "Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventHandler"); RegisterCustomAbiTypeMappingNoLock(typeof(Matrix3x2), typeof(ABI.System.Numerics.Matrix3x2), "Windows.Foundation.Numerics.Matrix3x2"); RegisterCustomAbiTypeMappingNoLock(typeof(Matrix4x4), typeof(ABI.System.Numerics.Matrix4x4), "Windows.Foundation.Numerics.Matrix4x4"); @@ -94,29 +136,28 @@ static Projections() RegisterCustomAbiTypeMappingNoLock(typeof(Vector3), typeof(ABI.System.Numerics.Vector3), "Windows.Foundation.Numerics.Vector3"); RegisterCustomAbiTypeMappingNoLock(typeof(Vector4), typeof(ABI.System.Numerics.Vector4), "Windows.Foundation.Numerics.Vector4"); + RegisterCustomAbiTypeMappingNoLock(typeof(EventHandler), typeof(ABI.System.EventHandler)); + // TODO: Ideally we should not need these - CustomTypeToHelperTypeMappings.Add(typeof(IMap<,>), typeof(ABI.System.Collections.Generic.IDictionary<,>)); - CustomTypeToHelperTypeMappings.Add(typeof(IVector<>), typeof(ABI.System.Collections.Generic.IList<>)); - CustomTypeToHelperTypeMappings.Add(typeof(IMapView<,>), typeof(ABI.System.Collections.Generic.IReadOnlyDictionary<,>)); - CustomTypeToHelperTypeMappings.Add(typeof(IVectorView<>), typeof(ABI.System.Collections.Generic.IReadOnlyList<>)); - CustomTypeToHelperTypeMappings.Add(typeof(Microsoft.UI.Xaml.Interop.IBindableVector), typeof(ABI.System.Collections.IList)); + RegisterCustomTypeToHelperTypeMappingNoLock(typeof(IMap<,>), typeof(ABI.System.Collections.Generic.IDictionary<,>)); + RegisterCustomTypeToHelperTypeMappingNoLock(typeof(IVector<>), typeof(ABI.System.Collections.Generic.IList<>)); + RegisterCustomTypeToHelperTypeMappingNoLock(typeof(IMapView<,>), typeof(ABI.System.Collections.Generic.IReadOnlyDictionary<,>)); + RegisterCustomTypeToHelperTypeMappingNoLock(typeof(IVectorView<>), typeof(ABI.System.Collections.Generic.IReadOnlyList<>)); + // The IBindable* types have the same IID on both MUX and WUX, so we can use the same type in both scenarios. + RegisterCustomTypeToHelperTypeMappingNoLock(typeof(Microsoft.UI.Xaml.Interop.IBindableVector), typeof(ABI.System.Collections.IList)); #if NET - CustomTypeToHelperTypeMappings.Add(typeof(ICollection<>), typeof(ABI.System.Collections.Generic.ICollection<>)); - CustomTypeToHelperTypeMappings.Add(typeof(IReadOnlyCollection<>), typeof(ABI.System.Collections.Generic.IReadOnlyCollection<>)); + RegisterCustomTypeToHelperTypeMappingNoLock(typeof(ICollection<>), typeof(ABI.System.Collections.Generic.ICollection<>)); + RegisterCustomTypeToHelperTypeMappingNoLock(typeof(IReadOnlyCollection<>), typeof(ABI.System.Collections.Generic.IReadOnlyCollection<>)); + RegisterCustomTypeToHelperTypeMappingNoLock(typeof(ICollection), typeof(ABI.System.Collections.ICollection)); #endif - RegisterCustomAbiTypeMappingNoLock(typeof(EventHandler), typeof(ABI.System.EventHandler)); - - CustomTypeToAbiTypeNameMappings.Add(typeof(System.Type), "Windows.UI.Xaml.Interop.TypeName"); } - public static void RegisterCustomAbiTypeMapping( + private static void RegisterCustomAbiTypeMapping( Type publicType, #if NET [DynamicallyAccessedMembers( DynamicallyAccessedMemberTypes.PublicMethods | - DynamicallyAccessedMemberTypes.NonPublicMethods | - DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicFields)] #endif Type abiType, @@ -134,13 +175,43 @@ public static void RegisterCustomAbiTypeMapping( } } + private static void RegisterCustomTypeToHelperTypeMapping( + Type publicType, +#if NET + [DynamicallyAccessedMembers( + DynamicallyAccessedMemberTypes.PublicMethods | + DynamicallyAccessedMemberTypes.PublicFields)] +#endif + Type helperType) + { + rwlock.EnterWriteLock(); + try + { + CustomTypeToHelperTypeMappings.Add(publicType, helperType); + } + finally + { + rwlock.ExitWriteLock(); + } + } + + private static void RegisterCustomTypeToHelperTypeMappingNoLock( + Type publicType, +#if NET + [DynamicallyAccessedMembers( + DynamicallyAccessedMemberTypes.PublicMethods | + DynamicallyAccessedMemberTypes.PublicFields)] +#endif + Type helperType) + { + CustomTypeToHelperTypeMappings.Add(publicType, helperType); + } + private static void RegisterCustomAbiTypeMappingNoLock( Type publicType, #if NET [DynamicallyAccessedMembers( DynamicallyAccessedMemberTypes.PublicMethods | - DynamicallyAccessedMemberTypes.NonPublicMethods | - DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicFields)] #endif Type abiType, @@ -158,13 +229,32 @@ private static void RegisterCustomAbiTypeMappingNoLock( } } + private static void RegisterCustomAbiTypeMapping( + Type publicType, +#if NET + [DynamicallyAccessedMembers( + DynamicallyAccessedMemberTypes.PublicMethods | + DynamicallyAccessedMemberTypes.PublicFields)] +#endif + Type abiType) + { + rwlock.EnterWriteLock(); + try + { + RegisterCustomAbiTypeMappingNoLock(publicType, abiType); + } + finally + { + rwlock.ExitWriteLock(); + } + } + private static void RegisterCustomAbiTypeMappingNoLock( Type publicType, #if NET [DynamicallyAccessedMembers( DynamicallyAccessedMemberTypes.PublicMethods | - DynamicallyAccessedMemberTypes.NonPublicMethods | - DynamicallyAccessedMemberTypes.PublicNestedTypes)] + DynamicallyAccessedMemberTypes.PublicFields)] #endif Type abiType) { @@ -173,13 +263,25 @@ private static void RegisterCustomAbiTypeMappingNoLock( } #if NET + [UnconditionalSuppressMessage("Trimming", "IL2055", Justification = "The type arguments are guaranteed to be valid for the generic ABI types.")] + [UnconditionalSuppressMessage("Trimming", "IL2068", Justification = "All types added to 'CustomTypeToHelperTypeMappings' have metadata explicitly preserved.")] [return: DynamicallyAccessedMembers( DynamicallyAccessedMemberTypes.PublicMethods | - DynamicallyAccessedMemberTypes.NonPublicMethods | - DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicFields)] #endif public static Type FindCustomHelperTypeMapping(Type publicType, bool filterToRuntimeClass = false) + { + return FindCustomHelperTypeMapping(publicType, filterToRuntimeClass, false); + } + +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2055", Justification = "The type arguments are guaranteed to be valid for the generic ABI types.")] + [UnconditionalSuppressMessage("Trimming", "IL2068", Justification = "All types added to 'CustomTypeToHelperTypeMappings' have metadata explicitly preserved.")] + [return: DynamicallyAccessedMembers( + DynamicallyAccessedMemberTypes.PublicMethods | + DynamicallyAccessedMemberTypes.PublicFields)] +#endif + internal static Type FindCustomHelperTypeMapping(Type publicType, bool filterToRuntimeClass, bool returnMarkerTypeIfNotAotCompatible) { rwlock.EnterReadLock(); try @@ -189,16 +291,28 @@ public static Type FindCustomHelperTypeMapping(Type publicType, bool filterToRun return null; } - if (publicType.IsGenericType) + if (publicType.IsGenericType && !publicType.IsGenericTypeDefinition) { if (CustomTypeToHelperTypeMappings.TryGetValue(publicType, out Type specializedAbiType)) { return specializedAbiType; } - return CustomTypeToHelperTypeMappings.TryGetValue(publicType.GetGenericTypeDefinition(), out Type abiTypeDefinition) - ? abiTypeDefinition.MakeGenericType(publicType.GetGenericArguments()) - : null; + if (CustomTypeToHelperTypeMappings.TryGetValue(publicType.GetGenericTypeDefinition(), out Type abiTypeDefinition)) + { +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return returnMarkerTypeIfNotAotCompatible ? typeof(HelperTypeMetadataNotAvailableOnAot) : throw new NotSupportedException($"Cannot retrieve a helper type for public type '{publicType}'."); + } +#endif + +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + return abiTypeDefinition.MakeGenericType(publicType.GetGenericArguments()); +#pragma warning restore IL3050 + } + + return null; } return CustomTypeToHelperTypeMappings.TryGetValue(publicType, out Type abiType) ? abiType : null; } @@ -208,6 +322,9 @@ public static Type FindCustomHelperTypeMapping(Type publicType, bool filterToRun } } +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2055", Justification = "The type arguments are guaranteed to be valid for the generic ABI types.")] +#endif public static Type FindCustomPublicTypeForAbiType(Type abiType) { rwlock.EnterReadLock(); @@ -220,9 +337,21 @@ public static Type FindCustomPublicTypeForAbiType(Type abiType) return specializedPublicType; } - return CustomAbiTypeToTypeMappings.TryGetValue(abiType.GetGenericTypeDefinition(), out Type publicTypeDefinition) - ? publicTypeDefinition.MakeGenericType(abiType.GetGenericArguments()) - : null; + if (CustomAbiTypeToTypeMappings.TryGetValue(abiType.GetGenericTypeDefinition(), out Type publicTypeDefinition)) + { +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"Cannot retrieve a public type for ABI type '{abiType}'."); + } +#endif + +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + return publicTypeDefinition.MakeGenericType(abiType.GetGenericArguments()); +#pragma warning restore IL3050 + } + + return null; } return CustomAbiTypeToTypeMappings.TryGetValue(abiType, out Type publicType) ? publicType : null; } @@ -258,7 +387,7 @@ public static string FindCustomAbiTypeNameForType(Type type) } } - private readonly static ConcurrentDictionary IsTypeWindowsRuntimeTypeCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary IsTypeWindowsRuntimeTypeCache = new(); public static bool IsTypeWindowsRuntimeType(Type type) { return IsTypeWindowsRuntimeTypeCache.GetOrAdd(type, (type) => @@ -274,7 +403,6 @@ public static bool IsTypeWindowsRuntimeType(Type type) private static bool IsTypeWindowsRuntimeTypeNoArray(Type type) { - type = type.GetAuthoringMetadataType() ?? type; if (type.IsConstructedGenericType) { if(IsTypeWindowsRuntimeTypeNoArray(type.GetGenericTypeDefinition())) @@ -295,10 +423,14 @@ private static bool IsTypeWindowsRuntimeTypeNoArray(Type type) || type == typeof(string) || type == typeof(Guid) || type == typeof(object) - || type.IsDefined(typeof(WindowsRuntimeTypeAttribute)); + || type.IsDefined(typeof(WindowsRuntimeTypeAttribute)) + || type.GetAuthoringMetadataType() != null; } // Use TryGetCompatibleWindowsRuntimeTypesForVariantType instead. +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2055", Justification = "Calls to 'MakeGenericType' are always done with compatible types.")] +#endif public static bool TryGetCompatibleWindowsRuntimeTypeForVariantType(Type type, out Type compatibleType) { compatibleType = null; @@ -314,6 +446,13 @@ public static bool TryGetCompatibleWindowsRuntimeTypeForVariantType(Type type, o return false; } +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"Cannot retrieve a compatible WinRT type for variant type '{type}'."); + } +#endif + var genericConstraints = definition.GetGenericArguments(); var genericArguments = type.GetGenericArguments(); var newArguments = new Type[genericArguments.Length]; @@ -336,11 +475,19 @@ public static bool TryGetCompatibleWindowsRuntimeTypeForVariantType(Type type, o newArguments[i] = genericArguments[i]; } } +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 compatibleType = definition.MakeGenericType(newArguments); +#pragma warning restore IL3050 return true; } - private static HashSet GetCompatibleTypes(Type type) +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif +#if NET + [RequiresUnreferencedCode(AttributeMessages.GenericRequiresUnreferencedCodeMessage)] +#endif + private static HashSet GetCompatibleTypes(Type type, Stack typeStack) { HashSet compatibleTypes = new HashSet(); @@ -352,13 +499,13 @@ private static HashSet GetCompatibleTypes(Type type) } if (iface.IsConstructedGenericType - && TryGetCompatibleWindowsRuntimeTypesForVariantType(iface, out var compatibleIfaces)) + && TryGetCompatibleWindowsRuntimeTypesForVariantType(iface, typeStack, out var compatibleIfaces)) { compatibleTypes.UnionWith(compatibleIfaces); } } - Type baseType = type.BaseType; + Type baseType = type.IsInterface ? typeof(object) : type.BaseType; while (baseType != null) { if (IsTypeWindowsRuntimeTypeNoArray(baseType)) @@ -371,6 +518,9 @@ private static HashSet GetCompatibleTypes(Type type) return compatibleTypes; } +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif internal static IEnumerable GetAllPossibleTypeCombinations(IEnumerable> compatibleTypesPerGeneric, Type definition) { // Implementation adapted from https://stackoverflow.com/a/4424005 @@ -386,9 +536,12 @@ internal static IEnumerable GetAllPossibleTypeCombinations(IEnumerable accum, Stack stack, IEnumerable[] compatibleTypes, int index) { @@ -411,7 +564,13 @@ void GetAllPossibleTypeCombinationsCore(List accum, Stack stack, IEn } } - internal static bool TryGetCompatibleWindowsRuntimeTypesForVariantType(Type type, out IEnumerable compatibleTypes) +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif +#if NET + [RequiresUnreferencedCode(AttributeMessages.GenericRequiresUnreferencedCodeMessage)] +#endif + internal static bool TryGetCompatibleWindowsRuntimeTypesForVariantType(Type type, Stack typeStack, out IEnumerable compatibleTypes) { compatibleTypes = null; if (!type.IsConstructedGenericType) @@ -426,6 +585,19 @@ internal static bool TryGetCompatibleWindowsRuntimeTypesForVariantType(Type type return false; } + if (typeStack == null) + { + typeStack = new Stack(); + } + else + { + if (typeStack.Contains(type)) + { + return false; + } + } + typeStack.Push(type); + var genericConstraints = definition.GetGenericArguments(); var genericArguments = type.GetGenericArguments(); List> compatibleTypesPerGeneric = new List>(); @@ -441,41 +613,64 @@ internal static bool TryGetCompatibleWindowsRuntimeTypesForVariantType(Type type } else if (!argumentCovariantObject) { + typeStack.Pop(); return false; } if (argumentCovariantObject) { - compatibleTypesForGeneric.AddRange(GetCompatibleTypes(genericArguments[i])); + compatibleTypesForGeneric.AddRange(GetCompatibleTypes(genericArguments[i], typeStack)); } compatibleTypesPerGeneric.Add(compatibleTypesForGeneric); } + typeStack.Pop(); compatibleTypes = GetAllPossibleTypeCombinations(compatibleTypesPerGeneric, definition); return true; } - private readonly static ConcurrentDictionary DefaultInterfaceTypeCache = new ConcurrentDictionary(); + private static readonly ConcurrentDictionary DefaultInterfaceTypeCache = new(); + +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2070", + Justification = + "The path using reflection to retrieve the default interface property is only used with legacy projections. " + + "Applications which make use of trimming will make use of updated projections and won't hit that code path.")] +#endif internal static bool TryGetDefaultInterfaceTypeForRuntimeClassType(Type runtimeClass, out Type defaultInterface) { defaultInterface = DefaultInterfaceTypeCache.GetOrAdd(runtimeClass, (runtimeClass) => { runtimeClass = runtimeClass.GetRuntimeClassCCWType() ?? runtimeClass; ProjectedRuntimeClassAttribute attr = runtimeClass.GetCustomAttribute(); + if (attr is null) { return null; } - if (attr.DefaultInterfaceProperty != null) +#if NET + // Using AOT requires using updated projections, which means we expect the type constructor to be used. + // The one taking a string for the property is not trim safe and is not used anymore by projections. + if (!RuntimeFeature.IsDynamicCodeCompiled) { - return runtimeClass.GetProperty(attr.DefaultInterfaceProperty, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly).PropertyType; + return attr.DefaultInterface; } - else +#endif + + if (attr.DefaultInterface != null) { return attr.DefaultInterface; + } + + // This path is only ever taken for .NET Standard legacy projections + if (attr.DefaultInterfaceProperty != null) + { + return runtimeClass.GetProperty(attr.DefaultInterfaceProperty, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly).PropertyType; } + + return null; }); return defaultInterface != null; } @@ -489,41 +684,83 @@ internal static Type GetDefaultInterfaceTypeForRuntimeClassType(Type runtimeClas return defaultInterface; } - internal static bool TryGetMarshalerTypeForProjectedRuntimeClass(IObjectReference objectReference, out Type type) +#if NET +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + internal static Type GetAbiDelegateType(params Type[] typeArgs) => Expression.GetDelegateType(typeArgs); +#else + private class DelegateTypeComparer : IEqualityComparer { - Type projectedType = typeof(T); - if (projectedType == typeof(object)) + public bool Equals(Type[] x, Type[] y) { - if (objectReference.TryAs(InterfaceIIDs.IInspectable_IID, out var inspectablePtr) == 0) + return x.SequenceEqual(y); + } + + public int GetHashCode(Type[] obj) + { + int hashCode = 0; + for (int idx = 0; idx < obj.Length; idx++) { - rwlock.EnterReadLock(); - try - { - IInspectable inspectable = inspectablePtr; - string runtimeClassName = inspectable.GetRuntimeClassName(true); - if (runtimeClassName is object) - { - if (ProjectedRuntimeClassNames.Contains(runtimeClassName)) - { - type = CustomTypeToHelperTypeMappings[CustomAbiTypeNameToTypeMappings[runtimeClassName]]; - return true; - } - } - } - finally - { - inspectablePtr.Dispose(); - rwlock.ExitReadLock(); - } + hashCode ^= obj[idx].GetHashCode(); } + return hashCode; } - else + } + + private static readonly ConcurrentDictionary abiDelegateCache = new(new DelegateTypeComparer()) + { + // IEnumerable + [new Type[] { typeof(void*), typeof(IntPtr).MakeByRefType(), typeof(int) }] = typeof(Interop._get_Current_IntPtr), + [new Type[] { typeof(void*), typeof(ABI.System.Type).MakeByRefType(), typeof(int) }] = typeof(Interop._get_Current_Type), + // IList / IReadOnlyList + [new Type[] { typeof(void*), typeof(uint), typeof(IntPtr).MakeByRefType(), typeof(int) }] = typeof(Interop._get_At_IntPtr), + [new Type[] { typeof(void*), typeof(uint), typeof(ABI.System.Type).MakeByRefType(), typeof(int) }] = typeof(Interop._get_At_Type), + [new Type[] { typeof(void*), typeof(IntPtr), typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }] = typeof(Interop._index_Of_IntPtr), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }] = typeof(Interop._index_Of_Type), + [new Type[] { typeof(void*), typeof(uint), typeof(IntPtr), typeof(int) }] = typeof(Interop._set_At_IntPtr), + [new Type[] { typeof(void*), typeof(uint), typeof(ABI.System.Type), typeof(int) }] = typeof(Interop._set_At_Type), + [new Type[] { typeof(void*), typeof(IntPtr), typeof(int) }] = typeof(Interop._append_IntPtr), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(int) }] = typeof(Interop._append_Type), + // IDictionary / IReadOnlyDictionary + [new Type[] { typeof(void*), typeof(IntPtr), typeof(IntPtr).MakeByRefType(), typeof(int) }] = typeof(Interop._lookup_IntPtr_IntPtr), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(ABI.System.Type).MakeByRefType(), typeof(int) }] = typeof(Interop._lookup_Type_Type), + [new Type[] { typeof(void*), typeof(IntPtr), typeof(ABI.System.Type).MakeByRefType(), typeof(int) }] = typeof(Interop._lookup_IntPtr_Type), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(IntPtr).MakeByRefType(), typeof(int) }] = typeof(Interop._lookup_Type_IntPtr), + [new Type[] { typeof(void*), typeof(IntPtr), typeof(byte).MakeByRefType(), typeof(int) }] = typeof(Interop._has_key_IntPtr), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(byte).MakeByRefType(), typeof(int) }] = typeof(Interop._has_key_Type), + [new Type[] { typeof(void*), typeof(IntPtr), typeof(IntPtr), typeof(byte).MakeByRefType(), typeof(int) }] = typeof(Interop._insert_IntPtr_IntPtr), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(ABI.System.Type), typeof(byte).MakeByRefType(), typeof(int) }] = typeof(Interop._insert_Type_Type), + [new Type[] { typeof(void*), typeof(IntPtr), typeof(ABI.System.Type), typeof(byte).MakeByRefType(), typeof(int) }] = typeof(Interop._insert_IntPtr_Type), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(IntPtr), typeof(byte).MakeByRefType(), typeof(int) }] = typeof(Interop._insert_Type_IntPtr), + // EventHandler + [new Type[] { typeof(void*), typeof(IntPtr), typeof(IntPtr), typeof(int) }] = typeof(Interop._invoke_IntPtr_IntPtr), + [new Type[] { typeof(void*), typeof(IntPtr), typeof(ABI.System.Type), typeof(int) }] = typeof(Interop._invoke_IntPtr_Type), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(IntPtr), typeof(int) }] = typeof(Interop._invoke_Type_IntPtr), + [new Type[] { typeof(void*), typeof(ABI.System.Type), typeof(ABI.System.Type), typeof(int) }] = typeof(Interop._invoke_Type_Type), + // IKeyValuePair + [new Type[] { typeof(IntPtr), typeof(IntPtr*), typeof(int) }] = typeof(Interop._get_Key_IntPtr), + }; + + public static void RegisterAbiDelegate(Type[] delegateSignature, Type delegateType) + { + abiDelegateCache.TryAdd(delegateSignature, delegateType); + } + + // The .NET Standard projection can be used in both .NET Core and .NET Framework scenarios. + // With the latter, using Expression.GetDelegateType to create custom delegates with void* parameters + // doesn't seem to be supported. So we handle that by pregenerating all the ABI delegates that we need + // based on the WinMD and also by allowing apps to register their own if there are any + // that we couldn't detect (i.e. types passed as object in WinMD). + public static Type GetAbiDelegateType(params Type[] typeArgs) + { + if (abiDelegateCache.TryGetValue(typeArgs, out var delegateType)) { - type = FindCustomHelperTypeMapping(projectedType, true); - return type != null; + return delegateType; } - type = null; - return false; + + return Expression.GetDelegateType(typeArgs); } +#endif } } \ No newline at end of file diff --git a/src/WinRT.Runtime/Projections/Bindable.net5.cs b/src/WinRT.Runtime/Projections/Bindable.net5.cs index 6f4d943ac..6498d0042 100644 --- a/src/WinRT.Runtime/Projections/Bindable.net5.cs +++ b/src/WinRT.Runtime/Projections/Bindable.net5.cs @@ -4,14 +4,15 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq.Expressions; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using WinRT; - - +using WinRT.Interop; + + #pragma warning disable 0169 // warning CS0169: The field '...' is never used #pragma warning disable 0649 // warning CS0169: Field '...' is never assigned to - + namespace Microsoft.UI.Xaml.Interop { [global::WinRT.WindowsRuntimeType] @@ -74,124 +75,98 @@ internal unsafe interface IBindableIterable : global::Microsoft.UI.Xaml.Interop. [DynamicInterfaceCastableImplementation] [Guid("6A1D6C07-076D-49F2-8314-F52C9C9A8331")] internal unsafe interface IBindableIterator : global::Microsoft.UI.Xaml.Interop.IBindableIterator - { - [Guid("6A1D6C07-076D-49F2-8314-F52C9C9A8331")] - public struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _get_Current_0; - public delegate* unmanaged[Stdcall] get_Current_0 { get => (delegate* unmanaged[Stdcall])_get_Current_0; set => _get_Current_0 = value; } - private void* _get_HasCurrent_1; - public delegate* unmanaged[Stdcall] get_HasCurrent_1 { get => (delegate* unmanaged[Stdcall])_get_HasCurrent_1; set => _get_HasCurrent_1 = value; } - private void* _MoveNext_2; - public delegate* unmanaged[Stdcall] MoveNext_2 { get => (delegate* unmanaged[Stdcall])_MoveNext_2; set => _MoveNext_2 = value; } - // Note this may not be a valid address and should not be called. - private void* _GetMany_3; - public delegate* unmanaged[Stdcall] GetMany_3 { get => (delegate* unmanaged[Stdcall])_GetMany_3; set => _GetMany_3 = value; } - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - - _get_Current_0 = (delegate* unmanaged)&Do_Abi_get_Current_0, - _get_HasCurrent_1 = (delegate* unmanaged)&Do_Abi_get_HasCurrent_1, - _MoveNext_2 = (delegate* unmanaged)&Do_Abi_MoveNext_2, - _GetMany_3 = (delegate* unmanaged)&Do_Abi_GetMany_3 - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 4); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_MoveNext_2(IntPtr thisPtr, byte* result) - { - bool __result = default; - *result = default; - try - { - __result = global::WinRT.ComWrappersSupport.FindObject(thisPtr).MoveNext(); - *result = (byte)(__result ? 1 : 0); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetMany_3(IntPtr thisPtr, int __itemsSize, IntPtr items, uint* result) - { - *result = default; - - try - { - // Should never be called. - throw new NotImplementedException(); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_get_Current_0(IntPtr thisPtr, IntPtr* value) - { - object __value = default; - *value = default; - try - { - __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).Current; - *value = MarshalInspectable.FromManaged(__value); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_get_HasCurrent_1(IntPtr thisPtr, byte* value) - { - bool __value = default; - *value = default; - try - { - __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).HasCurrent; - *value = (byte)(__value ? 1 : 0); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } + { + public static readonly IntPtr AbiToProjectionVftablePtr; + static IBindableIterator() + { + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(IBindableIterator), sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * 4); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[6] = &Do_Abi_get_Current_0; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[7] = &Do_Abi_get_HasCurrent_1; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[8] = &Do_Abi_MoveNext_2; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[9] = &Do_Abi_GetMany_3; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_MoveNext_2(IntPtr thisPtr, byte* result) + { + bool __result = default; + *result = default; + try + { + __result = global::WinRT.ComWrappersSupport.FindObject(thisPtr).MoveNext(); + *result = (byte)(__result ? 1 : 0); + } + catch (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* result) + { + *result = default; + + try + { + // Should never be called. + throw new NotImplementedException(); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_get_Current_0(IntPtr thisPtr, IntPtr* value) + { + object __value = default; + *value = default; + try + { + __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).Current; + *value = MarshalInspectable.FromManaged(__value); + } + catch (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* value) + { + bool __value = default; + *value = default; + try + { + __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).HasCurrent; + *value = (byte)(__value ? 1 : 0); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); + + internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr, IID.IID_IUnknown); unsafe bool global::Microsoft.UI.Xaml.Interop.IBindableIterator.MoveNext() { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Microsoft.UI.Xaml.Interop.IBindableIterator).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Microsoft.UI.Xaml.Interop.IBindableIterator).TypeHandle); var ThisPtr = _obj.ThisPtr; - byte __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.MoveNext_2(ThisPtr, &__retval)); + byte __retval = default; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[8](ThisPtr, &__retval)); + GC.KeepAlive(_obj); return __retval != 0; } @@ -205,12 +180,13 @@ private static unsafe int Do_Abi_get_HasCurrent_1(IntPtr thisPtr, byte* value) { get { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Microsoft.UI.Xaml.Interop.IBindableIterator).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Microsoft.UI.Xaml.Interop.IBindableIterator).TypeHandle); var ThisPtr = _obj.ThisPtr; IntPtr __retval = default; try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Current_0(ThisPtr, &__retval)); + { + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6](ThisPtr, &__retval)); + GC.KeepAlive(_obj); return MarshalInspectable.FromAbi(__retval); } finally @@ -224,10 +200,11 @@ private static unsafe int Do_Abi_get_HasCurrent_1(IntPtr thisPtr, byte* value) { get { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Microsoft.UI.Xaml.Interop.IBindableIterator).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Microsoft.UI.Xaml.Interop.IBindableIterator).TypeHandle); var ThisPtr = _obj.ThisPtr; - byte __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_HasCurrent_1(ThisPtr, &__retval)); + byte __retval = default; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[7](ThisPtr, &__retval)); + GC.KeepAlive(_obj); return __retval != 0; } } @@ -245,118 +222,95 @@ internal static class IBindableIterator_Delegates [DynamicInterfaceCastableImplementation] [Guid("346DD6E7-976E-4BC3-815D-ECE243BC0F33")] internal unsafe interface IBindableVectorView : global::Microsoft.UI.Xaml.Interop.IBindableVectorView - { - [Guid("346DD6E7-976E-4BC3-815D-ECE243BC0F33")] - public struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _GetAt_0; - public delegate* unmanaged[Stdcall] GetAt_0 { get => (delegate* unmanaged[Stdcall])_GetAt_0; set => _GetAt_0 = value; } - private void* _get_Size_1; - public delegate* unmanaged[Stdcall] get_Size_1 { get => (delegate* unmanaged[Stdcall])_get_Size_1; set => _get_Size_1 = value; } - private void* _IndexOf_2; - public delegate* unmanaged[Stdcall] IndexOf_2 { get => (delegate* unmanaged[Stdcall])_IndexOf_2; set => _IndexOf_2 = value; } - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - - _GetAt_0 = (delegate* unmanaged)&Do_Abi_GetAt_0, - _get_Size_1 = (delegate* unmanaged)&Do_Abi_get_Size_1, - _IndexOf_2 = (delegate* unmanaged)&Do_Abi_IndexOf_2 - - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 3); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetAt_0(IntPtr thisPtr, uint index, IntPtr* result) - { - object __result = default; - - try - { - __result = global::WinRT.ComWrappersSupport.FindObject(thisPtr).GetAt(index); - *result = MarshalInspectable.FromManaged(__result); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_IndexOf_2(IntPtr thisPtr, IntPtr value, uint* index, byte* returnValue) - { - bool __returnValue = default; - - *index = default; - *returnValue = default; - uint __index = default; - - try - { - __returnValue = global::WinRT.ComWrappersSupport.FindObject(thisPtr).IndexOf(MarshalInspectable.FromAbi(value), out __index); - *index = __index; - *returnValue = (byte)(__returnValue ? 1 : 0); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_get_Size_1(IntPtr thisPtr, uint* value) - { - uint __value = default; - - *value = default; - - try - { - __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).Size; - *value = __value; - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } + { + public static readonly IntPtr AbiToProjectionVftablePtr; + + static IBindableVectorView() + { + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(IBindableVectorView), sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * 3); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[6] = &Do_Abi_GetAt_0; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[7] = &Do_Abi_get_Size_1; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[8] = &Do_Abi_IndexOf_2; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_GetAt_0(IntPtr thisPtr, uint index, IntPtr* result) + { + object __result = default; + + try + { + __result = global::WinRT.ComWrappersSupport.FindObject(thisPtr).GetAt(index); + *result = MarshalInspectable.FromManaged(__result); + + } + catch (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, IntPtr value, uint* index, byte* returnValue) + { + bool __returnValue = default; + + *index = default; + *returnValue = default; + uint __index = default; + + try + { + __returnValue = global::WinRT.ComWrappersSupport.FindObject(thisPtr).IndexOf(MarshalInspectable.FromAbi(value), out __index); + *index = __index; + *returnValue = (byte)(__returnValue ? 1 : 0); + + } + catch (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* value) + { + uint __value = default; + + *value = default; + + try + { + __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).Size; + *value = __value; + + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - private static global::System.Runtime.CompilerServices.ConditionalWeakTable _helperTable = - new global::System.Runtime.CompilerServices.ConditionalWeakTable(); + internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr, IID.IID_IBindableVectorView); + + private static readonly global::System.Runtime.CompilerServices.ConditionalWeakTable _helperTable = new(); unsafe object global::Microsoft.UI.Xaml.Interop.IBindableVectorView.GetAt(uint index) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Microsoft.UI.Xaml.Interop.IBindableIterator).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Microsoft.UI.Xaml.Interop.IBindableIterator).TypeHandle); var ThisPtr = _obj.ThisPtr; IntPtr __retval = default; try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetAt_0(ThisPtr, index, &__retval)); + { + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6](ThisPtr, index, &__retval)); + GC.KeepAlive(_obj); return MarshalInspectable.FromAbi(__retval); } finally @@ -367,15 +321,20 @@ private static unsafe int Do_Abi_get_Size_1(IntPtr thisPtr, uint* value) unsafe bool global::Microsoft.UI.Xaml.Interop.IBindableVectorView.IndexOf(object value, out uint index) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Microsoft.UI.Xaml.Interop.IBindableIterator).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Microsoft.UI.Xaml.Interop.IBindableIterator).TypeHandle); var ThisPtr = _obj.ThisPtr; ObjectReferenceValue __value = default; uint __index = default; byte __retval = default; try { - __value = MarshalInspectable.CreateMarshaler2(value); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.IndexOf_2(ThisPtr, MarshalInspectable.GetAbi(__value), &__index, &__retval)); + __value = MarshalInspectable.CreateMarshaler2(value); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[8]( + ThisPtr, + MarshalInspectable.GetAbi(__value), + &__index, + &__retval)); + GC.KeepAlive(_obj); index = __index; return __retval != 0; } @@ -389,10 +348,11 @@ private static unsafe int Do_Abi_get_Size_1(IntPtr thisPtr, uint* value) { get { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Microsoft.UI.Xaml.Interop.IBindableIterator).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Microsoft.UI.Xaml.Interop.IBindableIterator).TypeHandle); var ThisPtr = _obj.ThisPtr; - uint __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Size_1(ThisPtr, &__retval)); + uint __retval = default; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[7](ThisPtr, &__retval)); + GC.KeepAlive(_obj); return __retval; } } @@ -414,37 +374,115 @@ internal static class IBindableVectorView_Delegates } namespace ABI.System.Collections -{ +{ using global::Microsoft.UI.Xaml.Interop; - using global::System; - using global::System.Runtime.CompilerServices; + using global::System; + using global::System.Diagnostics.CodeAnalysis; + using global::System.Reflection; + using global::System.Runtime.CompilerServices; + +#if EMBED + internal +#else + public +#endif + static class IEnumerableMethods + { + public static global::System.Guid IID => global::WinRT.Interop.IID.IID_IEnumerable; + + public static IntPtr AbiToProjectionVftablePtr => IEnumerable.AbiToProjectionVftablePtr; + } [DynamicInterfaceCastableImplementation] [Guid("036D2C08-DF29-41AF-8AA2-D774BE62BA6F")] internal unsafe interface IEnumerable : global::System.Collections.IEnumerable, global::Microsoft.UI.Xaml.Interop.IBindableIterable { - public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IEnumerable)); - - public sealed class AdaptiveFromAbiHelper : FromAbiHelper, global::System.Collections.IEnumerable - { - private readonly Func _enumerator; + public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IEnumerable)); + +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + public sealed class AdaptiveFromAbiHelper : FromAbiHelper, global::System.Collections.IEnumerable +#pragma warning restore CA2257 + { + /// + /// The cached method. + /// + private static readonly MethodInfo EnumerableOfTGetEnumerator = typeof(IEnumerable<>).GetMethod("GetEnumerator"); + +#if NET8_0_OR_GREATER + private readonly MethodInvoker _enumerator; +#else + private readonly MethodInfo _enumerator; +#endif public AdaptiveFromAbiHelper(Type runtimeType, IWinRTObject winRTObject) :base(winRTObject) { - Type enumGenericType = (runtimeType.IsGenericType && runtimeType.GetGenericTypeDefinition() == typeof(global::System.Collections.Generic.IEnumerable<>)) ? - runtimeType : runtimeType.GetInterface("System.Collections.Generic.IEnumerable`1"); - if(enumGenericType != null) - { - var getEnumerator = enumGenericType.GetMethod("GetEnumerator"); - _enumerator = (IWinRTObject obj) => (global::System.Collections.IEnumerator)getEnumerator.Invoke(obj, null); - } - } - - public override global::System.Collections.IEnumerator GetEnumerator() => _enumerator != null ? _enumerator(_winrtObject) : base.GetEnumerator(); - } - - public class FromAbiHelper : global::System.Collections.IEnumerable + Type enumGenericType; + + // First, look for and get the IEnumerable<> interface implemented by this type, if one exists. The scenario is, imagine you + // got an 'IList' from somewhere, and then you use LINQ with it. What LINQ does is it converts both to object. Then + // it does a cast to 'IEnumerable'. At this point, you are trying an IDIC cast for 'IEnumerable' and asking the IDIC 'IEnumerable' + // interface to handle it. The question to answer is, what version of 'IEnumerable' is it. Is it the one provided by 'IList' + // or is it the one that maps to 'IBindableIterable'. We actually don't know at this point which one to use, especially given the + // 'IBindableIterable' interface may not even be implemented on the native object, as it was an 'IList' originally. This is + // also why we can't just check whether the object implements 'IEnumerable' and just call 'GetEnumerator()' on that. + if (runtimeType.IsGenericType && runtimeType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + enumGenericType = runtimeType; + } + else + { + [UnconditionalSuppressMessage("Trimming", "IL2070", Justification = + """ + 'SomeType.GetInterfaces().Any(t => t.GetGenericTypeDefinition() == typeof(IEnumerable<>)' is safe, + provided you obtained someType from something like an analyzable 'Type.GetType' or 'object.GetType' + (i.e. it is safe when the type you're asking about can exist on the GC heap as allocated). + """)] + [MethodImpl(MethodImplOptions.NoInlining)] + static Type GetEnumerableOfTInterface(Type runtimeType) + { + foreach (Type interfaceType in runtimeType.GetInterfaces()) + { + if (interfaceType.IsGenericType && interfaceType.GetGenericTypeDefinition() == typeof(IEnumerable<>)) + { + return interfaceType; + } + } + + return null; + } + + enumGenericType = GetEnumerableOfTInterface(runtimeType); + } + + var methodInfo = (MethodInfo)enumGenericType?.GetMemberWithSameMetadataDefinitionAs(EnumerableOfTGetEnumerator); + +#if NET8_0_OR_GREATER + _enumerator = methodInfo is null ? null : MethodInvoker.Create(methodInfo); +#else + _enumerator = methodInfo; +#endif + } + + public override IEnumerator GetEnumerator() + { + if (_enumerator is not null) + { + // The method returns IEnumerator<>, which implements IEnumerator +#if NET8_0_OR_GREATER + return Unsafe.As(_enumerator.Invoke(_winrtObject)); +#else + return Unsafe.As(_enumerator.Invoke(_winrtObject, null)); +#endif + } + + return base.GetEnumerator(); + } + } + +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + public class FromAbiHelper : global::System.Collections.IEnumerable +#pragma warning restore CA2257 { private readonly global::System.Collections.IEnumerable _iterable; protected readonly IWinRTObject _winrtObject; @@ -466,7 +504,7 @@ private IWinRTObject GetIterable() } public virtual global::System.Collections.IEnumerator GetEnumerator() => - new Generic.IEnumerator.FromAbiHelper(new NonGenericToGenericIterator(((global::Microsoft.UI.Xaml.Interop.IBindableIterable) GetIterable()).First())); + new Generic.FromAbiEnumerator(new NonGenericToGenericIterator(((global::Microsoft.UI.Xaml.Interop.IBindableIterable) GetIterable()).First())); private sealed class NonGenericToGenericIterator : global::Windows.Foundation.Collections.IIterator { @@ -479,9 +517,11 @@ private sealed class NonGenericToGenericIterator : global::Windows.Foundation.Co public bool _MoveNext() { return iterator.MoveNext(); } public uint GetMany(ref object[] items) => throw new NotSupportedException(); } - } - - public sealed class ToAbiHelper : IBindableIterable + } + +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + public sealed class ToAbiHelper : IBindableIterable +#pragma warning restore CA2257 { private readonly IEnumerable m_enumerable; @@ -503,74 +543,58 @@ private sealed class NonGenericToGenericEnumerator : IEnumerator public void Reset() { enumerator.Reset(); } public void Dispose() { } } + } + + public static readonly IntPtr AbiToProjectionVftablePtr; + static IEnumerable() + { + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(IEnumerable), sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[6] = &Do_Abi_First_0; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_First_0(IntPtr thisPtr, IntPtr* result) + { + *result = default; + try + { + var __this = global::WinRT.ComWrappersSupport.FindObject(thisPtr); + var iterator = ToAbiHelper.MakeBindableIterator(__this.GetEnumerator()); + *result = MarshalInterface.FromManaged(iterator); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; } - [Guid("036D2C08-DF29-41AF-8AA2-D774BE62BA6F")] - public struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _First_0; - public delegate* unmanaged[Stdcall] First_0 { get => (delegate* unmanaged[Stdcall])_First_0; set => _First_0 = value; } - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - - _First_0 = (delegate* unmanaged)&Do_Abi_First_0 - - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - - [UnmanagedCallersOnly] - private static unsafe int Do_Abi_First_0(IntPtr thisPtr, IntPtr* result) - { - *result = default; - try - { - var __this = global::WinRT.ComWrappersSupport.FindObject(thisPtr); - var iterator = ToAbiHelper.MakeBindableIterator(__this.GetEnumerator()); - *result = MarshalInterface.FromManaged(iterator); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - internal static ObjectReference ObjRefFromAbi(IntPtr thisPtr) + internal static ObjectReference ObjRefFromAbi(IntPtr thisPtr) { if (thisPtr == IntPtr.Zero) { return null; } - return ObjectReference.FromAbi(thisPtr); + return ObjectReference.FromAbi(thisPtr, IID.IID_IUnknown); } private static FromAbiHelper _AbiHelper(IWinRTObject _this) { - return (FromAbiHelper)_this.GetOrCreateTypeHelperData(typeof(global::System.Collections.IEnumerable).TypeHandle, - () => new FromAbiHelper((global::System.Collections.IEnumerable)_this)); + return (FromAbiHelper)_this.AdditionalTypeData.GetOrAdd(typeof(global::System.Collections.IEnumerable).TypeHandle, + static (_, _this) => new FromAbiHelper((global::System.Collections.IEnumerable)_this), _this); } unsafe global::Microsoft.UI.Xaml.Interop.IBindableIterator global::Microsoft.UI.Xaml.Interop.IBindableIterable.First() { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IEnumerable).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IEnumerable).TypeHandle); var ThisPtr = _obj.ThisPtr; IntPtr __retval = default; try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.First_0(ThisPtr, &__retval)); + { + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6](ThisPtr, &__retval)); + GC.KeepAlive(_obj); return MarshalInterface.FromAbi(__retval); } finally @@ -590,19 +614,33 @@ private static FromAbiHelper _AbiHelper(IWinRTObject _this) internal #else public -#endif +#endif static class IEnumerable_Delegates { public unsafe delegate int First_0(IntPtr thisPtr, IntPtr* result); - } - + } + +#if EMBED + internal +#else + public +#endif + static class IListMethods + { + public static Guid IID => global::WinRT.Interop.IID.IID_IList; + + public static IntPtr AbiToProjectionVftablePtr => IList.AbiToProjectionVftablePtr; + } + [DynamicInterfaceCastableImplementation] [Guid("393DE7DE-6FD0-4C0D-BB71-47244A113E93")] - unsafe interface IList : global::System.Collections.IList, global::Microsoft.UI.Xaml.Interop.IBindableVector + internal unsafe interface IList : global::System.Collections.IList, global::Microsoft.UI.Xaml.Interop.IBindableVector { - public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IList)); - - public sealed class FromAbiHelper : global::System.Collections.IList + public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IList)); + +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + public sealed class FromAbiHelper : global::System.Collections.IList +#pragma warning restore CA2257 { private readonly global::Microsoft.UI.Xaml.Interop.IBindableVector _vector; @@ -836,11 +874,13 @@ public IEnumerator GetEnumerator() { return ((IEnumerable)(IWinRTObject)_vector).GetEnumerator(); } - } - - public sealed class ToAbiHelper : IBindableVector + } + +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + public sealed class ToAbiHelper : IBindableVector +#pragma warning restore CA2257 { - private global::System.Collections.IList _list; + private readonly global::System.Collections.IList _list; public ToAbiHelper(global::System.Collections.IList list) => _list = list; @@ -962,10 +1002,31 @@ private static void EnsureIndexInt32(uint index, int listCapacity) } } - public IEnumerator GetEnumerator() => _list.GetEnumerator(); - + public IEnumerator GetEnumerator() => _list.GetEnumerator(); + + internal sealed class ListToBindableVectorViewAdapterTypeDetails : IWinRTExposedTypeDetails + { + public ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + { + return new ComWrappers.ComInterfaceEntry[] + { + new ComWrappers.ComInterfaceEntry + { + IID = IID.IID_IBindableVectorView, + Vtable = ABI.Microsoft.UI.Xaml.Interop.IBindableVectorView.AbiToProjectionVftablePtr + }, + new ComWrappers.ComInterfaceEntry + { + IID = ABI.System.Collections.IEnumerableMethods.IID, + Vtable = ABI.System.Collections.IEnumerableMethods.AbiToProjectionVftablePtr + } + }; + } + } + /// A Windows Runtime IBindableVectorView implementation that wraps around a managed IList exposing /// it to Windows runtime interop. + [global::WinRT.WinRTExposedType(typeof(ListToBindableVectorViewAdapterTypeDetails))] internal sealed class ListToBindableVectorViewAdapter : IBindableVectorView { private readonly global::System.Collections.IList list; @@ -1024,271 +1085,225 @@ public bool IndexOf(object value, out uint index) public IEnumerator GetEnumerator() => list.GetEnumerator(); } + } + + public static readonly IntPtr AbiToProjectionVftablePtr; + static IList() + { + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(IList), sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * 10); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[6] = &Do_Abi_GetAt_0; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[7] = &Do_Abi_get_Size_1; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[8] = &Do_Abi_GetView_2; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[9] = &Do_Abi_IndexOf_3; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[10] = &Do_Abi_SetAt_4; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[11] = &Do_Abi_InsertAt_5; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[12] = &Do_Abi_RemoveAt_6; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[13] = &Do_Abi_Append_7; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[14] = &Do_Abi_RemoveAtEnd_8; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[15] = &Do_Abi_Clear_9; + } + + private static readonly ConditionalWeakTable _adapterTable = new(); + + private static IBindableVector FindAdapter(IntPtr thisPtr) + { + var __this = global::WinRT.ComWrappersSupport.FindObject(thisPtr); + return _adapterTable.GetValue(__this, (list) => new ToAbiHelper(list)); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_GetAt_0(IntPtr thisPtr, uint index, IntPtr* result) + { + object __result = default; + *result = default; + try + { + __result = FindAdapter(thisPtr).GetAt(index); + *result = MarshalInspectable.FromManaged(__result); + + } + catch (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* result) + { + global::Microsoft.UI.Xaml.Interop.IBindableVectorView __result = default; + *result = default; + try + { + __result = FindAdapter(thisPtr).GetView(); + *result = MarshalInterface.FromManaged(__result); + } + catch (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, IntPtr value, uint* index, byte* returnValue) + { + bool __returnValue = default; + *index = default; + *returnValue = default; + uint __index = default; + try + { + __returnValue = FindAdapter(thisPtr).IndexOf(MarshalInspectable.FromAbi(value), out __index); + *index = __index; + *returnValue = (byte)(__returnValue ? 1 : 0); + } + catch (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, IntPtr value) + { + try + { + FindAdapter(thisPtr).SetAt(index, MarshalInspectable.FromAbi(value)); + } + catch (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, IntPtr value) + { + try + { + FindAdapter(thisPtr).InsertAt(index, MarshalInspectable.FromAbi(value)); + } + catch (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 + { + FindAdapter(thisPtr).RemoveAt(index); + } + catch (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, IntPtr value) + { + try + { + FindAdapter(thisPtr).Append(MarshalInspectable.FromAbi(value)); + } + catch (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 + { + FindAdapter(thisPtr).RemoveAtEnd(); + } + catch (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 + { + FindAdapter(thisPtr).Clear(); + } + catch (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* value) + { + uint __value = default; + + *value = default; + + try + { + __value = FindAdapter(thisPtr).Size; + *value = __value; + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; } - [Guid("393DE7DE-6FD0-4C0D-BB71-47244A113E93")] - public struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _GetAt_0; - private void* _get_Size_1; - private void* _GetView_2; - private void* _IndexOf_3; - private void* _SetAt_4; - private void* _InsertAt_5; - private void* _RemoveAt_6; - private void* _Append_7; - private void* _RemoveAtEnd_8; - private void* _Clear_9; - - public delegate* unmanaged[Stdcall] GetAt_0 { get => (delegate* unmanaged[Stdcall])_GetAt_0; set => _GetAt_0 = value; } - public delegate* unmanaged[Stdcall] get_Size_1 { get => (delegate* unmanaged[Stdcall])_get_Size_1; set => _get_Size_1 = value; } - public delegate* unmanaged[Stdcall] GetView_2 { get => (delegate* unmanaged[Stdcall])_GetView_2; set => _GetView_2 = value; } - public delegate* unmanaged[Stdcall] IndexOf_3 { get => (delegate* unmanaged[Stdcall])_IndexOf_3; set => _IndexOf_3 = value; } - public delegate* unmanaged[Stdcall] SetAt_4 { get => (delegate* unmanaged[Stdcall])_SetAt_4; set => _SetAt_4 = value; } - public delegate* unmanaged[Stdcall] InsertAt_5 { get => (delegate* unmanaged[Stdcall])_InsertAt_5; set => _InsertAt_5 = value; } - public delegate* unmanaged[Stdcall] RemoveAt_6 { get => (delegate* unmanaged[Stdcall])_RemoveAt_6; set => _RemoveAt_6 = value; } - public delegate* unmanaged[Stdcall] Append_7 { get => (delegate* unmanaged[Stdcall])_Append_7; set => _Append_7 = value; } - public delegate* unmanaged[Stdcall] RemoveAtEnd_8 { get => (delegate* unmanaged[Stdcall])_RemoveAtEnd_8; set => _RemoveAtEnd_8 = value; } - public delegate* unmanaged[Stdcall] Clear_9 { get => (delegate* unmanaged[Stdcall])_Clear_9; set => _Clear_9 = value; } - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - - _GetAt_0 = (delegate* unmanaged)&Do_Abi_GetAt_0, - _get_Size_1 = (delegate* unmanaged)&Do_Abi_get_Size_1, - _GetView_2 = (delegate* unmanaged)&Do_Abi_GetView_2, - _IndexOf_3 = (delegate* unmanaged)&Do_Abi_IndexOf_3, - _SetAt_4 = (delegate* unmanaged)&Do_Abi_SetAt_4, - _InsertAt_5 = (delegate* unmanaged)&Do_Abi_InsertAt_5, - _RemoveAt_6 = (delegate* unmanaged)&Do_Abi_RemoveAt_6, - _Append_7 = (delegate* unmanaged)&Do_Abi_Append_7, - _RemoveAtEnd_8 = (delegate* unmanaged)&Do_Abi_RemoveAtEnd_8, - _Clear_9 = (delegate* unmanaged)&Do_Abi_Clear_9, - - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 10); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - private static ConditionalWeakTable _adapterTable = - new ConditionalWeakTable(); - - private static IBindableVector FindAdapter(IntPtr thisPtr) - { - var __this = global::WinRT.ComWrappersSupport.FindObject(thisPtr); - return _adapterTable.GetValue(__this, (list) => new ToAbiHelper(list)); - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetAt_0(IntPtr thisPtr, uint index, IntPtr* result) - { - object __result = default; - *result = default; - try - { - __result = FindAdapter(thisPtr).GetAt(index); - *result = MarshalInspectable.FromManaged(__result); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetView_2(IntPtr thisPtr, IntPtr* result) - { - global::Microsoft.UI.Xaml.Interop.IBindableVectorView __result = default; - *result = default; - try - { - __result = FindAdapter(thisPtr).GetView(); - *result = MarshalInterface.FromManaged(__result); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_IndexOf_3(IntPtr thisPtr, IntPtr value, uint* index, byte* returnValue) - { - bool __returnValue = default; - *index = default; - *returnValue = default; - uint __index = default; - try - { - __returnValue = FindAdapter(thisPtr).IndexOf(MarshalInspectable.FromAbi(value), out __index); - *index = __index; - *returnValue = (byte)(__returnValue ? 1 : 0); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_SetAt_4(IntPtr thisPtr, uint index, IntPtr value) - { - try - { - FindAdapter(thisPtr).SetAt(index, MarshalInspectable.FromAbi(value)); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_InsertAt_5(IntPtr thisPtr, uint index, IntPtr value) - { - try - { - FindAdapter(thisPtr).InsertAt(index, MarshalInspectable.FromAbi(value)); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_RemoveAt_6(IntPtr thisPtr, uint index) - { - try - { - FindAdapter(thisPtr).RemoveAt(index); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_Append_7(IntPtr thisPtr, IntPtr value) - { - try - { - FindAdapter(thisPtr).Append(MarshalInspectable.FromAbi(value)); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_RemoveAtEnd_8(IntPtr thisPtr) - { - try - { - FindAdapter(thisPtr).RemoveAtEnd(); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_Clear_9(IntPtr thisPtr) - { - try - { - FindAdapter(thisPtr).Clear(); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_get_Size_1(IntPtr thisPtr, uint* value) - { - uint __value = default; - - *value = default; - - try - { - __value = FindAdapter(thisPtr).Size; - *value = __value; - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - internal static ObjectReference ObjRefFromAbi(IntPtr thisPtr) + internal static ObjectReference ObjRefFromAbi(IntPtr thisPtr) { if (thisPtr == IntPtr.Zero) { return null; } - return ObjectReference.FromAbi(thisPtr); + return ObjectReference.FromAbi(thisPtr, IID.IID_IUnknown); } - private static FromAbiHelper _VectorToList(IWinRTObject _this) + internal static FromAbiHelper _VectorToList(IWinRTObject _this) { - return (FromAbiHelper)_this.GetOrCreateTypeHelperData(typeof(global::System.Collections.IList).TypeHandle, - () => new FromAbiHelper((global::Microsoft.UI.Xaml.Interop.IBindableVector)_this)); + return (FromAbiHelper)_this.AdditionalTypeData.GetOrAdd(typeof(global::System.Collections.IList).TypeHandle, + static (_, _this) => new FromAbiHelper((global::Microsoft.UI.Xaml.Interop.IBindableVector)_this), _this); } unsafe object global::Microsoft.UI.Xaml.Interop.IBindableVector.GetAt(uint index) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle); var ThisPtr = _obj.ThisPtr; IntPtr __retval = default; try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetAt_0(ThisPtr, index, &__retval)); + { + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6](ThisPtr, index, &__retval)); + GC.KeepAlive(_obj); return MarshalInspectable.FromAbi(__retval); } finally @@ -1299,12 +1314,13 @@ private static FromAbiHelper _VectorToList(IWinRTObject _this) unsafe global::Microsoft.UI.Xaml.Interop.IBindableVectorView global::Microsoft.UI.Xaml.Interop.IBindableVector.GetView() { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle); var ThisPtr = _obj.ThisPtr; IntPtr __retval = default; try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetView_2(ThisPtr, &__retval)); + { + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[8](ThisPtr, &__retval)); + GC.KeepAlive(_obj); return MarshalInterface.FromAbi(__retval); } finally @@ -1315,15 +1331,16 @@ private static FromAbiHelper _VectorToList(IWinRTObject _this) unsafe bool global::Microsoft.UI.Xaml.Interop.IBindableVector.IndexOf(object value, out uint index) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle); var ThisPtr = _obj.ThisPtr; ObjectReferenceValue __value = default; uint __index = default; byte __retval = default; try { - __value = MarshalInspectable.CreateMarshaler2(value); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.IndexOf_3(ThisPtr, MarshalInspectable.GetAbi(__value), &__index, &__retval)); + __value = MarshalInspectable.CreateMarshaler2(value); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[9](ThisPtr, MarshalInspectable.GetAbi(__value), &__index, &__retval)); + GC.KeepAlive(_obj); index = __index; return __retval != 0; } @@ -1335,13 +1352,14 @@ private static FromAbiHelper _VectorToList(IWinRTObject _this) unsafe void global::Microsoft.UI.Xaml.Interop.IBindableVector.SetAt(uint index, object value) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle); var ThisPtr = _obj.ThisPtr; ObjectReferenceValue __value = default; try { - __value = MarshalInspectable.CreateMarshaler2(value); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.SetAt_4(ThisPtr, index, MarshalInspectable.GetAbi(__value))); + __value = MarshalInspectable.CreateMarshaler2(value); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[10](ThisPtr, index, MarshalInspectable.GetAbi(__value))); + GC.KeepAlive(_obj); } finally { @@ -1351,13 +1369,14 @@ private static FromAbiHelper _VectorToList(IWinRTObject _this) unsafe void global::Microsoft.UI.Xaml.Interop.IBindableVector.InsertAt(uint index, object value) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle); var ThisPtr = _obj.ThisPtr; ObjectReferenceValue __value = default; try { - __value = MarshalInspectable.CreateMarshaler2(value); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.InsertAt_5(ThisPtr, index, MarshalInspectable.GetAbi(__value))); + __value = MarshalInspectable.CreateMarshaler2(value); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[11](ThisPtr, index, MarshalInspectable.GetAbi(__value))); + GC.KeepAlive(_obj); } finally { @@ -1367,20 +1386,22 @@ private static FromAbiHelper _VectorToList(IWinRTObject _this) unsafe void global::Microsoft.UI.Xaml.Interop.IBindableVector.RemoveAt(uint index) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RemoveAt_6(ThisPtr, index)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle); + var ThisPtr = _obj.ThisPtr; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[12](ThisPtr, index)); + GC.KeepAlive(_obj); } unsafe void global::Microsoft.UI.Xaml.Interop.IBindableVector.Append(object value) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle); var ThisPtr = _obj.ThisPtr; ObjectReferenceValue __value = default; try { - __value = MarshalInspectable.CreateMarshaler2(value); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Append_7(ThisPtr, MarshalInspectable.GetAbi(__value))); + __value = MarshalInspectable.CreateMarshaler2(value); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[13](ThisPtr, MarshalInspectable.GetAbi(__value))); + GC.KeepAlive(_obj); } finally { @@ -1390,26 +1411,29 @@ private static FromAbiHelper _VectorToList(IWinRTObject _this) unsafe void global::Microsoft.UI.Xaml.Interop.IBindableVector.RemoveAtEnd() { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RemoveAtEnd_8(ThisPtr)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle); + var ThisPtr = _obj.ThisPtr; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[14](ThisPtr)); + GC.KeepAlive(_obj); } unsafe void global::Microsoft.UI.Xaml.Interop.IBindableVector.Clear() { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Clear_9(ThisPtr)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle); + var ThisPtr = _obj.ThisPtr; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[15](ThisPtr)); + GC.KeepAlive(_obj); } unsafe uint global::Microsoft.UI.Xaml.Interop.IBindableVector.Size { get { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.IList).TypeHandle); var ThisPtr = _obj.ThisPtr; - uint __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Size_1(ThisPtr, &__retval)); + uint __retval = default; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[7](ThisPtr, &__retval)); + GC.KeepAlive(_obj); return __retval; } } diff --git a/src/WinRT.Runtime/Projections/Bindable.netstandard2.0.cs b/src/WinRT.Runtime/Projections/Bindable.netstandard2.0.cs index 01b715ffb..a6710f931 100644 --- a/src/WinRT.Runtime/Projections/Bindable.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/Bindable.netstandard2.0.cs @@ -188,7 +188,8 @@ internal IBindableIterator(ObjectReference obj) public unsafe bool MoveNext() { byte __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.MoveNext_2(ThisPtr, &__retval)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.MoveNext_2(ThisPtr, &__retval)); + GC.KeepAlive(_obj); return __retval != 0; } @@ -199,7 +200,8 @@ public unsafe object Current IntPtr __retval = default; try { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Current_0(ThisPtr, &__retval)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Current_0(ThisPtr, &__retval)); + GC.KeepAlive(_obj); return MarshalInspectable.FromAbi(__retval); } finally @@ -214,7 +216,8 @@ public unsafe bool HasCurrent get { byte __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_HasCurrent_1(ThisPtr, &__retval)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_HasCurrent_1(ThisPtr, &__retval)); + GC.KeepAlive(_obj); return __retval != 0; } } @@ -354,7 +357,8 @@ public unsafe object GetAt(uint index) IntPtr __retval = default; try { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetAt_0(ThisPtr, index, &__retval)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetAt_0(ThisPtr, index, &__retval)); + GC.KeepAlive(_obj); return MarshalInspectable.FromAbi(__retval); } finally @@ -371,7 +375,8 @@ public unsafe bool IndexOf(object value, out uint index) try { __value = MarshalInspectable.CreateMarshaler2(value); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.IndexOf_2(ThisPtr, MarshalInspectable.GetAbi(__value), &__index, &__retval)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.IndexOf_2(ThisPtr, MarshalInspectable.GetAbi(__value), &__index, &__retval)); + GC.KeepAlive(_obj); index = __index; return __retval != 0; } @@ -386,7 +391,8 @@ public unsafe uint Size get { uint __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Size_1(ThisPtr, &__retval)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Size_1(ThisPtr, &__retval)); + GC.KeepAlive(_obj); return __retval; } } @@ -544,7 +550,8 @@ internal IEnumerable(ObjectReference obj) IntPtr __retval = default; try { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.First_0(ThisPtr, &__retval)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.First_0(ThisPtr, &__retval)); + GC.KeepAlive(_obj); return MarshalInterface.FromAbi(__retval); } finally @@ -691,7 +698,7 @@ public bool Contains(object item) public void Clear() { - _vector.Clear(); + _vector._Clear(); } public bool IsFixedSize { get => false; } @@ -1255,7 +1262,8 @@ public unsafe object GetAt(uint index) IntPtr __retval = default; try { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetAt_0(ThisPtr, index, &__retval)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetAt_0(ThisPtr, index, &__retval)); + GC.KeepAlive(_obj); return MarshalInspectable.FromAbi(__retval); } finally @@ -1269,7 +1277,8 @@ public unsafe object GetAt(uint index) IntPtr __retval = default; try { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetView_2(ThisPtr, &__retval)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetView_2(ThisPtr, &__retval)); + GC.KeepAlive(_obj); return MarshalInterface.FromAbi(__retval); } finally @@ -1286,7 +1295,8 @@ public unsafe bool IndexOf(object value, out uint index) try { __value = MarshalInspectable.CreateMarshaler2(value); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.IndexOf_3(ThisPtr, MarshalInspectable.GetAbi(__value), &__index, &__retval)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.IndexOf_3(ThisPtr, MarshalInspectable.GetAbi(__value), &__index, &__retval)); + GC.KeepAlive(_obj); index = __index; return __retval != 0; } @@ -1302,7 +1312,8 @@ public unsafe void SetAt(uint index, object value) try { __value = MarshalInspectable.CreateMarshaler2(value); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.SetAt_4(ThisPtr, index, MarshalInspectable.GetAbi(__value))); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.SetAt_4(ThisPtr, index, MarshalInspectable.GetAbi(__value))); + GC.KeepAlive(_obj); } finally { @@ -1316,7 +1327,8 @@ public unsafe void InsertAt(uint index, object value) try { __value = MarshalInspectable.CreateMarshaler2(value); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.InsertAt_5(ThisPtr, index, MarshalInspectable.GetAbi(__value))); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.InsertAt_5(ThisPtr, index, MarshalInspectable.GetAbi(__value))); + GC.KeepAlive(_obj); } finally { @@ -1326,7 +1338,8 @@ public unsafe void InsertAt(uint index, object value) public unsafe void RemoveAt(uint index) { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RemoveAt_6(ThisPtr, index)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RemoveAt_6(ThisPtr, index)); + GC.KeepAlive(_obj); } public unsafe void Append(object value) @@ -1335,7 +1348,8 @@ public unsafe void Append(object value) try { __value = MarshalInspectable.CreateMarshaler2(value); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Append_7(ThisPtr, MarshalInspectable.GetAbi(__value))); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Append_7(ThisPtr, MarshalInspectable.GetAbi(__value))); + GC.KeepAlive(_obj); } finally { @@ -1345,12 +1359,14 @@ public unsafe void Append(object value) public unsafe void RemoveAtEnd() { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RemoveAtEnd_8(ThisPtr)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RemoveAtEnd_8(ThisPtr)); + GC.KeepAlive(_obj); } public unsafe void _Clear() { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Clear_9(ThisPtr)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Clear_9(ThisPtr)); + GC.KeepAlive(_obj); } public unsafe uint Size @@ -1358,7 +1374,8 @@ public unsafe uint Size get { uint __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Size_1(ThisPtr, &__retval)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Size_1(ThisPtr, &__retval)); + GC.KeepAlive(_obj); return __retval; } } diff --git a/src/WinRT.Runtime/Projections/CollectionHybrid.net5.cs b/src/WinRT.Runtime/Projections/CollectionHybrid.net5.cs index 2754e61c6..4c13a8e73 100644 --- a/src/WinRT.Runtime/Projections/CollectionHybrid.net5.cs +++ b/src/WinRT.Runtime/Projections/CollectionHybrid.net5.cs @@ -4,6 +4,7 @@ using System; using System.Collections; using System.Reflection; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using WinRT; @@ -15,35 +16,44 @@ namespace ABI.System.Collections.Generic [DynamicInterfaceCastableImplementation] interface IReadOnlyCollection : global::System.Collections.Generic.IReadOnlyCollection { - private static global::System.Collections.Generic.IReadOnlyCollection CreateHelper(IWinRTObject _this) + private static global::System.Collections.Generic.IReadOnlyCollection CreateHelper(RuntimeTypeHandle type, IWinRTObject _this) { var genericType = typeof(T); if (genericType.IsGenericType && genericType.GetGenericTypeDefinition() == typeof(global::System.Collections.Generic.KeyValuePair<,>)) - { + { +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"'IDynamicInterfaceCastable' is not supported for generic type argument '{typeof(T)}'."); + } +#endif + +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 var iReadOnlyDictionary = typeof(global::System.Collections.Generic.IReadOnlyDictionary<,>).MakeGenericType(genericType.GetGenericArguments()); if (_this.IsInterfaceImplemented(iReadOnlyDictionary.TypeHandle, false)) { - var iReadOnlyDictionaryImpl = typeof(global::System.Collections.Generic.IReadOnlyDictionaryImpl<,>).MakeGenericType(genericType.GetGenericArguments()); + var iReadOnlyDictionaryImpl = typeof(global::System.Collections.Generic.IReadOnlyDictionaryImpl<,>).MakeGenericType(genericType.GetGenericArguments()); +#pragma warning restore IL3050 return (global::System.Collections.Generic.IReadOnlyCollection) iReadOnlyDictionaryImpl.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new global::System.Type[] { typeof(IObjectReference) }, null) .Invoke(new object[] { _this.NativeObject }); } - } - + } + var iReadOnlyList = typeof(global::System.Collections.Generic.IReadOnlyList); if (_this.IsInterfaceImplemented(iReadOnlyList.TypeHandle, false)) { return new global::System.Collections.Generic.IReadOnlyListImpl(_this.NativeObject); } - throw new InvalidOperationException("IReadOnlyCollection helper can not determine derived type."); + throw new InvalidOperationException("IReadOnlyCollection<> helper can not determine derived type."); } private static global::System.Collections.Generic.IReadOnlyCollection GetHelper(IWinRTObject _this) { - return (global::System.Collections.Generic.IReadOnlyCollection) _this.GetOrCreateTypeHelperData( + return (global::System.Collections.Generic.IReadOnlyCollection)_this.AdditionalTypeData.GetOrAdd( typeof(global::System.Collections.Generic.IReadOnlyCollection).TypeHandle, - () => CreateHelper(_this)); + CreateHelper, _this); } int global::System.Collections.Generic.IReadOnlyCollection.Count @@ -59,15 +69,24 @@ interface IReadOnlyCollection : global::System.Collections.Generic.IReadOnlyC [DynamicInterfaceCastableImplementation] interface ICollection : global::System.Collections.Generic.ICollection { - private static global::System.Collections.Generic.ICollection CreateHelper(IWinRTObject _this) + private static global::System.Collections.Generic.ICollection CreateHelper(RuntimeTypeHandle type, IWinRTObject _this) { var genericType = typeof(T); if (genericType.IsGenericType && genericType.GetGenericTypeDefinition() == typeof(global::System.Collections.Generic.KeyValuePair<,>)) - { + { +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"'IDynamicInterfaceCastable' is not supported for generic type argument '{typeof(T)}'."); + } +#endif + +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 var iDictionary = typeof(global::System.Collections.Generic.IDictionary<,>).MakeGenericType(genericType.GetGenericArguments()); if (_this.IsInterfaceImplemented(iDictionary.TypeHandle, false)) { - var iDictionaryImpl = typeof(global::System.Collections.Generic.IDictionaryImpl<,>).MakeGenericType(genericType.GetGenericArguments()); + var iDictionaryImpl = typeof(global::System.Collections.Generic.IDictionaryImpl<,>).MakeGenericType(genericType.GetGenericArguments()); +#pragma warning restore IL3050 return (global::System.Collections.Generic.ICollection) iDictionaryImpl.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new global::System.Type[] { typeof(IObjectReference) }, null) .Invoke(new object[] { _this.NativeObject }); @@ -80,17 +99,17 @@ interface ICollection : global::System.Collections.Generic.ICollection return new global::System.Collections.Generic.IListImpl(_this.NativeObject); } - throw new InvalidOperationException("ICollection helper can not determine derived type."); + throw new InvalidOperationException("ICollection<> helper can not determine derived type."); } private static global::System.Collections.Generic.ICollection GetHelper(IWinRTObject _this) { - return (global::System.Collections.Generic.ICollection)_this.GetOrCreateTypeHelperData( + return (global::System.Collections.Generic.ICollection)_this.AdditionalTypeData.GetOrAdd( typeof(global::System.Collections.Generic.ICollection).TypeHandle, - () => CreateHelper(_this)); + CreateHelper, _this); } - int global::System.Collections.Generic.ICollection.Count + int global::System.Collections.Generic.ICollection.Count => GetHelper((IWinRTObject)this).Count; bool global::System.Collections.Generic.ICollection.IsReadOnly @@ -116,5 +135,45 @@ interface ICollection : global::System.Collections.Generic.ICollection global::System.Collections.Generic.IEnumerator global::System.Collections.Generic.IEnumerable.GetEnumerator() => GetHelper((IWinRTObject)this).GetEnumerator(); + } +} + +namespace ABI.System.Collections +{ + [DynamicInterfaceCastableImplementation] + interface ICollection : global::System.Collections.ICollection + { + private static global::System.Collections.ICollection CreateHelper(RuntimeTypeHandle type, IWinRTObject _this) + { + var iList = typeof(global::System.Collections.IList); + if (_this.IsInterfaceImplemented(iList.TypeHandle, false)) + { + return IList._VectorToList(_this); + } + + throw new InvalidOperationException("ICollection helper can not determine derived type."); + } + + private static global::System.Collections.ICollection GetHelper(IWinRTObject _this) + { + return (global::System.Collections.ICollection)_this.AdditionalTypeData.GetOrAdd( + typeof(global::System.Collections.ICollection).TypeHandle, + CreateHelper, _this); + } + + int global::System.Collections.ICollection.Count + => GetHelper((IWinRTObject)this).Count; + + bool global::System.Collections.ICollection.IsSynchronized + => GetHelper((IWinRTObject)this).IsSynchronized; + + object global::System.Collections.ICollection.SyncRoot + => GetHelper((IWinRTObject)this).SyncRoot; + + void global::System.Collections.ICollection.CopyTo(Array array, int arrayIndex) + => GetHelper((IWinRTObject)this).CopyTo(array, arrayIndex); + + IEnumerator global::System.Collections.IEnumerable.GetEnumerator() + => GetHelper((IWinRTObject)this).GetEnumerator(); } } diff --git a/src/WinRT.Runtime/Projections/DataErrorsChangedEventArgs.cs b/src/WinRT.Runtime/Projections/DataErrorsChangedEventArgs.cs index 98793d74e..f0dabaac2 100644 --- a/src/WinRT.Runtime/Projections/DataErrorsChangedEventArgs.cs +++ b/src/WinRT.Runtime/Projections/DataErrorsChangedEventArgs.cs @@ -22,8 +22,9 @@ internal unsafe struct IDataErrorsChangedEventArgsVftbl public delegate* unmanaged[Stdcall] put_PropertyName_1 => (delegate* unmanaged[Stdcall])_put_PropertyName_1; } - - [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] +#if !NET + [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] +#endif [Guid("62D0BD1E-B85F-5FCC-842A-7CB0DDA37FE5")] internal unsafe sealed class WinRTDataErrorsChangedEventArgsRuntimeClassFactory { @@ -35,15 +36,11 @@ public struct Vftbl private void* _CreateInstance_0; public delegate* unmanaged[Stdcall] CreateInstance_0 => (delegate* unmanaged[Stdcall])_CreateInstance_0; } - public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); + public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr, IID.IID_DataErrorsChangedEventArgsRuntimeClassFactory); - public static implicit operator WinRTDataErrorsChangedEventArgsRuntimeClassFactory(IObjectReference obj) => (obj != null) ? new WinRTDataErrorsChangedEventArgsRuntimeClassFactory(obj) : null; - public static implicit operator WinRTDataErrorsChangedEventArgsRuntimeClassFactory(ObjectReference obj) => (obj != null) ? new WinRTDataErrorsChangedEventArgsRuntimeClassFactory(obj) : null; private readonly ObjectReference _obj; public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public WinRTDataErrorsChangedEventArgsRuntimeClassFactory(IObjectReference obj) : this(obj.As()) { } + public WinRTDataErrorsChangedEventArgsRuntimeClassFactory(IObjectReference obj) : this(obj.As(IID.IID_DataErrorsChangedEventArgsRuntimeClassFactory)) { } public WinRTDataErrorsChangedEventArgsRuntimeClassFactory(ObjectReference obj) { _obj = obj; @@ -57,8 +54,9 @@ public unsafe IObjectReference CreateInstance(string name) MarshalString.Pinnable __name = new(name); fixed (void* ___name = __name) { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.CreateInstance_0(ThisPtr, MarshalString.GetAbi(ref __name), &__retval)); - return ObjectReference.Attach(ref __retval); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.CreateInstance_0(ThisPtr, MarshalString.GetAbi(ref __name), &__retval)); + GC.KeepAlive(_obj); + return ObjectReference.Attach(ref __retval, IID.IID_IUnknown); } } finally @@ -73,7 +71,8 @@ public unsafe ObjectReferenceValue CreateInstanceForMarshaling(string name) MarshalString.Pinnable __name = new(name); fixed (void* ___name = __name) { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.CreateInstance_0(ThisPtr, MarshalString.GetAbi(ref __name), &__retval)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.CreateInstance_0(ThisPtr, MarshalString.GetAbi(ref __name), &__retval)); + GC.KeepAlive(_obj); return new ObjectReferenceValue(__retval); } } @@ -91,15 +90,7 @@ namespace ABI.System.ComponentModel #endif unsafe struct DataErrorsChangedEventArgs { - private sealed class ActivationFactory : BaseActivationFactory - { - public ActivationFactory() : base("Microsoft.UI.Xaml.Data", "Microsoft.UI.Xaml.Data.DataErrorsChangedEventArgs") - { - } - - internal static ABI.Microsoft.UI.Xaml.Data.WinRTDataErrorsChangedEventArgsRuntimeClassFactory Instance = - new ActivationFactory()._As(); - } + private static readonly ABI.Microsoft.UI.Xaml.Data.WinRTDataErrorsChangedEventArgsRuntimeClassFactory Instance = new(ActivationFactory.Get("Microsoft.UI.Xaml.Data.DataErrorsChangedEventArgs")); public static IObjectReference CreateMarshaler(global::System.ComponentModel.DataErrorsChangedEventArgs value) { @@ -108,7 +99,7 @@ public static IObjectReference CreateMarshaler(global::System.ComponentModel.Dat return null; } - return ActivationFactory.Instance.CreateInstance(value.PropertyName); + return Instance.CreateInstance(value.PropertyName); } public static ObjectReferenceValue CreateMarshaler2(global::System.ComponentModel.DataErrorsChangedEventArgs value) @@ -118,7 +109,7 @@ public static ObjectReferenceValue CreateMarshaler2(global::System.ComponentMode return new ObjectReferenceValue(); } - return ActivationFactory.Instance.CreateInstanceForMarshaling(value.PropertyName); + return Instance.CreateInstanceForMarshaling(value.PropertyName); } public static IntPtr GetAbi(IObjectReference m) => m?.ThisPtr ?? IntPtr.Zero; @@ -157,7 +148,15 @@ public static IntPtr FromManaged(global::System.ComponentModel.DataErrorsChanged } public static void DisposeMarshaler(IObjectReference m) { m?.Dispose(); } - public static void DisposeAbi(IntPtr abi) { MarshalInspectable.DisposeAbi(abi); } + public static void DisposeAbi(IntPtr abi) { MarshalInspectable.DisposeAbi(abi); } + + public static unsafe MarshalInterfaceHelper.MarshalerArray CreateMarshalerArray(global::System.ComponentModel.DataErrorsChangedEventArgs[] array) => MarshalInterfaceHelper.CreateMarshalerArray2(array, CreateMarshaler2); + public static (int length, IntPtr data) GetAbiArray(object box) => MarshalInterfaceHelper.GetAbiArray(box); + public static unsafe global::System.ComponentModel.DataErrorsChangedEventArgs[] FromAbiArray(object box) => MarshalInterfaceHelper.FromAbiArray(box, FromAbi); + public static void CopyAbiArray(global::System.ComponentModel.DataErrorsChangedEventArgs[] array, object box) => MarshalInterfaceHelper.CopyAbiArray(array, box, FromAbi); + public static (int length, IntPtr data) FromManagedArray(global::System.ComponentModel.DataErrorsChangedEventArgs[] array) => MarshalInterfaceHelper.FromManagedArray(array, FromManaged); + public static void DisposeMarshalerArray(MarshalInterfaceHelper.MarshalerArray array) => MarshalInterfaceHelper.DisposeMarshalerArray(array); + public static unsafe void DisposeAbiArray(object box) => MarshalInspectable.DisposeAbiArray(box); public static string GetGuidSignature() { diff --git a/src/WinRT.Runtime/Projections/EventHandler.cs b/src/WinRT.Runtime/Projections/EventHandler.cs index dcc08c148..d6fcd3fc9 100644 --- a/src/WinRT.Runtime/Projections/EventHandler.cs +++ b/src/WinRT.Runtime/Projections/EventHandler.cs @@ -1,335 +1,517 @@ // Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Concurrent; -using System.ComponentModel; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using WinRT; -using WinRT.Interop; - -namespace ABI.System -{ - [Guid("9DE1C535-6AE1-11E0-84E1-18A905BCC53F"), EditorBrowsable(EditorBrowsableState.Never)] -#if EMBED - internal -#else - public -#endif - static class EventHandler - { - public static Guid PIID = GuidGenerator.CreateIID(typeof(global::System.EventHandler)); - private static readonly global::System.Type Abi_Invoke_Type = Expression.GetDelegateType(new global::System.Type[] { typeof(void*), typeof(IntPtr), Marshaler.AbiType, typeof(int) }); - - private static readonly global::WinRT.Interop.IDelegateVftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - - static EventHandler() - { - AbiInvokeDelegate = global::System.Delegate.CreateDelegate(Abi_Invoke_Type, typeof(EventHandler).GetMethod(nameof(Do_Abi_Invoke), BindingFlags.Static | BindingFlags.NonPublic).MakeGenericMethod(new global::System.Type[] { Marshaler.AbiType }) - ); - AbiToProjectionVftable = new global::WinRT.Interop.IDelegateVftbl - { - IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, - Invoke = Marshal.GetFunctionPointerForDelegate(AbiInvokeDelegate) - }; - var nativeVftbl = ComWrappersSupport.AllocateVtableMemory(typeof(EventHandler), Marshal.SizeOf()); - Marshal.StructureToPtr(AbiToProjectionVftable, nativeVftbl, false); - AbiToProjectionVftablePtr = nativeVftbl; - } - - public static global::System.Delegate AbiInvokeDelegate { get; } - - public static unsafe IObjectReference CreateMarshaler(global::System.EventHandler managedDelegate) => +// Licensed under the MIT License. + +using System; +using System.Collections.Concurrent; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Threading; +using WinRT; +using WinRT.Interop; + +namespace ABI.System +{ + internal static class EventHandlerMethods + { + internal unsafe volatile static delegate* _Invoke; + + private static IntPtr abiToProjectionVftablePtr; + internal static IntPtr AbiToProjectionVftablePtr => abiToProjectionVftablePtr; + + internal static bool TryInitCCWVtable(IntPtr ptr) + { + bool success = global::System.Threading.Interlocked.CompareExchange(ref abiToProjectionVftablePtr, ptr, IntPtr.Zero) == IntPtr.Zero; + if (success) + { + EventHandler.AbiToProjectionVftablePtr = abiToProjectionVftablePtr; + +#if NET + ComWrappersSupport.RegisterComInterfaceEntries( + typeof(global::System.EventHandler), + DelegateTypeDetails>.GetExposedInterfaces( + new ComWrappers.ComInterfaceEntry + { + IID = EventHandler.PIID, + Vtable = abiToProjectionVftablePtr + })); +#endif + } + return success; + } + } + +#if EMBED + internal +#else + public +#endif + static class EventHandlerMethods where TAbi : unmanaged + { + public unsafe static bool InitRcwHelper(delegate* invoke) + { + if (EventHandlerMethods._Invoke == null) + { + EventHandlerMethods._Invoke = invoke; + } + return true; + } + + public static unsafe bool InitCcw( + delegate* unmanaged[Stdcall] invoke) + { + if (EventHandlerMethods.AbiToProjectionVftablePtr != default) + { + return false; + } + +#if NET + var abiToProjectionVftablePtr = (IntPtr)NativeMemory.AllocZeroed((nuint)(sizeof(IUnknownVftbl) + sizeof(IntPtr))); +#else + var abiToProjectionVftablePtr = Marshal.AllocCoTaskMem((sizeof(IUnknownVftbl) + sizeof(IntPtr))); +#endif + *(IUnknownVftbl*)abiToProjectionVftablePtr = IUnknownVftbl.AbiToProjectionVftbl; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[3] = invoke; + + if (!EventHandlerMethods.TryInitCCWVtable(abiToProjectionVftablePtr)) + { +#if NET + NativeMemory.Free((void*)abiToProjectionVftablePtr); +#else + Marshal.FreeCoTaskMem(abiToProjectionVftablePtr); +#endif + return false; + } + + return true; + } + + public static void Abi_Invoke(IntPtr thisPtr, object sender, T args) + { +#if NET + var invoke = ComWrappersSupport.FindObject>(thisPtr); + invoke.Invoke(sender, args); +#else + global::WinRT.ComWrappersSupport.MarshalDelegateInvoke(thisPtr, (global::System.EventHandler invoke) => + { + invoke.Invoke(sender, args); + }); +#endif + } + } + + [Guid("9DE1C535-6AE1-11E0-84E1-18A905BCC53F"), EditorBrowsable(EditorBrowsableState.Never)] +#if EMBED + internal +#else + public +#endif + static class EventHandler + { + public static readonly Guid PIID = GuidGenerator.CreateIIDUnsafe(typeof(global::System.EventHandler)); + + public static Guid IID => PIID; + + /// + /// The ABI delegate type for the fallback, non-AOT scenario. + /// This is lazily-initialized from the fallback paths below. + /// + private static global::System.Type _abi_invoke_type; + + public static IntPtr AbiToProjectionVftablePtr; + + static unsafe EventHandler() + { + ComWrappersSupport.RegisterHelperType(typeof(global::System.EventHandler), typeof(global::ABI.System.EventHandler)); + ComWrappersSupport.RegisterDelegateFactory(typeof(global::System.EventHandler), CreateRcw); + +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + // On NAOT, we always just use the available vtable, no matter if it's been set or not. + // See: https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. + // We specifically need this check to be separate than that of the vtable not being null. + AbiToProjectionVftablePtr = EventHandlerMethods.AbiToProjectionVftablePtr; + + return; + } +#endif + + if (EventHandlerMethods.AbiToProjectionVftablePtr != default) + { + AbiToProjectionVftablePtr = EventHandlerMethods.AbiToProjectionVftablePtr; + } + else + { +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + // Initialize the ABI invoke delegate type (we don't want to do that from a method in this type, or it will get rooted). + // That is because there's other reflection paths just preserving members from EventHandler unconditionally. + _abi_invoke_type = Projections.GetAbiDelegateType(typeof(void*), typeof(IntPtr), Marshaler.AbiType, typeof(int)); + + // Handle the compat scenario where the source generator wasn't used or IDIC was used. + AbiInvokeDelegate = global::System.Delegate.CreateDelegate(_abi_invoke_type, typeof(EventHandler).GetMethod(nameof(Do_Abi_Invoke), BindingFlags.Static | BindingFlags.NonPublic). + MakeGenericMethod(new global::System.Type[] { Marshaler.AbiType })); + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(EventHandler), sizeof(global::WinRT.Interop.IDelegateVftbl)); + *(global::WinRT.Interop.IUnknownVftbl*)AbiToProjectionVftablePtr = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl; + ((IntPtr*)AbiToProjectionVftablePtr)[3] = Marshal.GetFunctionPointerForDelegate(AbiInvokeDelegate); +#pragma warning restore IL3050 + } + } + + public static global::System.Delegate AbiInvokeDelegate { get; } + + public static unsafe IObjectReference CreateMarshaler(global::System.EventHandler managedDelegate) => managedDelegate is null ? null : MarshalDelegate.CreateMarshaler(managedDelegate, PIID); - public static unsafe ObjectReferenceValue CreateMarshaler2(global::System.EventHandler managedDelegate) => - MarshalDelegate.CreateMarshaler2(managedDelegate, PIID); - - public static IntPtr GetAbi(IObjectReference value) => - value is null ? IntPtr.Zero : MarshalInterfaceHelper>.GetAbi(value); - - public static unsafe global::System.EventHandler FromAbi(IntPtr nativeDelegate) + public static unsafe ObjectReferenceValue CreateMarshaler2(global::System.EventHandler managedDelegate) => + MarshalDelegate.CreateMarshaler2(managedDelegate, PIID); + + public static IntPtr GetAbi(IObjectReference value) => + value is null ? IntPtr.Zero : MarshalInterfaceHelper>.GetAbi(value); + + public static unsafe global::System.EventHandler FromAbi(IntPtr nativeDelegate) { - return MarshalDelegate.FromAbi>(nativeDelegate); + return MarshalDelegate.FromAbi>(nativeDelegate); } public static global::System.EventHandler CreateRcw(IntPtr ptr) { return new global::System.EventHandler(new NativeDelegateWrapper(ComWrappersSupport.GetObjectReferenceForInterface(ptr, PIID)).Invoke); - } - - [global::WinRT.ObjectReferenceWrapper(nameof(_nativeDelegate))] -#if !NET - private sealed class NativeDelegateWrapper -#else - private sealed class NativeDelegateWrapper : IWinRTObject -#endif + } + +#if !NET + [global::WinRT.ObjectReferenceWrapper(nameof(_nativeDelegate))] + private sealed class NativeDelegateWrapper +#else + private sealed class NativeDelegateWrapper : IWinRTObject +#endif { - private readonly ObjectReference _nativeDelegate; - - public NativeDelegateWrapper(ObjectReference nativeDelegate) + private readonly ObjectReference _nativeDelegate; + + public NativeDelegateWrapper(ObjectReference nativeDelegate) { - _nativeDelegate = nativeDelegate; + _nativeDelegate = nativeDelegate; } -#if NET +#if NET IObjectReference IWinRTObject.NativeObject => _nativeDelegate; bool IWinRTObject.HasUnwrappableNativeObject => true; - private volatile ConcurrentDictionary _queryInterfaceCache; - private ConcurrentDictionary MakeQueryInterfaceCache() + private volatile ConcurrentDictionary _queryInterfaceCache; + private ConcurrentDictionary MakeQueryInterfaceCache() { - global::System.Threading.Interlocked.CompareExchange(ref _queryInterfaceCache, new ConcurrentDictionary(), null); + global::System.Threading.Interlocked.CompareExchange(ref _queryInterfaceCache, new ConcurrentDictionary(), null); return _queryInterfaceCache; } - ConcurrentDictionary IWinRTObject.QueryInterfaceCache => _queryInterfaceCache ?? MakeQueryInterfaceCache(); - - private volatile ConcurrentDictionary _additionalTypeData; - private ConcurrentDictionary MakeAdditionalTypeData() + ConcurrentDictionary IWinRTObject.QueryInterfaceCache => _queryInterfaceCache ?? MakeQueryInterfaceCache(); + + private volatile ConcurrentDictionary _additionalTypeData; + private ConcurrentDictionary MakeAdditionalTypeData() { - global::System.Threading.Interlocked.CompareExchange(ref _additionalTypeData, new ConcurrentDictionary(), null); + global::System.Threading.Interlocked.CompareExchange(ref _additionalTypeData, new ConcurrentDictionary(), null); return _additionalTypeData; } - ConcurrentDictionary IWinRTObject.AdditionalTypeData => _additionalTypeData ?? MakeAdditionalTypeData(); -#endif - - public void Invoke(object sender, T args) - { - IntPtr ThisPtr = _nativeDelegate.ThisPtr; - var abiInvoke = Marshal.GetDelegateForFunctionPointer(_nativeDelegate.Vftbl.Invoke, Abi_Invoke_Type); - ObjectReferenceValue __sender = default; - object __args = default; - var __params = new object[] { ThisPtr, null, null }; - try - { - __sender = MarshalInspectable.CreateMarshaler2(sender); - __params[1] = MarshalInspectable.GetAbi(__sender); - __args = Marshaler.CreateMarshaler2(args); - __params[2] = Marshaler.GetAbi(__args); - abiInvoke.DynamicInvokeAbi(__params); - } - finally - { - MarshalInspectable.DisposeMarshaler(__sender); - Marshaler.DisposeMarshaler(__args); - } - } - } - - public static IntPtr FromManaged(global::System.EventHandler managedDelegate) => - CreateMarshaler2(managedDelegate).Detach(); - - public static void DisposeMarshaler(IObjectReference value) => MarshalInterfaceHelper>.DisposeMarshaler(value); - - public static void DisposeAbi(IntPtr abi) => MarshalInterfaceHelper>.DisposeAbi(abi); - - private static unsafe int Do_Abi_Invoke(void* thisPtr, IntPtr sender, TAbi args) - { - try + ConcurrentDictionary IWinRTObject.AdditionalTypeData => _additionalTypeData ?? MakeAdditionalTypeData(); +#endif + + public unsafe void Invoke(object sender, T args) { -#if NET - var invoke = ComWrappersSupport.FindObject>(new IntPtr(thisPtr)); +#if NET + // Standalone path for NAOT to ensure the linker trims code as expected + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + EventHandlerMethods._Invoke(_nativeDelegate, sender, args); + + return; + } +#endif + + if (EventHandlerMethods._Invoke != null) + { + EventHandlerMethods._Invoke(_nativeDelegate, sender, args); + } + else + { +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + // Same as in the static constructor, we initialize the ABI delegate type manually here if needed. + // We gate this behind a null check to avoid unnecessarily calling Projections.GetAbiDelegateType. + if (Volatile.Read(ref _abi_invoke_type) is null) + { + global::System.Threading.Interlocked.CompareExchange(ref _abi_invoke_type, Projections.GetAbiDelegateType(typeof(void*), typeof(IntPtr), Marshaler.AbiType, typeof(int)), null); + } + + IntPtr ThisPtr = _nativeDelegate.ThisPtr; + var abiInvoke = Marshal.GetDelegateForFunctionPointer(_nativeDelegate.Vftbl.Invoke, _abi_invoke_type); + ObjectReferenceValue __sender = default; + object __args = default; + var __params = new object[] { ThisPtr, null, null }; + try + { + __sender = MarshalInspectable.CreateMarshaler2(sender); + __params[1] = MarshalInspectable.GetAbi(__sender); + __args = Marshaler.CreateMarshaler2(args); + __params[2] = Marshaler.GetAbi(__args); + abiInvoke.DynamicInvokeAbi(__params); + GC.KeepAlive(_nativeDelegate); + GC.KeepAlive(__params); + } + finally + { + MarshalInspectable.DisposeMarshaler(__sender); + Marshaler.DisposeMarshaler(__args); + } + } +#pragma warning restore IL3050 + } + } + + public static IntPtr FromManaged(global::System.EventHandler managedDelegate) => + CreateMarshaler2(managedDelegate).Detach(); + + public static void DisposeMarshaler(IObjectReference value) => MarshalInterfaceHelper>.DisposeMarshaler(value); + + public static void DisposeAbi(IntPtr abi) => MarshalInterfaceHelper>.DisposeAbi(abi); + + public static unsafe MarshalInterfaceHelper>.MarshalerArray CreateMarshalerArray(global::System.EventHandler[] array) => MarshalInterfaceHelper>.CreateMarshalerArray2(array, CreateMarshaler2); + public static (int length, IntPtr data) GetAbiArray(object box) => MarshalInterfaceHelper>.GetAbiArray(box); + public static unsafe global::System.EventHandler[] FromAbiArray(object box) => MarshalInterfaceHelper>.FromAbiArray(box, FromAbi); + public static void CopyAbiArray(global::System.EventHandler[] array, object box) => MarshalInterfaceHelper>.CopyAbiArray(array, box, FromAbi); + public static (int length, IntPtr data) FromManagedArray(global::System.EventHandler[] array) => MarshalInterfaceHelper>.FromManagedArray(array, FromManaged); + public static void DisposeMarshalerArray(MarshalInterfaceHelper>.MarshalerArray array) => MarshalInterfaceHelper>.DisposeMarshalerArray(array); + public static unsafe void DisposeAbiArray(object box) => MarshalInspectable.DisposeAbiArray(box); + +#if NET + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_Invoke(void* thisPtr, IntPtr sender, TAbi args) + { + try + { +#if NET + var invoke = ComWrappersSupport.FindObject>(new IntPtr(thisPtr)); invoke.Invoke(MarshalInspectable.FromAbi(sender), Marshaler.FromAbi(args)); -#else +#else global::WinRT.ComWrappersSupport.MarshalDelegateInvoke(new IntPtr(thisPtr), (global::System.EventHandler invoke) => { invoke.Invoke(MarshalInspectable.FromAbi(sender), Marshaler.FromAbi(args)); - }); -#endif - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - [Guid("c50898f6-c536-5f47-8583-8b2c2438a13b")] - internal static class EventHandler - { -#if !NET - private delegate int Abi_Invoke(IntPtr thisPtr, IntPtr sender, IntPtr args); -#endif - - private static readonly global::WinRT.Interop.IDelegateVftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - - static unsafe EventHandler() - { - AbiToProjectionVftable = new global::WinRT.Interop.IDelegateVftbl - { - IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, -#if !NET - Invoke = Marshal.GetFunctionPointerForDelegate(AbiInvokeDelegate = (Abi_Invoke)Do_Abi_Invoke) -#else - Invoke = (IntPtr)(delegate* unmanaged[Stdcall])&Do_Abi_Invoke -#endif - }; - var nativeVftbl = ComWrappersSupport.AllocateVtableMemory(typeof(EventHandler), Marshal.SizeOf()); - Marshal.StructureToPtr(AbiToProjectionVftable, nativeVftbl, false); - AbiToProjectionVftablePtr = nativeVftbl; - } - -#if !NET - public static global::System.Delegate AbiInvokeDelegate { get; } -#endif - - private static readonly Guid IID = new(0xc50898f6, 0xc536, 0x5f47, 0x85, 0x83, 0x8b, 0x2c, 0x24, 0x38, 0xa1, 0x3b); - - public static unsafe IObjectReference CreateMarshaler(global::System.EventHandler managedDelegate) => + }); +#endif + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + + [Guid("c50898f6-c536-5f47-8583-8b2c2438a13b")] + internal static class EventHandler + { +#if !NET + private delegate int Abi_Invoke(IntPtr thisPtr, IntPtr sender, IntPtr args); +#endif + + private static readonly global::WinRT.Interop.IDelegateVftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + + static unsafe EventHandler() + { + AbiToProjectionVftable = new global::WinRT.Interop.IDelegateVftbl + { + IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, +#if !NET + Invoke = Marshal.GetFunctionPointerForDelegate(AbiInvokeDelegate = (Abi_Invoke)Do_Abi_Invoke) +#else + Invoke = (IntPtr)(delegate* unmanaged[Stdcall])&Do_Abi_Invoke +#endif + }; + var nativeVftbl = ComWrappersSupport.AllocateVtableMemory(typeof(EventHandler), sizeof(global::WinRT.Interop.IDelegateVftbl)); + *(global::WinRT.Interop.IDelegateVftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = nativeVftbl; + ComWrappersSupport.RegisterDelegateFactory(typeof(global::System.EventHandler), CreateRcw); + } + +#if !NET + public static global::System.Delegate AbiInvokeDelegate { get; } +#endif + + public static Guid IID => global::WinRT.Interop.IID.IID_EventHandler; + + + public static unsafe IObjectReference CreateMarshaler(global::System.EventHandler managedDelegate) => managedDelegate is null ? null : MarshalDelegate.CreateMarshaler(managedDelegate, IID); - public static unsafe ObjectReferenceValue CreateMarshaler2(global::System.EventHandler managedDelegate) => - MarshalDelegate.CreateMarshaler2(managedDelegate, IID); - - public static IntPtr GetAbi(IObjectReference value) => - value is null ? IntPtr.Zero : MarshalInterfaceHelper>.GetAbi(value); - - public static unsafe global::System.EventHandler FromAbi(IntPtr nativeDelegate) + public static unsafe ObjectReferenceValue CreateMarshaler2(global::System.EventHandler managedDelegate) => + MarshalDelegate.CreateMarshaler2(managedDelegate, IID); + + public static IntPtr GetAbi(IObjectReference value) => + value is null ? IntPtr.Zero : MarshalInterfaceHelper>.GetAbi(value); + + public static unsafe global::System.EventHandler FromAbi(IntPtr nativeDelegate) { return MarshalDelegate.FromAbi(nativeDelegate); } public static global::System.EventHandler CreateRcw(IntPtr ptr) { - return new global::System.EventHandler(new NativeDelegateWrapper(ComWrappersSupport.GetObjectReferenceForInterface(ptr, IID)).Invoke); + return new global::System.EventHandler(new NativeDelegateWrapper( +#if NET + ComWrappersSupport.GetObjectReferenceForInterface(ptr, IID)).Invoke +#else + ComWrappersSupport.GetObjectReferenceForInterface(ptr, IID)).Invoke +#endif + ); } - [global::WinRT.ObjectReferenceWrapper(nameof(_nativeDelegate))] -#if !NET - private sealed class NativeDelegateWrapper -#else - private sealed class NativeDelegateWrapper : IWinRTObject -#endif +#if !NET + [global::WinRT.ObjectReferenceWrapper(nameof(_nativeDelegate))] + private sealed class NativeDelegateWrapper +#else + private sealed class NativeDelegateWrapper : IWinRTObject +#endif { - private readonly ObjectReference _nativeDelegate; - - public NativeDelegateWrapper(ObjectReference nativeDelegate) +#if NET + private readonly ObjectReference _nativeDelegate; +#else + private readonly ObjectReference _nativeDelegate; +#endif + + public NativeDelegateWrapper( +#if NET + ObjectReference nativeDelegate +#else + ObjectReference nativeDelegate +#endif + ) { - _nativeDelegate = nativeDelegate; + _nativeDelegate = nativeDelegate; } -#if NET +#if NET IObjectReference IWinRTObject.NativeObject => _nativeDelegate; bool IWinRTObject.HasUnwrappableNativeObject => true; - private volatile ConcurrentDictionary _queryInterfaceCache; - private ConcurrentDictionary MakeQueryInterfaceCache() + private volatile ConcurrentDictionary _queryInterfaceCache; + private ConcurrentDictionary MakeQueryInterfaceCache() { - global::System.Threading.Interlocked.CompareExchange(ref _queryInterfaceCache, new ConcurrentDictionary(), null); + global::System.Threading.Interlocked.CompareExchange(ref _queryInterfaceCache, new ConcurrentDictionary(), null); return _queryInterfaceCache; } - ConcurrentDictionary IWinRTObject.QueryInterfaceCache => _queryInterfaceCache ?? MakeQueryInterfaceCache(); - - private volatile ConcurrentDictionary _additionalTypeData; - private ConcurrentDictionary MakeAdditionalTypeData() + ConcurrentDictionary IWinRTObject.QueryInterfaceCache => _queryInterfaceCache ?? MakeQueryInterfaceCache(); + + private volatile ConcurrentDictionary _additionalTypeData; + private ConcurrentDictionary MakeAdditionalTypeData() { - global::System.Threading.Interlocked.CompareExchange(ref _additionalTypeData, new ConcurrentDictionary(), null); + global::System.Threading.Interlocked.CompareExchange(ref _additionalTypeData, new ConcurrentDictionary(), null); return _additionalTypeData; } - ConcurrentDictionary IWinRTObject.AdditionalTypeData => _additionalTypeData ?? MakeAdditionalTypeData(); -#endif - - public unsafe void Invoke(object sender, EventArgs args) - { - IntPtr ThisPtr = _nativeDelegate.ThisPtr; -#if !NET - var abiInvoke = Marshal.GetDelegateForFunctionPointer(_nativeDelegate.Vftbl.Invoke); -#else - var abiInvoke = (delegate* unmanaged[Stdcall])(_nativeDelegate.Vftbl.Invoke); -#endif - ObjectReferenceValue __sender = default; - ObjectReferenceValue __args = default; - try - { - __sender = MarshalInspectable.CreateMarshaler2(sender); - __args = MarshalInspectable.CreateMarshaler2(args); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(abiInvoke( - ThisPtr, - MarshalInspectable.GetAbi(__sender), - MarshalInspectable.GetAbi(__args))); - } - finally - { - MarshalInspectable.DisposeMarshaler(__sender); - MarshalInspectable.DisposeMarshaler(__args); - } - } - } - - public static IntPtr FromManaged(global::System.EventHandler managedDelegate) => - CreateMarshaler2(managedDelegate).Detach(); - - public static void DisposeMarshaler(IObjectReference value) => MarshalInterfaceHelper>.DisposeMarshaler(value); - - public static void DisposeAbi(IntPtr abi) => MarshalInterfaceHelper>.DisposeAbi(abi); - -#if NET - [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] -#endif - private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr sender, IntPtr args) - { - try + ConcurrentDictionary IWinRTObject.AdditionalTypeData => _additionalTypeData ?? MakeAdditionalTypeData(); +#endif + + public unsafe void Invoke(object sender, EventArgs args) { -#if NET - var invoke = ComWrappersSupport.FindObject(thisPtr); - invoke.Invoke( + IntPtr ThisPtr = _nativeDelegate.ThisPtr; +#if !NET + var abiInvoke = Marshal.GetDelegateForFunctionPointer(_nativeDelegate.Vftbl.Invoke); +#else + var abiInvoke = (delegate* unmanaged[Stdcall])(*(void***)ThisPtr)[3]; +#endif + ObjectReferenceValue __sender = default; + ObjectReferenceValue __args = default; + try + { + __sender = MarshalInspectable.CreateMarshaler2(sender); + __args = MarshalInspectable.CreateMarshaler2(args); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(abiInvoke( + ThisPtr, + MarshalInspectable.GetAbi(__sender), + MarshalInspectable.GetAbi(__args))); + GC.KeepAlive(_nativeDelegate); + } + finally + { + MarshalInspectable.DisposeMarshaler(__sender); + MarshalInspectable.DisposeMarshaler(__args); + } + } + } + + public static IntPtr FromManaged(global::System.EventHandler managedDelegate) => + CreateMarshaler2(managedDelegate).Detach(); + + public static void DisposeMarshaler(IObjectReference value) => MarshalInterfaceHelper>.DisposeMarshaler(value); + + public static void DisposeAbi(IntPtr abi) => MarshalInterfaceHelper>.DisposeAbi(abi); + + public static unsafe MarshalInterfaceHelper.MarshalerArray CreateMarshalerArray(global::System.EventHandler[] array) => MarshalInterfaceHelper.CreateMarshalerArray2(array, CreateMarshaler2); + public static (int length, IntPtr data) GetAbiArray(object box) => MarshalInterfaceHelper.GetAbiArray(box); + public static unsafe global::System.EventHandler[] FromAbiArray(object box) => MarshalInterfaceHelper.FromAbiArray(box, FromAbi); + public static void CopyAbiArray(global::System.EventHandler[] array, object box) => MarshalInterfaceHelper.CopyAbiArray(array, box, FromAbi); + public static (int length, IntPtr data) FromManagedArray(global::System.EventHandler[] array) => MarshalInterfaceHelper.FromManagedArray(array, FromManaged); + public static void DisposeMarshalerArray(MarshalInterfaceHelper.MarshalerArray array) => MarshalInterfaceHelper.DisposeMarshalerArray(array); + public static unsafe void DisposeAbiArray(object box) => MarshalInspectable.DisposeAbiArray(box); + +#if NET + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] +#endif + private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr sender, IntPtr args) + { + try + { +#if NET + var invoke = ComWrappersSupport.FindObject(thisPtr); + invoke.Invoke( MarshalInspectable.FromAbi(sender), MarshalInspectable.FromAbi(args) as EventArgs ?? EventArgs.Empty); -#else - global::WinRT.ComWrappersSupport.MarshalDelegateInvoke(thisPtr, (global::System.EventHandler invoke) => - { - invoke.Invoke( - MarshalInspectable.FromAbi(sender), - MarshalInspectable.FromAbi(args) as EventArgs ?? EventArgs.Empty); - }); -#endif - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - internal sealed unsafe class EventHandlerEventSource : EventSource - { - internal EventHandlerEventSource(IObjectReference obj, - delegate* unmanaged[Stdcall] addHandler, - delegate* unmanaged[Stdcall] removeHandler) - : base(obj, addHandler, removeHandler) - { - } - - protected override ObjectReferenceValue CreateMarshaler(global::System.EventHandler del) => - EventHandler.CreateMarshaler2(del); - - protected override State CreateEventState() => - new EventState(_obj.ThisPtr, _index); - - private sealed class EventState : State - { - public EventState(IntPtr obj, int index) - : base(obj, index) - { - } - - protected override Delegate GetEventInvoke() - { - global::System.EventHandler handler = (global::System.Object obj, global::System.EventArgs e) => - { - var localDel = (global::System.EventHandler) del; - if (localDel != null) - localDel.Invoke(obj, e); - }; - return handler; - } - } - } -} +#else + global::WinRT.ComWrappersSupport.MarshalDelegateInvoke(thisPtr, (global::System.EventHandler invoke) => + { + invoke.Invoke( + MarshalInspectable.FromAbi(sender), + MarshalInspectable.FromAbi(args) as EventArgs ?? EventArgs.Empty); + }); +#endif + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + +#if NET + [SkipLocalsInit] + internal static ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + { + Span entries = stackalloc ComWrappers.ComInterfaceEntry[3]; + int count = 0; + + entries[count++] = new ComWrappers.ComInterfaceEntry + { + IID = IID, + Vtable = AbiToProjectionVftablePtr + }; + + if (FeatureSwitches.EnableIReferenceSupport) + { + entries[count++] = new ComWrappers.ComInterfaceEntry + { + IID = global::WinRT.Interop.IID.IID_IPropertyValue, + Vtable = ABI.Windows.Foundation.ManagedIPropertyValueImpl.AbiToProjectionVftablePtr + }; + + entries[count++] = new ComWrappers.ComInterfaceEntry + { + IID = global::WinRT.Interop.IID.IID_NullableEventHandler, + Vtable = Nullable_EventHandler.AbiToProjectionVftablePtr + }; + } + + return entries.Slice(0, count).ToArray(); + } +#endif + } +} diff --git a/src/WinRT.Runtime/Projections/GenericDelegateHelper.net5.cs b/src/WinRT.Runtime/Projections/GenericDelegateHelper.net5.cs new file mode 100644 index 000000000..982964476 --- /dev/null +++ b/src/WinRT.Runtime/Projections/GenericDelegateHelper.net5.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace WinRT +{ + internal static class GenericDelegateHelper + { + internal static ConditionalWeakTable DelegateTable = new(); + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + internal unsafe static Delegate CreateDelegate(IntPtr ptr, ref Delegate delegateRef, Type delegateType, int offset) + { + var newDelegate = Marshal.GetDelegateForFunctionPointer((*(IntPtr**)ptr)[offset], delegateType); + global::System.Threading.Interlocked.CompareExchange(ref delegateRef, newDelegate, null); + return delegateRef; + } + } +} diff --git a/src/WinRT.Runtime/Projections/Geometry.cs b/src/WinRT.Runtime/Projections/Geometry.cs index a7227556d..c21e65ea5 100644 --- a/src/WinRT.Runtime/Projections/Geometry.cs +++ b/src/WinRT.Runtime/Projections/Geometry.cs @@ -13,7 +13,10 @@ internal static class GSR } [global::WinRT.WindowsRuntimeType("Windows.Foundation.FoundationContract")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.Foundation.Point))] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.Foundation.Point))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif [StructLayout(LayoutKind.Sequential)] #if EMBED internal @@ -106,7 +109,10 @@ public override int GetHashCode() } [global::WinRT.WindowsRuntimeType("Windows.Foundation.FoundationContract")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.Foundation.Rect))] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.Foundation.Rect))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif [StructLayout(LayoutKind.Sequential)] #if EMBED internal @@ -431,7 +437,10 @@ public override int GetHashCode() } [global::WinRT.WindowsRuntimeType("Windows.Foundation.FoundationContract")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.Foundation.Size))] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.Foundation.Size))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif [StructLayout(LayoutKind.Sequential)] #if EMBED internal diff --git a/src/WinRT.Runtime/Projections/ICommand.net5.cs b/src/WinRT.Runtime/Projections/ICommand.net5.cs index 967ef03af..7ad7b6102 100644 --- a/src/WinRT.Runtime/Projections/ICommand.net5.cs +++ b/src/WinRT.Runtime/Projections/ICommand.net5.cs @@ -1,6 +1,8 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using ABI.System.ComponentModel; +using ABI.WinRT.Interop; using System; using System.ComponentModel; using System.Runtime.InteropServices; @@ -8,26 +10,86 @@ namespace ABI.System.Windows.Input { - [EditorBrowsable(EditorBrowsableState.Never)] - [Guid("E5AF3542-CA67-4081-995B-709DD13792DF")] - [DynamicInterfaceCastableImplementation] #if EMBED internal #else public #endif - unsafe interface ICommand : global::System.Windows.Input.ICommand + static class ICommandMethods + { + public static global::System.Guid IID => global::WinRT.Interop.IID.IID_ICommand; + + public static IntPtr AbiToProjectionVftablePtr => ICommand.Vftbl.AbiToProjectionVftablePtr; + + public static unsafe bool CanExecute(IObjectReference obj, object parameter) + { + var ThisPtr = obj.ThisPtr; + ObjectReferenceValue __parameter = default; + byte __retval = default; + try + { + __parameter = MarshalInspectable.CreateMarshaler2(parameter); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[8](ThisPtr, MarshalInspectable.GetAbi(__parameter), &__retval)); + GC.KeepAlive(obj); + return __retval != 0; + } + finally + { + MarshalInspectable.DisposeMarshaler(__parameter); + } + } + + public static unsafe void Execute(IObjectReference obj, object parameter) + { + var ThisPtr = obj.ThisPtr; + ObjectReferenceValue __parameter = default; + try + { + __parameter = MarshalInspectable.CreateMarshaler2(parameter); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[9](ThisPtr, MarshalInspectable.GetAbi(__parameter))); + GC.KeepAlive(obj); + } + finally + { + MarshalInspectable.DisposeMarshaler(__parameter); + } + } + + private volatile static global::System.Runtime.CompilerServices.ConditionalWeakTable _CanExecuteChanged; + private static global::System.Runtime.CompilerServices.ConditionalWeakTable MakeCanExecuteChangedTable() + { + global::System.Threading.Interlocked.CompareExchange(ref _CanExecuteChanged, new(), null); + return _CanExecuteChanged; + } + private static global::System.Runtime.CompilerServices.ConditionalWeakTable CanExecuteChanged => _CanExecuteChanged ?? MakeCanExecuteChangedTable(); + + public static unsafe EventHandlerEventSource Get_CanExecuteChanged2(IObjectReference obj, object thisObj) + { + return CanExecuteChanged.GetValue(thisObj, (key) => + { + return new EventHandlerEventSource(obj, 6); + }); + } + } + + // ICommand has the same IID for both Windows.UI.Xaml.Input.ICommand and Microsoft.UI.Xaml.Input.ICommand, so we can use one interface definition for both without marking it as a WUX/MUX type. + [EditorBrowsable(EditorBrowsableState.Never)] + [Guid("E5AF3542-CA67-4081-995B-709DD13792DF")] + [DynamicInterfaceCastableImplementation] + internal unsafe interface ICommand : global::System.Windows.Input.ICommand { [Guid("E5AF3542-CA67-4081-995B-709DD13792DF")] +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) public struct Vftbl +#pragma warning restore CA2257 { internal IInspectable.Vftbl IInspectableVftbl; private void* _add_CanExecuteChanged_0; - public delegate* unmanaged[Stdcall] add_CanExecuteChanged_0 { get => (delegate* unmanaged[Stdcall])_add_CanExecuteChanged_0; set => _add_CanExecuteChanged_0 = value; } + public delegate* unmanaged[Stdcall] add_CanExecuteChanged_0 { get => (delegate* unmanaged[Stdcall])_add_CanExecuteChanged_0; set => _add_CanExecuteChanged_0 = value; } private void* _remove_CanExecuteChanged_1; public delegate* unmanaged[Stdcall] remove_CanExecuteChanged_1 { get => (delegate* unmanaged[Stdcall])_remove_CanExecuteChanged_1; set => _remove_CanExecuteChanged_1 = value; } private void* _CanExecute_2; - public delegate* unmanaged[Stdcall] CanExecute_2 { get => (delegate* unmanaged[Stdcall])_CanExecute_2; set => _CanExecute_2 = value; } + public delegate* unmanaged[Stdcall] CanExecute_2 { get => (delegate* unmanaged[Stdcall])_CanExecute_2; set => _CanExecute_2 = value; } private void* _Execute_3; public delegate* unmanaged[Stdcall] Execute_3 { get => (delegate* unmanaged[Stdcall])_Execute_3; set => _Execute_3 = value; } @@ -40,15 +102,14 @@ static unsafe Vftbl() { IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - _add_CanExecuteChanged_0 = (delegate* unmanaged)&Do_Abi_add_CanExecuteChanged_0, + _add_CanExecuteChanged_0 = (delegate* unmanaged)&Do_Abi_add_CanExecuteChanged_0, _remove_CanExecuteChanged_1 = (delegate* unmanaged)&Do_Abi_remove_CanExecuteChanged_1, _CanExecute_2 = (delegate* unmanaged)&Do_Abi_CanExecute_2, _Execute_3 = (delegate* unmanaged)&Do_Abi_Execute_3, }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 4); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 4); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; } @@ -138,48 +199,24 @@ private static unsafe int Do_Abi_remove_CanExecuteChanged_1(IntPtr thisPtr, glob } } } - public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); + public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr, global::WinRT.Interop.IID.IID_ICommand); - private static EventHandlerEventSource _CanExecuteChanged(IWinRTObject _this) + private static global::ABI.WinRT.Interop.EventHandlerEventSource _CanExecuteChanged(IWinRTObject _this) { - var _obj = ((ObjectReference)((IWinRTObject)_this).GetObjectReferenceForType(typeof(global::System.Windows.Input.ICommand).TypeHandle)); - - return (EventHandlerEventSource)_this.GetOrCreateTypeHelperData(typeof(global::System.Windows.Input.ICommand).TypeHandle, - () => new EventHandlerEventSource(_obj, _obj.Vftbl.add_CanExecuteChanged_0, _obj.Vftbl.remove_CanExecuteChanged_1)); + var _obj = _this.GetObjectReferenceForType(typeof(global::System.Windows.Input.ICommand).TypeHandle); + return ICommandMethods.Get_CanExecuteChanged2(_obj, _this); } unsafe bool global::System.Windows.Input.ICommand.CanExecute(object parameter) { - ObjectReferenceValue __parameter = default; - byte __retval = default; - try - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Windows.Input.ICommand).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - __parameter = MarshalInspectable.CreateMarshaler2(parameter); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.CanExecute_2(ThisPtr, MarshalInspectable.GetAbi(__parameter), out __retval)); - return __retval != 0; - } - finally - { - MarshalInspectable.DisposeMarshaler(__parameter); - } + var obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Windows.Input.ICommand).TypeHandle); + return ICommandMethods.CanExecute(obj, parameter); } unsafe void global::System.Windows.Input.ICommand.Execute(object parameter) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Windows.Input.ICommand).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - ObjectReferenceValue __parameter = default; - try - { - __parameter = MarshalInspectable.CreateMarshaler2(parameter); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Execute_3(ThisPtr, MarshalInspectable.GetAbi(__parameter))); - } - finally - { - MarshalInspectable.DisposeMarshaler(__parameter); - } + var obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Windows.Input.ICommand).TypeHandle); + ICommandMethods.Execute(obj, parameter); } event global::System.EventHandler global::System.Windows.Input.ICommand.CanExecuteChanged diff --git a/src/WinRT.Runtime/Projections/ICommand.netstandard2.0.cs b/src/WinRT.Runtime/Projections/ICommand.netstandard2.0.cs index 543197c32..63506fc29 100644 --- a/src/WinRT.Runtime/Projections/ICommand.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/ICommand.netstandard2.0.cs @@ -5,6 +5,7 @@ using System.ComponentModel; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using ABI.WinRT.Interop; using WinRT; using WinRT.Interop; @@ -145,11 +146,7 @@ public ICommand(IObjectReference obj) : this(obj.As()) { } public ICommand(ObjectReference obj) { _obj = obj; - - _CanExecuteChanged = - new EventHandlerEventSource(_obj, - _obj.Vftbl.add_CanExecuteChanged_0, - _obj.Vftbl.remove_CanExecuteChanged_1); + _CanExecuteChanged = new EventHandlerEventSource(_obj, 6); } @@ -161,6 +158,7 @@ public unsafe bool CanExecute(object parameter) { __parameter = MarshalInspectable.CreateMarshaler2(parameter); global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.CanExecute_2(ThisPtr, MarshalInspectable.GetAbi(__parameter), out __retval)); + GC.KeepAlive(_obj); return __retval != 0; } finally @@ -176,6 +174,7 @@ public unsafe void Execute(object parameter) { __parameter = MarshalInspectable.CreateMarshaler2(parameter); global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Execute_3(ThisPtr, MarshalInspectable.GetAbi(__parameter))); + GC.KeepAlive(_obj); } finally { diff --git a/src/WinRT.Runtime/Projections/ICustomPropertyProvider.net5.cs b/src/WinRT.Runtime/Projections/ICustomPropertyProvider.net5.cs index ca33a3457..a2bfb9301 100644 --- a/src/WinRT.Runtime/Projections/ICustomPropertyProvider.net5.cs +++ b/src/WinRT.Runtime/Projections/ICustomPropertyProvider.net5.cs @@ -1,417 +1,539 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Reflection; -using System.Runtime.InteropServices; -using WinRT; - -namespace Microsoft.UI.Xaml.Data -{ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using WinRT; + +// These types have the same GUIDs in both Microsoft.UI.Xaml and Windows.UI.Xaml, +// so we don't need to duplicate them for the internal usage here as they can be transparently used by both WUX and MUX. +namespace Microsoft.UI.Xaml.Data +{ [global::WinRT.WindowsRuntimeType] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Data.ICustomProperty))] - [Guid("30DA92C0-23E8-42A0-AE7C-734A0E5D2782")] - interface ICustomProperty - { - object GetValue(object target); - void SetValue(object target, object value); - object GetIndexedValue(object target, object index); - void SetIndexedValue(object target, object value, object index); - bool CanRead { get; } - bool CanWrite { get; } - string Name { get; } - global::System.Type Type { get; } - } -} - -namespace ABI.Microsoft.UI.Xaml.Data -{ - [Guid("30DA92C0-23E8-42A0-AE7C-734A0E5D2782")] - internal sealed unsafe class ICustomProperty - { - [Guid("30DA92C0-23E8-42A0-AE7C-734A0E5D2782")] - public struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - public void* get_Type_0; - public void* get_Name_1; - public void* GetValue_2; - public void* SetValue_3; - public void* GetIndexedValue_4; - public void* SetIndexedValue_5; - public void* get_CanWrite_6; - public void* get_CanRead_7; - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - - get_Type_0 = (delegate* unmanaged)&Do_Abi_get_Type_0, - get_Name_1 = (delegate* unmanaged)&Do_Abi_get_Name_1, - GetValue_2 = (delegate* unmanaged)&Do_Abi_GetValue_2, - SetValue_3 = (delegate* unmanaged)&Do_Abi_SetValue_3, - GetIndexedValue_4 = (delegate* unmanaged)&Do_Abi_GetIndexedValue_4, - SetIndexedValue_5 = (delegate* unmanaged)&Do_Abi_SetIndexedValue_5, - get_CanWrite_6 = (delegate* unmanaged)&Do_Abi_get_CanWrite_6, - get_CanRead_7 = (delegate* unmanaged)&Do_Abi_get_CanRead_7 - - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 8); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetValue_2(IntPtr thisPtr, IntPtr target, IntPtr* result) - { - object __result = default; - - *result = default; - - try - { - __result = global::WinRT.ComWrappersSupport.FindObject(thisPtr).GetValue(MarshalInspectable.FromAbi(target)); - *result = MarshalInspectable.FromManaged(__result); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_SetValue_3(IntPtr thisPtr, IntPtr target, IntPtr value) - { - - - try - { - global::WinRT.ComWrappersSupport.FindObject(thisPtr).SetValue(MarshalInspectable.FromAbi(target), MarshalInspectable.FromAbi(value)); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetIndexedValue_4(IntPtr thisPtr, IntPtr target, IntPtr index, IntPtr* result) - { - object __result = default; - - try - { - __result = global::WinRT.ComWrappersSupport.FindObject(thisPtr).GetIndexedValue(MarshalInspectable.FromAbi(target), MarshalInspectable.FromAbi(index)); - *result = MarshalInspectable.FromManaged(__result); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_SetIndexedValue_5(IntPtr thisPtr, IntPtr target, IntPtr value, IntPtr index) - { - - - try - { - global::WinRT.ComWrappersSupport.FindObject(thisPtr).SetIndexedValue(MarshalInspectable.FromAbi(target), MarshalInspectable.FromAbi(value), MarshalInspectable.FromAbi(index)); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_get_CanRead_7(IntPtr thisPtr, byte* value) - { - bool __value = default; - - try - { - __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).CanRead; *value = (byte)(__value ? 1 : 0); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_get_CanWrite_6(IntPtr thisPtr, byte* value) - { - bool __value = default; - - try - { - __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).CanWrite; *value = (byte)(__value ? 1 : 0); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_get_Name_1(IntPtr thisPtr, IntPtr* value) - { - string __value = default; - - try - { - __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).Name; - *value = MarshalString.FromManaged(__value); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_get_Type_0(IntPtr thisPtr, global::ABI.System.Type* value) - { - global::System.Type __value = default; - - try - { - __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).Type; - *value = global::ABI.System.Type.FromManaged(__value); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - } - internal static class ICustomProperty_Delegates - { - public unsafe delegate int get_Type_0(IntPtr thisPtr, global::ABI.System.Type* value); - public unsafe delegate int get_Name_1(IntPtr thisPtr, IntPtr* value); - public unsafe delegate int GetValue_2(IntPtr thisPtr, IntPtr target, IntPtr* result); - public unsafe delegate int SetValue_3(IntPtr thisPtr, IntPtr target, IntPtr value); - public unsafe delegate int GetIndexedValue_4(IntPtr thisPtr, IntPtr target, IntPtr index, IntPtr* result); - public unsafe delegate int SetIndexedValue_5(IntPtr thisPtr, IntPtr target, IntPtr value, IntPtr index); - public unsafe delegate int get_CanWrite_6(IntPtr thisPtr, byte* value); - public unsafe delegate int get_CanRead_7(IntPtr thisPtr, byte* value); - } - - internal sealed class ManagedCustomProperty : global::Microsoft.UI.Xaml.Data.ICustomProperty - { - private readonly PropertyInfo _property; - - public ManagedCustomProperty(PropertyInfo property) - { - _property = property; - } - - public bool CanRead => _property.CanRead; - - public bool CanWrite => _property.CanWrite; - - public string Name => _property.Name; - - public Type Type => _property.PropertyType; - - public object GetIndexedValue(object target, object index) - { - return _property.GetValue(target, new[] { index }); - } - - public object GetValue(object target) - { - return _property.GetValue(target); - } - - public void SetIndexedValue(object target, object value, object index) - { - _property.SetValue(target, value, new[] { index }); - } - - public void SetValue(object target, object value) - { - _property.SetValue(target, value); - } - } - - [Guid("7C925755-3E48-42B4-8677-76372267033F")] - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct ManagedCustomPropertyProviderVftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* GetCustomProperty_0; - private void* GetIndexedProperty_1; - private void* GetStringRepresentation_2; - private void* get_Type_3; - - private static readonly ManagedCustomPropertyProviderVftbl AbiToProjectionVftable; + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Data.ICustomProperty))] + [Guid("30DA92C0-23E8-42A0-AE7C-734A0E5D2782")] + internal interface ICustomProperty + { + object GetValue(object target); + void SetValue(object target, object value); + object GetIndexedValue(object target, object index); + void SetIndexedValue(object target, object value, object index); + bool CanRead { get; } + bool CanWrite { get; } + string Name { get; } + global::System.Type Type { get; } + } + + /// + /// An interface complementing providing the implementation to expose the specified properties. + /// +#if EMBED + internal +#else + public +#endif + interface IBindableCustomPropertyImplementation + { + /// + /// Get the generated implementation representing the specified property name. + /// + /// The name of the property to get. + /// The implementation for the property specified by . + public Microsoft.UI.Xaml.Data.BindableCustomProperty GetProperty(string name); + + /// + /// Get the generated implementation representing the specified index property type. + /// + /// The index property to get. + /// The implementation for the property specified by . + public Microsoft.UI.Xaml.Data.BindableCustomProperty GetProperty(Type indexParameterType); + } + + /// + /// An implementation that relies on a source generation approach for its implememtation + /// rather than reflection. This is used by the source generator generating the implementation for . + /// + [global::WinRT.WinRTExposedType(typeof(global::ABI.Microsoft.UI.Xaml.Data.ManagedCustomPropertyWinRTTypeDetails))] +#if EMBED + internal +#else + public +#endif + sealed class BindableCustomProperty : ICustomProperty + { + private readonly bool _canRead; + private readonly bool _canWrite; + private readonly string _name; + private readonly Type _type; + private readonly Func _getValue; + private readonly Action _setValue; + private readonly Func _getIndexedValue; + private readonly Action _setIndexedValue; + + public BindableCustomProperty( + bool canRead, + bool canWrite, + string name, + Type type, + Func getValue, + Action setValue, + Func getIndexedValue, + Action setIndexedValue) + { + _canRead = canRead; + _canWrite = canWrite; + _name = name; + _type = type; + _getValue = getValue; + _setValue = setValue; + _getIndexedValue = getIndexedValue; + _setIndexedValue = setIndexedValue; + } + + bool ICustomProperty.CanRead => _canRead; + + bool ICustomProperty.CanWrite => _canWrite; + + string ICustomProperty.Name => _name; + + Type ICustomProperty.Type => _type; + + object ICustomProperty.GetIndexedValue(object target, object index) => _getIndexedValue != null ? _getIndexedValue(target, index) : throw new NotImplementedException(); + + object ICustomProperty.GetValue(object target) => _getValue != null ? _getValue(target) : throw new NotImplementedException(); + + void ICustomProperty.SetIndexedValue(object target, object value, object index) + { + if (_setIndexedValue == null) + { + throw new NotImplementedException(); + } + + _setIndexedValue(target, value, index); + } + + void ICustomProperty.SetValue(object target, object value) + { + if (_setValue == null) + { + throw new NotImplementedException(); + } + + _setValue(target, value); + } + } +} + +namespace ABI.Microsoft.UI.Xaml.Data +{ + [Guid("30DA92C0-23E8-42A0-AE7C-734A0E5D2782")] + internal unsafe interface ICustomProperty + { + public static readonly IntPtr AbiToProjectionVftablePtr; + public static global::System.Guid IID => global::WinRT.Interop.IID.IID_ICustomProperty; + + static unsafe ICustomProperty() + { + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(ICustomProperty), sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * 8); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((delegate* unmanaged*)AbiToProjectionVftablePtr)[6] = &Do_Abi_get_Type_0; + ((delegate* unmanaged*)AbiToProjectionVftablePtr)[7] = &Do_Abi_get_Name_1; + ((delegate* unmanaged*)AbiToProjectionVftablePtr)[8] = &Do_Abi_GetValue_2; + ((delegate* unmanaged*)AbiToProjectionVftablePtr)[9] = &Do_Abi_SetValue_3; + ((delegate* unmanaged*)AbiToProjectionVftablePtr)[10] = &Do_Abi_GetIndexedValue_4; + ((delegate* unmanaged*)AbiToProjectionVftablePtr)[11] = &Do_Abi_SetIndexedValue_5; + ((delegate* unmanaged*)AbiToProjectionVftablePtr)[12] = &Do_Abi_get_CanWrite_6; + ((delegate* unmanaged*)AbiToProjectionVftablePtr)[13] = &Do_Abi_get_CanRead_7; + } + + [UnmanagedCallersOnly] + private static unsafe int Do_Abi_GetValue_2(IntPtr thisPtr, IntPtr target, IntPtr* result) + { + object __result = default; + + *result = default; + + try + { + __result = global::WinRT.ComWrappersSupport.FindObject(thisPtr).GetValue(MarshalInspectable.FromAbi(target)); + *result = MarshalInspectable.FromManaged(__result); + + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + [UnmanagedCallersOnly] + private static unsafe int Do_Abi_SetValue_3(IntPtr thisPtr, IntPtr target, IntPtr value) + { + try + { + global::WinRT.ComWrappersSupport.FindObject(thisPtr).SetValue(MarshalInspectable.FromAbi(target), MarshalInspectable.FromAbi(value)); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + [UnmanagedCallersOnly] + private static unsafe int Do_Abi_GetIndexedValue_4(IntPtr thisPtr, IntPtr target, IntPtr index, IntPtr* result) + { + object __result = default; + + try + { + __result = global::WinRT.ComWrappersSupport.FindObject(thisPtr).GetIndexedValue(MarshalInspectable.FromAbi(target), MarshalInspectable.FromAbi(index)); + *result = MarshalInspectable.FromManaged(__result); + + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + [UnmanagedCallersOnly] + private static unsafe int Do_Abi_SetIndexedValue_5(IntPtr thisPtr, IntPtr target, IntPtr value, IntPtr index) + { + try + { + global::WinRT.ComWrappersSupport.FindObject(thisPtr).SetIndexedValue(MarshalInspectable.FromAbi(target), MarshalInspectable.FromAbi(value), MarshalInspectable.FromAbi(index)); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + [UnmanagedCallersOnly] + private static unsafe int Do_Abi_get_CanRead_7(IntPtr thisPtr, byte* value) + { + bool __value = default; + + try + { + __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).CanRead; *value = (byte)(__value ? 1 : 0); + + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + [UnmanagedCallersOnly] + private static unsafe int Do_Abi_get_CanWrite_6(IntPtr thisPtr, byte* value) + { + bool __value = default; + + try + { + __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).CanWrite; *value = (byte)(__value ? 1 : 0); + + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + [UnmanagedCallersOnly] + private static unsafe int Do_Abi_get_Name_1(IntPtr thisPtr, IntPtr* value) + { + string __value = default; + + try + { + __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).Name; + *value = MarshalString.FromManaged(__value); + + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + [UnmanagedCallersOnly] + private static unsafe int Do_Abi_get_Type_0(IntPtr thisPtr, global::ABI.System.Type* value) + { + global::System.Type __value = default; + + try + { + __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).Type; + *value = global::ABI.System.Type.FromManaged(__value); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + internal static class ICustomProperty_Delegates + { + public unsafe delegate int get_Type_0(IntPtr thisPtr, global::ABI.System.Type* value); + public unsafe delegate int get_Name_1(IntPtr thisPtr, IntPtr* value); + public unsafe delegate int GetValue_2(IntPtr thisPtr, IntPtr target, IntPtr* result); + public unsafe delegate int SetValue_3(IntPtr thisPtr, IntPtr target, IntPtr value); + public unsafe delegate int GetIndexedValue_4(IntPtr thisPtr, IntPtr target, IntPtr index, IntPtr* result); + public unsafe delegate int SetIndexedValue_5(IntPtr thisPtr, IntPtr target, IntPtr value, IntPtr index); + public unsafe delegate int get_CanWrite_6(IntPtr thisPtr, byte* value); + public unsafe delegate int get_CanRead_7(IntPtr thisPtr, byte* value); + } + + internal sealed class ManagedCustomPropertyWinRTTypeDetails : global::WinRT.IWinRTExposedTypeDetails + { + public ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + { + return new ComWrappers.ComInterfaceEntry[] + { + new ComWrappers.ComInterfaceEntry + { + IID = ICustomProperty.IID, + Vtable = ICustomProperty.AbiToProjectionVftablePtr + }, + }; + } + } + + [global::WinRT.WinRTExposedType(typeof(ManagedCustomPropertyWinRTTypeDetails))] + internal sealed class ManagedCustomProperty : global::Microsoft.UI.Xaml.Data.ICustomProperty + { + private readonly PropertyInfo _property; + + public ManagedCustomProperty(PropertyInfo property) + { + _property = property; + } + + public bool CanRead => _property.CanRead; + + public bool CanWrite => _property.CanWrite; + + public string Name => _property.Name; + + public Type Type => _property.PropertyType; + + public object GetIndexedValue(object target, object index) + { + return _property.GetValue(target, new[] { index }); + } + + public object GetValue(object target) + { + return _property.GetValue(target); + } + + public void SetIndexedValue(object target, object value, object index) + { + _property.SetValue(target, value, new[] { index }); + } + + public void SetValue(object target, object value) + { + _property.SetValue(target, value); + } + } + + [Guid("7C925755-3E48-42B4-8677-76372267033F")] + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct ManagedCustomPropertyProviderVftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* GetCustomProperty_0; + private void* GetIndexedProperty_1; + private void* GetStringRepresentation_2; + private void* get_Type_3; + + private static readonly ManagedCustomPropertyProviderVftbl AbiToProjectionVftable; public static readonly IntPtr AbiToProjectionVftablePtr; - internal static readonly Guid IID = new(0x7C925755, 0x3E48, 0x42B4, 0x86, 0x77, 0x76, 0x37, 0x22, 0x67, 0x03, 0x3F); - - static unsafe ManagedCustomPropertyProviderVftbl() - { - AbiToProjectionVftable = new ManagedCustomPropertyProviderVftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - - GetCustomProperty_0 = (delegate* unmanaged)&Do_Abi_GetCustomProperty_0, - GetIndexedProperty_1 = (delegate* unmanaged)&Do_Abi_GetIndexedProperty_1, - GetStringRepresentation_2 = (delegate* unmanaged)&Do_Abi_GetStringRepresentation_2, - get_Type_3 = (delegate* unmanaged)&Do_Abi_get_Type_3 - - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(ManagedCustomPropertyProviderVftbl), Marshal.SizeOf() + sizeof(IntPtr) * 4); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetCustomProperty_0(IntPtr thisPtr, IntPtr name, IntPtr* result) - { - global::Microsoft.UI.Xaml.Data.ICustomProperty __result = default; - try - { - string _name = MarshalString.FromAbi(name); - object target = global::WinRT.ComWrappersSupport.FindObject(thisPtr); - PropertyInfo propertyInfo = target.GetType().GetProperty( - _name, - BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); - - if (propertyInfo is object) - { - __result = new ManagedCustomProperty(propertyInfo); - } - - *result = MarshalInterface.FromManaged(__result); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetIndexedProperty_1(IntPtr thisPtr, IntPtr name, global::ABI.System.Type type, IntPtr* result) - { - global::Microsoft.UI.Xaml.Data.ICustomProperty __result = default; - try - { - string _name = MarshalString.FromAbi(name); - object target = global::WinRT.ComWrappersSupport.FindObject(thisPtr); - PropertyInfo propertyInfo = target.GetType().GetProperty( - _name, - BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public, - null, // default binder - null, // ignore return type - new Type[] { global::ABI.System.Type.FromAbi(type) }, // indexed parameter type - null // ignore type modifier - ); - - if (propertyInfo is object) - { - __result = new ManagedCustomProperty(propertyInfo); - } - - *result = MarshalInterface.FromManaged(__result); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetStringRepresentation_2(IntPtr thisPtr, IntPtr* result) - { - string __result = default; - try - { - __result = global::WinRT.ComWrappersSupport.FindObject(thisPtr).ToString(); - *result = MarshalString.FromManaged(__result); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_get_Type_3(IntPtr thisPtr, global::ABI.System.Type* value) - { - global::System.Type __value = default; - try - { - __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).GetType(); - *value = global::ABI.System.Type.FromManaged(__value); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - internal static class ICustomPropertyProvider_Delegates - { - public unsafe delegate int GetCustomProperty_0(IntPtr thisPtr, IntPtr name, IntPtr* result); - public unsafe delegate int GetIndexedProperty_1(IntPtr thisPtr, IntPtr name, global::ABI.System.Type type, IntPtr* result); - public unsafe delegate int GetStringRepresentation_2(IntPtr thisPtr, IntPtr* result); - public unsafe delegate int get_Type_3(IntPtr thisPtr, global::ABI.System.Type* value); - } - -} + static unsafe ManagedCustomPropertyProviderVftbl() + { + AbiToProjectionVftable = new ManagedCustomPropertyProviderVftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, + + GetCustomProperty_0 = (delegate* unmanaged)&Do_Abi_GetCustomProperty_0, + GetIndexedProperty_1 = (delegate* unmanaged)&Do_Abi_GetIndexedProperty_1, + GetStringRepresentation_2 = (delegate* unmanaged)&Do_Abi_GetStringRepresentation_2, + get_Type_3 = (delegate* unmanaged)&Do_Abi_get_Type_3 + + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(ManagedCustomPropertyProviderVftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 4); + *(ManagedCustomPropertyProviderVftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + + + [UnmanagedCallersOnly] + private static unsafe int Do_Abi_GetCustomProperty_0(IntPtr thisPtr, IntPtr name, IntPtr* result) + { + global::Microsoft.UI.Xaml.Data.ICustomProperty __result = default; + try + { + string _name = MarshalString.FromAbi(name); + object target = global::WinRT.ComWrappersSupport.FindObject(thisPtr); + + if (target is global::Microsoft.UI.Xaml.Data.IBindableCustomPropertyImplementation bindableCustomPropertyImplementation) + { + __result = bindableCustomPropertyImplementation.GetProperty(_name); + *result = MarshalInterface.FromManaged(__result); + return 0; + } + + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException( + $"ICustomProperty support used by XAML binding for type '{target.GetType()}' (property '{_name}') requires the type to marked with 'WinRT.GeneratedBindableCustomPropertyAttribute'. " + + $"If this is a built-in type or a type that can't be marked, a wrapper type should be used around it that is marked to enable this support."); + } + + GetCustomPropertyForJit(target, _name, result); + + [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Fallback method for JIT environments that is not trim-safe by design.")] + [MethodImpl(MethodImplOptions.NoInlining)] + static void GetCustomPropertyForJit(object target, string name, IntPtr* result) + { + global::Microsoft.UI.Xaml.Data.ICustomProperty __result = default; + + PropertyInfo propertyInfo = target.GetType().GetProperty( + name, + BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public); + + if (propertyInfo is not null) + { + __result = new ManagedCustomProperty(propertyInfo); + } + + *result = MarshalInterface.FromManaged(__result); + } + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + [UnmanagedCallersOnly] + private static unsafe int Do_Abi_GetIndexedProperty_1(IntPtr thisPtr, IntPtr name, global::ABI.System.Type type, IntPtr* result) + { + global::Microsoft.UI.Xaml.Data.ICustomProperty __result = default; + try + { + Type _type = global::ABI.System.Type.FromAbi(type); + + object target = global::WinRT.ComWrappersSupport.FindObject(thisPtr); + + if (target is global::Microsoft.UI.Xaml.Data.IBindableCustomPropertyImplementation bindableCustomPropertyImplementation) + { + __result = bindableCustomPropertyImplementation.GetProperty(_type); + *result = MarshalInterface.FromManaged(__result); + return 0; + } + + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException( + $"ICustomProperty support used by XAML binding for type '{target.GetType()}' (indexer with parameter of type '{_type}') requires the type to marked with 'WinRT.GeneratedBindableCustomPropertyAttribute'. " + + $"If this is a built-in type or a type that can't be marked, a wrapper type should be used around it that is marked to enable this support."); + } + + // Intentionally declare this here to avoid marshalling this value entirely on AOT, + // as it's not needed. The indexer property is just matched by the parameter type. + string _name = MarshalString.FromAbi(name); + + GetCustomPropertyForJit(target, _name, _type, result); + + [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "Fallback method for JIT environments that is not trim-safe by design.")] + [MethodImpl(MethodImplOptions.NoInlining)] + static void GetCustomPropertyForJit(object target, string name, Type type, IntPtr* result) + { + global::Microsoft.UI.Xaml.Data.ICustomProperty __result = default; + + PropertyInfo propertyInfo = target.GetType().GetProperty( + name, + BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public, + null, // default binder + null, // ignore return type + new Type[] { type }, // indexed parameter type + null // ignore type modifier + ); + + if (propertyInfo is not null) + { + __result = new ManagedCustomProperty(propertyInfo); + } + + *result = MarshalInterface.FromManaged(__result); + } + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + [UnmanagedCallersOnly] + private static unsafe int Do_Abi_GetStringRepresentation_2(IntPtr thisPtr, IntPtr* result) + { + string __result = default; + try + { + __result = global::WinRT.ComWrappersSupport.FindObject(thisPtr).ToString(); + *result = MarshalString.FromManaged(__result); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + [UnmanagedCallersOnly] + private static unsafe int Do_Abi_get_Type_3(IntPtr thisPtr, global::ABI.System.Type* value) + { + global::System.Type __value = default; + try + { + __value = global::WinRT.ComWrappersSupport.FindObject(thisPtr).GetType(); + *value = global::ABI.System.Type.FromManaged(__value); + + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } +} diff --git a/src/WinRT.Runtime/Projections/IDictionary.net5.cs b/src/WinRT.Runtime/Projections/IDictionary.net5.cs index 74509f9ac..afdd8581e 100644 --- a/src/WinRT.Runtime/Projections/IDictionary.net5.cs +++ b/src/WinRT.Runtime/Projections/IDictionary.net5.cs @@ -1,1101 +1,1494 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.InteropServices; -using WinRT; -using WinRT.Interop; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -#pragma warning disable 0169 // warning CS0169: The field '...' is never used -#pragma warning disable 0649 // warning CS0169: Field '...' is never assigned to - -namespace Windows.Foundation.Collections -{ - //Need to rethink how to name/define this interface - [Guid("3C2925FE-8519-45C1-AA79-197B6718C1C1")] - interface IMap : IIterable> - { - V Lookup(K key); - bool HasKey(K key); - IReadOnlyDictionary GetView(); // Combining IMap & IReadOnlyDictionary needs redesign - bool Insert(K key, V value); - void _Remove(K key); - void Clear(); - uint Size { get; } - } -} - -namespace System.Collections.Generic -{ - - internal sealed class IDictionaryImpl : IDictionary, IWinRTObject - { - private IObjectReference _inner; - private Dictionary _lookupCache; - - internal IDictionaryImpl(IObjectReference _inner) - { - this._inner = _inner; - this._lookupCache = new Dictionary(); - } - - public static IDictionaryImpl CreateRcw(IInspectable obj) => new(obj.ObjRef); - - private volatile IObjectReference __iDictionaryObjRef; - private IObjectReference Make_IDictionaryObjRef() - { - global::System.Threading.Interlocked.CompareExchange(ref __iDictionaryObjRef, _inner.As.Vftbl>(), null); - return __iDictionaryObjRef; - } - private IObjectReference iDictionaryObjRef => __iDictionaryObjRef ?? Make_IDictionaryObjRef(); - - private volatile IObjectReference __iEnumerableObjRef; - private IObjectReference Make_IEnumerableObjRef() - { - global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, _inner.As>.Vftbl>(), null); - return __iEnumerableObjRef; - } - private IObjectReference iEnumerableObjRef => __iEnumerableObjRef ?? Make_IEnumerableObjRef(); - - IObjectReference IWinRTObject.NativeObject => _inner; - - bool IWinRTObject.HasUnwrappableNativeObject => true; - - private volatile global::System.Collections.Concurrent.ConcurrentDictionary _queryInterfaceCache; - private global::System.Collections.Concurrent.ConcurrentDictionary MakeQueryInterfaceCache() - { - global::System.Threading.Interlocked.CompareExchange(ref _queryInterfaceCache, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); - return _queryInterfaceCache; - } - global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.QueryInterfaceCache => _queryInterfaceCache ?? MakeQueryInterfaceCache(); - private volatile global::System.Collections.Concurrent.ConcurrentDictionary _additionalTypeData; - private global::System.Collections.Concurrent.ConcurrentDictionary MakeAdditionalTypeData() - { - global::System.Threading.Interlocked.CompareExchange(ref _additionalTypeData, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); - return _additionalTypeData; - } - global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.AdditionalTypeData => _additionalTypeData ?? MakeAdditionalTypeData(); - - public V this[K key] - { - get => ABI.System.Collections.Generic.IDictionaryMethods.Indexer_Get(iDictionaryObjRef, _lookupCache, key); - set => ABI.System.Collections.Generic.IDictionaryMethods.Indexer_Set(iDictionaryObjRef, key, value); - } - - public ICollection Keys => ABI.System.Collections.Generic.IDictionaryMethods.get_Keys(iDictionaryObjRef); - - public ICollection Values => ABI.System.Collections.Generic.IDictionaryMethods.get_Values(iDictionaryObjRef); - - public int Count => ABI.System.Collections.Generic.IDictionaryMethods.get_Count(iDictionaryObjRef); - - public bool IsReadOnly => ABI.System.Collections.Generic.IDictionaryMethods.get_IsReadOnly(iDictionaryObjRef); - - public void Add(K key, V value) - { - ABI.System.Collections.Generic.IDictionaryMethods.Add(iDictionaryObjRef, key, value); - } - - public void Add(KeyValuePair item) - { - ABI.System.Collections.Generic.IDictionaryMethods.Add(iDictionaryObjRef, item); - } - - public void Clear() - { - ABI.System.Collections.Generic.IDictionaryMethods.Clear(iDictionaryObjRef); - } - - public bool Contains(KeyValuePair item) - { - return ABI.System.Collections.Generic.IDictionaryMethods.Contains(iDictionaryObjRef, _lookupCache, item); - } - - public bool ContainsKey(K key) - { - return ABI.System.Collections.Generic.IDictionaryMethods.ContainsKey(iDictionaryObjRef, key); - } - - public void CopyTo(KeyValuePair[] array, int arrayIndex) - { - ABI.System.Collections.Generic.IDictionaryMethods.CopyTo(iDictionaryObjRef, iEnumerableObjRef, array, arrayIndex); - } - - public IEnumerator> GetEnumerator() - { - return ABI.System.Collections.Generic.IEnumerableMethods>.GetEnumerator(iEnumerableObjRef); - } - - public bool Remove(K key) - { - return ABI.System.Collections.Generic.IDictionaryMethods.Remove(iDictionaryObjRef, key); - } - - public bool Remove(KeyValuePair item) - { - return ABI.System.Collections.Generic.IDictionaryMethods.Remove(iDictionaryObjRef, item); - } - - public bool TryGetValue(K key, [MaybeNullWhen(false)] out V value) - { - return ABI.System.Collections.Generic.IDictionaryMethods.TryGetValue(iDictionaryObjRef, _lookupCache, key, out value); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} - -namespace ABI.Windows.Foundation.Collections -{ - using global::System; - using global::System.Runtime.CompilerServices; - using ABI.System.Collections.Generic; - - internal static class IMapMethods - { - public static unsafe V Lookup(IObjectReference obj, Dictionary __lookupCache, K key) - { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - object __key = default; - var __params = new object[] { ThisPtr, null, null }; - try - { - __key = Marshaler.CreateMarshaler2(key); - __params[1] = Marshaler.GetAbi(__key); - _obj.Vftbl.Lookup_0.DynamicInvokeAbi(__params); - - if (__lookupCache != null && __lookupCache.TryGetValue(key, out var __cachedRcw) && __cachedRcw.Item1 == (IntPtr)__params[2]) - { - return __cachedRcw.Item2; - } - else - { - var value = Marshaler.FromAbi(__params[2]); - if (__lookupCache != null) - { - __lookupCache[key] = ((IntPtr)__params[2], value); - } - return value; - } - } - finally - { - Marshaler.DisposeMarshaler(__key); - Marshaler.DisposeAbi(__params[2]); - } - } - - public static unsafe bool HasKey(IObjectReference obj, K key) - { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - object __key = default; - var __params = new object[] { ThisPtr, null, null }; - try - { - __key = Marshaler.CreateMarshaler2(key); - __params[1] = Marshaler.GetAbi(__key); - _obj.Vftbl.HasKey_2.DynamicInvokeAbi(__params); - return (byte)__params[2] != 0; - } - finally - { - Marshaler.DisposeMarshaler(__key); - } - } - - public static unsafe global::System.Collections.Generic.IReadOnlyDictionary GetView(IObjectReference obj) - { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - IntPtr __retval = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetView_3(ThisPtr, out __retval)); - return MarshalInterface>.FromAbi(__retval); - } - finally - { - MarshalInterface>.DisposeAbi(__retval); - } - } - - public static unsafe bool Insert(IObjectReference obj, K key, V value) - { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - object __key = default; - object __value = default; - var __params = new object[] { ThisPtr, null, null, null }; - try - { - __key = Marshaler.CreateMarshaler2(key); - __params[1] = Marshaler.GetAbi(__key); - __value = Marshaler.CreateMarshaler2(value); - __params[2] = Marshaler.GetAbi(__value); - _obj.Vftbl.Insert_4.DynamicInvokeAbi(__params); - return (byte)__params[3] != 0; - } - finally - { - Marshaler.DisposeMarshaler(__key); - Marshaler.DisposeMarshaler(__value); - } - } - - public static unsafe void Remove(IObjectReference obj, K key) - { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - object __key = default; - var __params = new object[] { ThisPtr, null }; - try - { - __key = Marshaler.CreateMarshaler2(key); - __params[1] = Marshaler.GetAbi(__key); - _obj.Vftbl.Remove_5.DynamicInvokeAbi(__params); - } - finally - { - Marshaler.DisposeMarshaler(__key); - } - } - - public static void Clear(IObjectReference obj) - { - _ClearHelper(obj); - } - - private static unsafe void _ClearHelper(IObjectReference obj) - { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Clear_6(ThisPtr)); - } - - public static unsafe uint get_Size(IObjectReference obj) - { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - uint __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetSize_1(ThisPtr, out __retval)); - return __retval; - } - } - -} - -namespace ABI.System.Collections.Generic -{ - using ABI.Windows.Foundation.Collections; - -#if EMBED - internal -#else - public -#endif - static class IDictionaryMethods - { - public static int get_Count(IObjectReference obj) - { - uint size = IMapMethods.get_Size(obj); - if (((uint)int.MaxValue) < size) - { - throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_CollectionBackingDictionaryTooLarge); - } - return (int)size; - } - - public static bool get_IsReadOnly(IObjectReference _) => false; - - public static void Add(IObjectReference obj, global::System.Collections.Generic.KeyValuePair item) - { - IMapMethods.Insert(obj, item.Key, item.Value); - } - - public static void Clear(IObjectReference obj) - { - IMapMethods.Clear(obj); - } - - public static bool Contains(IObjectReference obj, Dictionary __lookupCache, global::System.Collections.Generic.KeyValuePair item) - { - bool hasKey = IMapMethods.HasKey(obj, item.Key); - if (!hasKey) - return false; - // todo: toctou - V value = IMapMethods.Lookup(obj, __lookupCache, item.Key); - return EqualityComparer.Default.Equals(value, item.Value); - } - - public static void CopyTo(IObjectReference obj, IObjectReference iEnumerableObjRef, global::System.Collections.Generic.KeyValuePair[] array, int arrayIndex) - { - if (array == null) - throw new ArgumentNullException(nameof(array)); - - if (arrayIndex < 0) - throw new ArgumentOutOfRangeException(nameof(arrayIndex)); - - if (array.Length <= arrayIndex && get_Count(obj) > 0) - throw new ArgumentException(WinRTRuntimeErrorStrings.Argument_IndexOutOfArrayBounds); - - if (array.Length - arrayIndex < get_Count(obj)) - throw new ArgumentException(WinRTRuntimeErrorStrings.Argument_InsufficientSpaceToCopyCollection); - - foreach (global::System.Collections.Generic.KeyValuePair mapping in (new IEnumerableImpl>(iEnumerableObjRef))) - { - array[arrayIndex++] = mapping; - } - } - - public static bool Remove(IObjectReference obj, global::System.Collections.Generic.KeyValuePair item) - { - IMapMethods.Remove(obj, item.Key); - return true; - } - - public static V Indexer_Get(IObjectReference obj, Dictionary __lookupCache, K key) - { - if (key == null) - throw new ArgumentNullException(nameof(key)); - return Lookup(obj, __lookupCache, key); - } - - public static void Indexer_Set(IObjectReference obj, K key, V value) - { - if (key == null) - throw new ArgumentNullException(nameof(key)); - Insert(obj, key, value); - } - - public static global::System.Collections.Generic.ICollection get_Keys(IObjectReference obj) => new DictionaryKeyCollection(obj); - - public static global::System.Collections.Generic.ICollection get_Values(IObjectReference obj) => new DictionaryValueCollection(obj); - - public static bool ContainsKey(IObjectReference obj, K key) - { - if (key == null) - throw new ArgumentNullException(nameof(key)); - return IMapMethods.HasKey(obj, key); - } - - public static void Add(IObjectReference obj, K key, V value) - { - if (key == null) - throw new ArgumentNullException(nameof(key)); - - if (ContainsKey(obj, key)) - throw new ArgumentException(WinRTRuntimeErrorStrings.Argument_AddingDuplicate); - - Insert(obj, key, value); - } - - public static bool Remove(IObjectReference obj, K key) - { - if (key == null) - throw new ArgumentNullException(nameof(key)); - - if (!IMapMethods.HasKey(obj, key)) - return false; - - try - { - IMapMethods.Remove(obj, key); - return true; - } - catch (global::System.Exception ex) - { - if (ExceptionHelpers.E_BOUNDS == ex.HResult) - return false; - - throw; - } - } - - public static bool TryGetValue(IObjectReference obj, Dictionary __lookupCache, K key, out V value) - { - if (key == null) - throw new ArgumentNullException(nameof(key)); - - if (!IMapMethods.HasKey(obj, key)) - { - value = default!; - return false; - } - - try - { - value = Lookup(obj, __lookupCache, key); - return true; - } - catch (KeyNotFoundException) - { - value = default!; - return false; - } - } - - private static V Lookup(IObjectReference obj, Dictionary __lookupCache, K key) - { - Debug.Assert(null != key); - - try - { - return IMapMethods.Lookup(obj, __lookupCache, key); - } - catch (global::System.Exception ex) - { - if (ExceptionHelpers.E_BOUNDS == ex.HResult) - throw new KeyNotFoundException(WinRTRuntimeErrorStrings.Arg_KeyNotFound); - throw; - } - } - - private static bool Insert(IObjectReference obj, K key, V value) - { - Debug.Assert(null != key); - - bool replaced = IMapMethods.Insert(obj, key, value); - return replaced; - } - - private sealed class DictionaryKeyCollection : global::System.Collections.Generic.ICollection - { - private readonly IObjectReference iDictionaryObjRef; - - public DictionaryKeyCollection(IObjectReference iDictionaryObjRef) - { - if (iDictionaryObjRef == null) - throw new ArgumentNullException(nameof(iDictionaryObjRef)); - - this.iDictionaryObjRef = iDictionaryObjRef; - } - - private volatile IObjectReference __iEnumerableObjRef; - private IObjectReference Make_IEnumerableObjRef() - { - global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, iDictionaryObjRef.As>.Vftbl>(), null); - return __iEnumerableObjRef; - } - private IObjectReference iEnumerableObjRef => __iEnumerableObjRef ?? Make_IEnumerableObjRef(); - - public void CopyTo(K[] array, int index) - { - if (array == null) - throw new ArgumentNullException(nameof(array)); - if (index < 0) - throw new ArgumentOutOfRangeException(nameof(index)); - if (array.Length <= index && this.Count > 0) - throw new ArgumentException(WinRTRuntimeErrorStrings.Arg_IndexOutOfRangeException); - if (array.Length - index < IDictionaryMethods.get_Count(iDictionaryObjRef)) - throw new ArgumentException(WinRTRuntimeErrorStrings.Argument_InsufficientSpaceToCopyCollection); - - int i = index; - foreach (global::System.Collections.Generic.KeyValuePair mapping in (new IEnumerableImpl>(iEnumerableObjRef))) - { - array[i++] = mapping.Key; - } - } - - public int Count => IDictionaryMethods.get_Count(iDictionaryObjRef); - - public bool IsReadOnly => true; - - void global::System.Collections.Generic.ICollection.Add(K item) - { - throw new NotSupportedException(WinRTRuntimeErrorStrings.NotSupported_KeyCollectionSet); - } - - void global::System.Collections.Generic.ICollection.Clear() - { - throw new NotSupportedException(WinRTRuntimeErrorStrings.NotSupported_KeyCollectionSet); - } - - public bool Contains(K item) - { - return IDictionaryMethods.ContainsKey(iDictionaryObjRef, item); - } - - bool global::System.Collections.Generic.ICollection.Remove(K item) - { - throw new NotSupportedException(WinRTRuntimeErrorStrings.NotSupported_KeyCollectionSet); - } - - global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); - - public global::System.Collections.Generic.IEnumerator GetEnumerator() => - new DictionaryKeyEnumerator(iEnumerableObjRef); - - private sealed class DictionaryKeyEnumerator : global::System.Collections.Generic.IEnumerator - { - private readonly IObjectReference iEnumerableObjRef; - private global::System.Collections.Generic.IEnumerator> enumeration; - - public DictionaryKeyEnumerator(IObjectReference iEnumerableObjRef) - { - this.iEnumerableObjRef = iEnumerableObjRef; - enumeration = IEnumerableMethods>.GetEnumerator(iEnumerableObjRef); - } - - public void Dispose() - { - enumeration.Dispose(); - } - - public bool MoveNext() - { - return enumeration.MoveNext(); - } - - object IEnumerator.Current => Current; - - public K Current => enumeration.Current.Key; - - public void Reset() - { - enumeration = IEnumerableMethods>.GetEnumerator(iEnumerableObjRef); - } - } - } - - private sealed class DictionaryValueCollection : global::System.Collections.Generic.ICollection - { - private readonly IObjectReference iDictionaryObjRef; - - public DictionaryValueCollection(IObjectReference iDictionaryObjRef) - { - this.iDictionaryObjRef = iDictionaryObjRef; - } - - private volatile IObjectReference __iEnumerableObjRef; - private IObjectReference Make_IEnumerableObjRef() - { - global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, iDictionaryObjRef.As>.Vftbl>(), null); - return __iEnumerableObjRef; - } - private IObjectReference iEnumerableObjRef => __iEnumerableObjRef ?? Make_IEnumerableObjRef(); - - public void CopyTo(V[] array, int index) - { - if (array == null) - throw new ArgumentNullException(nameof(array)); - if (index < 0) - throw new ArgumentOutOfRangeException(nameof(index)); - if (array.Length <= index && this.Count > 0) - throw new ArgumentException(WinRTRuntimeErrorStrings.Arg_IndexOutOfRangeException); - if (array.Length - index < IDictionaryMethods.get_Count(iDictionaryObjRef)) - throw new ArgumentException(WinRTRuntimeErrorStrings.Argument_InsufficientSpaceToCopyCollection); - - int i = index; - foreach (global::System.Collections.Generic.KeyValuePair mapping in (new IEnumerableImpl>(iEnumerableObjRef))) - { - array[i++] = mapping.Value; - } - } - - public int Count => IDictionaryMethods.get_Count(iDictionaryObjRef); - - public bool IsReadOnly => true; - - void global::System.Collections.Generic.ICollection.Add(V item) - { - throw new NotSupportedException(WinRTRuntimeErrorStrings.NotSupported_ValueCollectionSet); - } - - void global::System.Collections.Generic.ICollection.Clear() - { - throw new NotSupportedException(WinRTRuntimeErrorStrings.NotSupported_ValueCollectionSet); - } - - public bool Contains(V item) - { - EqualityComparer comparer = EqualityComparer.Default; - foreach (V value in this) - if (comparer.Equals(item, value)) - return true; - return false; - } - - bool global::System.Collections.Generic.ICollection.Remove(V item) - { - throw new NotSupportedException(WinRTRuntimeErrorStrings.NotSupported_ValueCollectionSet); - } - - IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); - - public global::System.Collections.Generic.IEnumerator GetEnumerator() - { - return new DictionaryValueEnumerator(iEnumerableObjRef); - } - - private sealed class DictionaryValueEnumerator : global::System.Collections.Generic.IEnumerator - { - private readonly IObjectReference iEnumerableObjRef; - private global::System.Collections.Generic.IEnumerator> enumeration; - - public DictionaryValueEnumerator(IObjectReference iEnumerableObjRef) - { - this.iEnumerableObjRef = iEnumerableObjRef; - enumeration = IEnumerableMethods>.GetEnumerator(iEnumerableObjRef); - } - - public void Dispose() - { - enumeration.Dispose(); - } - - public bool MoveNext() - { - return enumeration.MoveNext(); - } - - object IEnumerator.Current => Current; - - public V Current => enumeration.Current.Value; - - public void Reset() - { - enumeration = IEnumerableMethods>.GetEnumerator(iEnumerableObjRef); - } - } - } - } - -} - -namespace ABI.System.Collections.Generic -{ - using global::System; - using global::System.Runtime.CompilerServices; - - //This interface does not need to implement IMapView. Needs to be refactored - [DynamicInterfaceCastableImplementation] - [Guid("3C2925FE-8519-45C1-AA79-197B6718C1C1")] - interface IDictionary : global::System.Collections.Generic.IDictionary - { - public static IObjectReference CreateMarshaler(global::System.Collections.Generic.IDictionary obj) => - obj is null ? null : ComWrappersSupport.CreateCCWForObject(obj, PIID); - - public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.Generic.IDictionary obj) => - ComWrappersSupport.CreateCCWForObjectForMarshaling(obj, PIID); - - public static IntPtr GetAbi(IObjectReference objRef) => - objRef?.ThisPtr ?? IntPtr.Zero; - - public static global::System.Collections.Generic.IDictionary FromAbi(IntPtr thisPtr) => - thisPtr == IntPtr.Zero ? null : (global::System.Collections.Generic.IDictionary)(object)new IInspectable(ObjRefFromAbi(thisPtr)); - - public static IntPtr FromManaged(global::System.Collections.Generic.IDictionary value) => - (value is null) ? IntPtr.Zero : CreateMarshaler2(value).Detach(); - - public static void DisposeMarshaler(IObjectReference objRef) => objRef?.Dispose(); - - public static void DisposeAbi(IntPtr abi) => - MarshalInterfaceHelper>.DisposeAbi(abi); - - public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IDictionary)); - - public sealed class ToAbiHelper : global::Windows.Foundation.Collections.IMap - { - private readonly global::System.Collections.Generic.IDictionary _dictionary; - - public ToAbiHelper(global::System.Collections.Generic.IDictionary dictionary) => _dictionary = dictionary; - - global::System.Collections.Generic.IEnumerator> global::Windows.Foundation.Collections.IIterable>.First() => - new KeyValuePair.Enumerator(_dictionary.GetEnumerator()); - - public V Lookup(K key) - { - V value; - bool keyFound = _dictionary.TryGetValue(key, out value); - - if (!keyFound) - { - Debug.Assert(key != null); - Exception e = new KeyNotFoundException(String.Format(WinRTRuntimeErrorStrings.Arg_KeyNotFoundWithKey, key.ToString())); - e.SetHResult(ExceptionHelpers.E_BOUNDS); - throw e; - } - - return value; - } - - public uint Size { get => (uint)_dictionary.Count; } - - public bool HasKey(K key) => _dictionary.ContainsKey(key); - - global::System.Collections.Generic.IReadOnlyDictionary global::Windows.Foundation.Collections.IMap.GetView() - { - if (!(_dictionary is global::System.Collections.Generic.IReadOnlyDictionary roDictionary)) - { - roDictionary = new ReadOnlyDictionary(_dictionary); - } - return roDictionary; - } - - public bool Insert(K key, V value) - { - bool replacing = _dictionary.ContainsKey(key); - _dictionary[key] = value; - return replacing; - } - - public void _Remove(K key) - { - bool removed = _dictionary.Remove(key); - - if (!removed) - { - Debug.Assert(key != null); - Exception e = new KeyNotFoundException(String.Format(WinRTRuntimeErrorStrings.Arg_KeyNotFoundWithKey, key.ToString())); - e.SetHResult(ExceptionHelpers.E_BOUNDS); - throw e; - } - } - - public void Clear() => _dictionary.Clear(); - } - - [Guid("3C2925FE-8519-45C1-AA79-197B6718C1C1")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - public global::System.Delegate Lookup_0; - private void* _get_Size_1; - internal delegate* unmanaged[Stdcall] GetSize_1 { get => (delegate* unmanaged[Stdcall])_get_Size_1; set => _get_Size_1 = (void*)value; } - public global::System.Delegate HasKey_2; - private void* _getView_3; - public delegate* unmanaged[Stdcall] GetView_3 { get => (delegate* unmanaged[Stdcall])_getView_3; set => _getView_3 = (void*)value; } - public global::System.Delegate Insert_4; - public global::System.Delegate Remove_5; - private void* _clear_6; - public delegate* unmanaged[Stdcall] Clear_6 { get => (delegate* unmanaged[Stdcall])_clear_6; set => _clear_6 = (void*)value; } - public static Guid PIID = GuidGenerator.CreateIID(typeof(IDictionary)); - private static readonly Type Lookup_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, Marshaler.AbiType.MakeByRefType(), typeof(int) }); - private static readonly Type HasKey_2_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(byte).MakeByRefType(), typeof(int) }); - private static readonly Type Insert_4_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, Marshaler.AbiType, typeof(byte).MakeByRefType(), typeof(int) }); - private static readonly Type Remove_5_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(int) }); - - internal unsafe Vftbl(IntPtr thisPtr) : this() - { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); - Lookup_0 = Marshal.GetDelegateForFunctionPointer(vftbl[6], Lookup_0_Type); - GetSize_1 = (delegate* unmanaged[Stdcall])vftbl[7]; - HasKey_2 = Marshal.GetDelegateForFunctionPointer(vftbl[8], HasKey_2_Type); - GetView_3 = (delegate* unmanaged[Stdcall])vftbl[9]; - Insert_4 = Marshal.GetDelegateForFunctionPointer(vftbl[10], Insert_4_Type); - Remove_5 = Marshal.GetDelegateForFunctionPointer(vftbl[11], Remove_5_Type); - Clear_6 = (delegate* unmanaged[Stdcall])vftbl[12]; - } - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - private static readonly Delegate[] DelegateCache = new Delegate[3]; - - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - Lookup_0 = global::System.Delegate.CreateDelegate(Lookup_0_Type, typeof(Vftbl).GetMethod("Do_Abi_Lookup_0", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType, Marshaler.AbiType)), - _get_Size_1 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[0] = new _get_PropertyAsUInt32(Do_Abi_get_Size_1)), - HasKey_2 = global::System.Delegate.CreateDelegate(HasKey_2_Type, typeof(Vftbl).GetMethod("Do_Abi_HasKey_2", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType)), - _getView_3 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[1] = new IDictionary_Delegates.GetView_3(Do_Abi_GetView_3)), - Insert_4 = global::System.Delegate.CreateDelegate(Insert_4_Type, typeof(Vftbl).GetMethod("Do_Abi_Insert_4", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType, Marshaler.AbiType)), - Remove_5 = global::System.Delegate.CreateDelegate(Remove_5_Type, typeof(Vftbl).GetMethod("Do_Abi_Remove_5", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType)), - _clear_6 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[2] = new IDictionary_Delegates.Clear_6(Do_Abi_Clear_6)), - }; - var nativeVftbl = (IntPtr*)Marshal.AllocCoTaskMem(Marshal.SizeOf() + sizeof(IntPtr) * 7); - Marshal.StructureToPtr(AbiToProjectionVftable.IInspectableVftbl, (IntPtr)nativeVftbl, false); - nativeVftbl[6] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.Lookup_0); - nativeVftbl[7] = (IntPtr)AbiToProjectionVftable._get_Size_1; - nativeVftbl[8] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.HasKey_2); - nativeVftbl[9] = (IntPtr)AbiToProjectionVftable.GetView_3; - nativeVftbl[10] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.Insert_4); - nativeVftbl[11] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.Remove_5); - nativeVftbl[12] = (IntPtr)AbiToProjectionVftable.Clear_6; - - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - private static ConditionalWeakTable, ToAbiHelper> _adapterTable = - new ConditionalWeakTable, ToAbiHelper>(); - - private static global::Windows.Foundation.Collections.IMap FindAdapter(IntPtr thisPtr) - { - var __this = global::WinRT.ComWrappersSupport.FindObject>(thisPtr); - return _adapterTable.GetValue(__this, (dictionary) => new ToAbiHelper(dictionary)); - } - - private static unsafe int Do_Abi_Lookup_0(void* thisPtr, KAbi key, out VAbi __return_value__) - { - V ____return_value__ = default; - - __return_value__ = default; - - try - { - ____return_value__ = FindAdapter(new IntPtr(thisPtr)).Lookup(Marshaler.FromAbi(key)); - __return_value__ = (VAbi)Marshaler.FromManaged(____return_value__); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_HasKey_2(void* thisPtr, KAbi key, out byte __return_value__) - { - bool ____return_value__ = default; - - __return_value__ = default; - - try - { - ____return_value__ = FindAdapter(new IntPtr(thisPtr)).HasKey(Marshaler.FromAbi(key)); - __return_value__ = (byte)(____return_value__ ? 1 : 0); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_GetView_3(IntPtr thisPtr, out IntPtr __return_value__) - { - global::System.Collections.Generic.IReadOnlyDictionary ____return_value__ = default; - - __return_value__ = default; - - try - { - ____return_value__ = FindAdapter(thisPtr).GetView(); - __return_value__ = MarshalInterface>.FromManaged(____return_value__); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_Insert_4(void* thisPtr, KAbi key, VAbi value, out byte __return_value__) - { - bool ____return_value__ = default; - - __return_value__ = default; - - try - { - ____return_value__ = FindAdapter(new IntPtr(thisPtr)).Insert(Marshaler.FromAbi(key), Marshaler.FromAbi(value)); - __return_value__ = (byte)(____return_value__ ? 1 : 0); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_Remove_5(void* thisPtr, KAbi key) - { - - - try - { - FindAdapter(new IntPtr(thisPtr))._Remove(Marshaler.FromAbi(key)); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_Clear_6(IntPtr thisPtr) - { - - - try - { - FindAdapter(thisPtr).Clear(); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_get_Size_1(IntPtr thisPtr, out uint __return_value__) - { - uint ____return_value__ = default; - - __return_value__ = default; - - try - { - ____return_value__ = FindAdapter(thisPtr).Size; __return_value__ = ____return_value__; - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) - { - if (thisPtr == IntPtr.Zero) - { - return null; - } - var vftblT = new Vftbl(thisPtr); - return ObjectReference.FromAbi(thisPtr, vftblT); - } - public static Guid PIID = Vftbl.PIID; - - global::System.Collections.Generic.ICollection global::System.Collections.Generic.IDictionary.Keys - { - get - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle)); - return IDictionaryMethods.get_Keys(_obj); - } - } - - global::System.Collections.Generic.ICollection global::System.Collections.Generic.IDictionary.Values - { - get - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle)); - return IDictionaryMethods.get_Values(_obj); - } - } - - int global::System.Collections.Generic.ICollection>.Count - { - get - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle)); - return IDictionaryMethods.get_Count(_obj); - } - } - - bool global::System.Collections.Generic.ICollection>.IsReadOnly - { - get - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle)); - return IDictionaryMethods.get_IsReadOnly(_obj); - } - } - - V global::System.Collections.Generic.IDictionary.this[K key] - { - get - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle)); - return IDictionaryMethods.Indexer_Get(_obj, GetLookupCache((IWinRTObject)this), key); - } - set - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle)); - IDictionaryMethods.Indexer_Set(_obj, key, value); - } - } - - void global::System.Collections.Generic.IDictionary.Add(K key, V value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle)); - IDictionaryMethods.Add(_obj, key, value); - } - - bool global::System.Collections.Generic.IDictionary.ContainsKey(K key) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle)); - return IDictionaryMethods.ContainsKey(_obj, key); - } - - bool global::System.Collections.Generic.IDictionary.Remove(K key) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle)); - return IDictionaryMethods.Remove(_obj, key); - } - - internal static global::System.Collections.Generic.Dictionary GetLookupCache(IWinRTObject _this) - { - return (Dictionary)_this.GetOrCreateTypeHelperData(typeof(global::System.Collections.Generic.IDictionary).TypeHandle, - () => new Dictionary()); - } - - bool global::System.Collections.Generic.IDictionary.TryGetValue(K key, out V value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle)); - return IDictionaryMethods.TryGetValue(_obj, GetLookupCache((IWinRTObject)this), key, out value); - } - - void global::System.Collections.Generic.ICollection>.Add(global::System.Collections.Generic.KeyValuePair item) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle)); - IDictionaryMethods.Add(_obj, item); - } - - bool global::System.Collections.Generic.ICollection>.Contains(global::System.Collections.Generic.KeyValuePair item) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle)); - return IDictionaryMethods.Contains(_obj, GetLookupCache((IWinRTObject)this), item); - } - - void global::System.Collections.Generic.ICollection>.CopyTo(global::System.Collections.Generic.KeyValuePair[] array, int arrayIndex) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle)); - ((IWinRTObject)this).IsInterfaceImplemented(typeof(global::System.Collections.Generic.IEnumerable>).TypeHandle, true); - var _objEnumerable = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IEnumerable>).TypeHandle)); - IDictionaryMethods.CopyTo(_obj, _objEnumerable, array, arrayIndex); - } - - bool global::System.Collections.Generic.ICollection>.Remove(global::System.Collections.Generic.KeyValuePair item) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle)); - return IDictionaryMethods.Remove(_obj, item); - } - - global::System.Collections.Generic.IEnumerator> global::System.Collections.Generic.IEnumerable>.GetEnumerator() - { - ((IWinRTObject)this).IsInterfaceImplemented(typeof(global::System.Collections.Generic.IEnumerable>).TypeHandle, true); - var _objEnumerable = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IEnumerable>).TypeHandle)); - return IEnumerableMethods>.GetEnumerator(_objEnumerable); - } - - IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); - } - -#if EMBED - internal -#else - public -#endif - static class IDictionary_Delegates - { - public unsafe delegate int GetView_3(IntPtr thisPtr, out IntPtr __return_value__); - public unsafe delegate int Clear_6(IntPtr thisPtr); - } -} +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Reflection; +using System.Runtime.InteropServices; +using WinRT; +using WinRT.Interop; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; + +#pragma warning disable 0169 // warning CS0169: The field '...' is never used +#pragma warning disable 0649 // warning CS0169: Field '...' is never assigned to + +namespace Windows.Foundation.Collections +{ + //Need to rethink how to name/define this interface + [Guid("3C2925FE-8519-45C1-AA79-197B6718C1C1")] + interface IMap : IIterable> + { + V Lookup(K key); + bool HasKey(K key); + IReadOnlyDictionary GetView(); // Combining IMap & IReadOnlyDictionary needs redesign + bool Insert(K key, V value); + void _Remove(K key); + void Clear(); + uint Size { get; } + } +} + +namespace System.Collections.Generic +{ + + internal sealed class IDictionaryImpl : IDictionary, IWinRTObject + { + private readonly IObjectReference _inner; + + internal IDictionaryImpl(IObjectReference _inner) + { + this._inner = _inner; + } + + public static IDictionaryImpl CreateRcw(IInspectable obj) => new(obj.ObjRef); + + private volatile IObjectReference __iDictionaryObjRef; + private IObjectReference Make_IDictionaryObjRef() + { + global::System.Threading.Interlocked.CompareExchange(ref __iDictionaryObjRef, _inner.As(ABI.System.Collections.Generic.IDictionaryMethods.PIID), null); + return __iDictionaryObjRef; + } + private IObjectReference iDictionaryObjRef => __iDictionaryObjRef ?? Make_IDictionaryObjRef(); + + private volatile IObjectReference __iEnumerableObjRef; + private IObjectReference Make_IEnumerableObjRef() + { + global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, _inner.As(ABI.System.Collections.Generic.IEnumerableMethods>.PIID), null); + return __iEnumerableObjRef; + } + private IObjectReference iEnumerableObjRef => __iEnumerableObjRef ?? Make_IEnumerableObjRef(); + + IObjectReference IWinRTObject.NativeObject => _inner; + + bool IWinRTObject.HasUnwrappableNativeObject => true; + + private volatile global::System.Collections.Concurrent.ConcurrentDictionary _queryInterfaceCache; + private global::System.Collections.Concurrent.ConcurrentDictionary MakeQueryInterfaceCache() + { + global::System.Threading.Interlocked.CompareExchange(ref _queryInterfaceCache, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); + return _queryInterfaceCache; + } + global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.QueryInterfaceCache => _queryInterfaceCache ?? MakeQueryInterfaceCache(); + private volatile global::System.Collections.Concurrent.ConcurrentDictionary _additionalTypeData; + private global::System.Collections.Concurrent.ConcurrentDictionary MakeAdditionalTypeData() + { + global::System.Threading.Interlocked.CompareExchange(ref _additionalTypeData, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); + return _additionalTypeData; + } + global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.AdditionalTypeData => _additionalTypeData ?? MakeAdditionalTypeData(); + + public V this[K key] + { + get => ABI.System.Collections.Generic.IDictionaryMethods.Indexer_Get(iDictionaryObjRef, null, key); + set => ABI.System.Collections.Generic.IDictionaryMethods.Indexer_Set(iDictionaryObjRef, key, value); + } + + public ICollection Keys => ABI.System.Collections.Generic.IDictionaryMethods.get_Keys(iDictionaryObjRef); + + public ICollection Values => ABI.System.Collections.Generic.IDictionaryMethods.get_Values(iDictionaryObjRef); + + public int Count => ABI.System.Collections.Generic.IDictionaryMethods.get_Count(iDictionaryObjRef); + + public bool IsReadOnly => ABI.System.Collections.Generic.IDictionaryMethods.get_IsReadOnly(iDictionaryObjRef); + + public void Add(K key, V value) + { + ABI.System.Collections.Generic.IDictionaryMethods.Add(iDictionaryObjRef, key, value); + } + + public void Add(KeyValuePair item) + { + ABI.System.Collections.Generic.IDictionaryMethods.Add(iDictionaryObjRef, item); + } + + public void Clear() + { + ABI.System.Collections.Generic.IDictionaryMethods.Clear(iDictionaryObjRef); + } + + public bool Contains(KeyValuePair item) + { + return ABI.System.Collections.Generic.IDictionaryMethods.Contains(iDictionaryObjRef, null, item); + } + + public bool ContainsKey(K key) + { + return ABI.System.Collections.Generic.IDictionaryMethods.ContainsKey(iDictionaryObjRef, key); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + ABI.System.Collections.Generic.IDictionaryMethods.CopyTo(iDictionaryObjRef, iEnumerableObjRef, array, arrayIndex); + } + + public IEnumerator> GetEnumerator() + { + return ABI.System.Collections.Generic.IEnumerableMethods>.GetEnumerator(iEnumerableObjRef); + } + + public bool Remove(K key) + { + return ABI.System.Collections.Generic.IDictionaryMethods.Remove(iDictionaryObjRef, key); + } + + public bool Remove(KeyValuePair item) + { + return ABI.System.Collections.Generic.IDictionaryMethods.Remove(iDictionaryObjRef, item); + } + + public bool TryGetValue(K key, [MaybeNullWhen(false)] out V value) + { + return ABI.System.Collections.Generic.IDictionaryMethods.TryGetValue(iDictionaryObjRef, null, key, out value); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } +} + +namespace ABI.Windows.Foundation.Collections +{ + using global::System; + using global::System.Runtime.CompilerServices; + + internal static class IMapMethods + { + // These function pointers will be set by IDictionaryMethods + // when it is called by the source generated type or by the fallback + // mechanism if the source generated type wasn't used. + internal volatile unsafe static delegate* _Lookup; + internal volatile unsafe static delegate* _HasKey; + internal volatile unsafe static delegate*> _GetView; + internal volatile unsafe static delegate* _Insert; + internal volatile unsafe static delegate* _Remove; + internal volatile static bool _RcwHelperInitialized; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe void EnsureInitialized() + { + if (RuntimeFeature.IsDynamicCodeCompiled) + { + return; + } + + if (!_RcwHelperInitialized) + { + [DoesNotReturn] + static void ThrowNotInitialized() + { + throw new NotImplementedException( + $"Type '{typeof(global::System.Collections.Generic.IDictionary)}' was called without initializing the RCW methods using 'IDictionaryMethods.InitRcwHelper'. " + + $"If using 'IDynamicInterfaceCastable' support to do a dynamic cast to this interface, ensure the 'InitRcwHelper' method is called."); + } + + ThrowNotInitialized(); + } + } + + public static unsafe V Lookup(IObjectReference obj, K key) + { + EnsureInitialized(); + return _Lookup(obj, key); + } + + public static unsafe bool HasKey(IObjectReference obj, K key) + { + EnsureInitialized(); + return _HasKey(obj, key); + } + + public static unsafe global::System.Collections.Generic.IReadOnlyDictionary GetView(IObjectReference obj) + { + // Early return to ensure things are trimmed correctly on NAOT. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + EnsureInitialized(); + return _GetView(obj); + } + + if (_GetView != null) + { + return _GetView(obj); + } + else + { + var ThisPtr = obj.ThisPtr; + IntPtr __retval = default; + try + { + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[9](ThisPtr, &__retval)); + GC.KeepAlive(obj); + return MarshalInterface>.FromAbi(__retval); + } + finally + { + MarshalInterface>.DisposeAbi(__retval); + } + } + } + + public static unsafe bool Insert(IObjectReference obj, K key, V value) + { + EnsureInitialized(); + return _Insert(obj, key, value); + } + + public static unsafe void Remove(IObjectReference obj, K key) + { + EnsureInitialized(); + _Remove(obj, key); + } + + public static void Clear(IObjectReference obj) + { + _ClearHelper(obj); + } + + private static unsafe void _ClearHelper(IObjectReference obj) + { + var ThisPtr = obj.ThisPtr; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[12](ThisPtr)); + GC.KeepAlive(obj); + } + + public static unsafe uint get_Size(IObjectReference obj) + { + var ThisPtr = obj.ThisPtr; + uint __retval = default; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[7](ThisPtr, &__retval)); + GC.KeepAlive(obj); + return __retval; + } + } +} + +namespace ABI.System.Collections.Generic +{ + using ABI.Windows.Foundation.Collections; + using global::System.Runtime.CompilerServices; + +#if EMBED + internal +#else + public +#endif + static class IDictionaryMethods + { + unsafe static IDictionaryMethods() + { + ComWrappersSupport.RegisterHelperType(typeof(global::System.Collections.Generic.IDictionary), typeof(global::ABI.System.Collections.Generic.IDictionary)); + + // Early return to ensure things are trimmed correctly on NAOT. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return; + } + +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + InitRcwHelperFallbackIfNeeded(); +#pragma warning restore IL3050 + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2080", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] + [UnconditionalSuppressMessage("Trimming", "IL2081", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] +#endif + [MethodImpl(MethodImplOptions.NoInlining)] + static void InitRcwHelperFallbackIfNeeded() + { + // Handle the compat scenario where the source generator wasn't used and IDIC hasn't been used yet + // and due to that the function pointers haven't been initialized. + if (!IMapMethods._RcwHelperInitialized) + { + var initRcwHelperFallback = (Func)typeof(IDictionaryMethods<,,,>).MakeGenericType(typeof(K), Marshaler.AbiType, typeof(V), Marshaler.AbiType). + GetMethod("InitRcwHelperFallback", BindingFlags.NonPublic | BindingFlags.Static). + CreateDelegate(typeof(Func)); + initRcwHelperFallback(); + } + } + } + + public static int get_Count(IObjectReference obj) + { + uint size = IMapMethods.get_Size(obj); + if (((uint)int.MaxValue) < size) + { + throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_CollectionBackingDictionaryTooLarge); + } + return (int)size; + } + + public static bool get_IsReadOnly(IObjectReference _) => false; + + public static void Add(IObjectReference obj, global::System.Collections.Generic.KeyValuePair item) + { + IMapMethods.Insert(obj, item.Key, item.Value); + } + + public static void Clear(IObjectReference obj) + { + IMapMethods.Clear(obj); + } + + public static bool Contains(IObjectReference obj, Dictionary __lookupCache, global::System.Collections.Generic.KeyValuePair item) + { + bool hasKey = IMapMethods.HasKey(obj, item.Key); + if (!hasKey) + return false; + // todo: toctou + V value = IMapMethods.Lookup(obj, item.Key); + return EqualityComparer.Default.Equals(value, item.Value); + } + + public static void CopyTo(IObjectReference obj, IObjectReference iEnumerableObjRef, global::System.Collections.Generic.KeyValuePair[] array, int arrayIndex) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + + if (arrayIndex < 0) + throw new ArgumentOutOfRangeException(nameof(arrayIndex)); + + if (array.Length <= arrayIndex && get_Count(obj) > 0) + throw new ArgumentException(WinRTRuntimeErrorStrings.Argument_IndexOutOfArrayBounds); + + if (array.Length - arrayIndex < get_Count(obj)) + throw new ArgumentException(WinRTRuntimeErrorStrings.Argument_InsufficientSpaceToCopyCollection); + + foreach (global::System.Collections.Generic.KeyValuePair mapping in (new IEnumerableImpl>(iEnumerableObjRef))) + { + array[arrayIndex++] = mapping; + } + } + + public static bool Remove(IObjectReference obj, global::System.Collections.Generic.KeyValuePair item) + { + IMapMethods.Remove(obj, item.Key); + return true; + } + + public static V Indexer_Get(IObjectReference obj, Dictionary __lookupCache, K key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + return Lookup(obj, key); + } + + public static void Indexer_Set(IObjectReference obj, K key, V value) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + Insert(obj, key, value); + } + + public static global::System.Collections.Generic.ICollection get_Keys(IObjectReference obj) => new DictionaryKeyCollection(obj); + + public static global::System.Collections.Generic.ICollection get_Values(IObjectReference obj) => new DictionaryValueCollection(obj); + + public static bool ContainsKey(IObjectReference obj, K key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + return IMapMethods.HasKey(obj, key); + } + + public static void Add(IObjectReference obj, K key, V value) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + if (ContainsKey(obj, key)) + throw new ArgumentException(WinRTRuntimeErrorStrings.Argument_AddingDuplicate); + + Insert(obj, key, value); + } + + public static bool Remove(IObjectReference obj, K key) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + if (!IMapMethods.HasKey(obj, key)) + return false; + + try + { + IMapMethods.Remove(obj, key); + return true; + } + catch (global::System.Exception ex) + { + if (ExceptionHelpers.E_BOUNDS == ex.HResult) + return false; + + throw; + } + } + + public static bool TryGetValue(IObjectReference obj, Dictionary __lookupCache, K key, out V value) + { + if (key == null) + throw new ArgumentNullException(nameof(key)); + + if (!IMapMethods.HasKey(obj, key)) + { + value = default!; + return false; + } + + try + { + value = Lookup(obj, key); + return true; + } + catch (KeyNotFoundException) + { + value = default!; + return false; + } + } + + private static V Lookup(IObjectReference obj, K key) + { + Debug.Assert(null != key); + + try + { + return IMapMethods.Lookup(obj, key); + } + catch (global::System.Exception ex) + { + if (ExceptionHelpers.E_BOUNDS == ex.HResult) + throw new KeyNotFoundException(WinRTRuntimeErrorStrings.Arg_KeyNotFound); + throw; + } + } + + private static bool Insert(IObjectReference obj, K key, V value) + { + Debug.Assert(null != key); + + bool replaced = IMapMethods.Insert(obj, key, value); + return replaced; + } + + private sealed class DictionaryKeyCollection : global::System.Collections.Generic.ICollection + { + private readonly IObjectReference iDictionaryObjRef; + + public DictionaryKeyCollection(IObjectReference iDictionaryObjRef) + { + if (iDictionaryObjRef == null) + throw new ArgumentNullException(nameof(iDictionaryObjRef)); + + this.iDictionaryObjRef = iDictionaryObjRef; + } + + private volatile IObjectReference __iEnumerableObjRef; + private IObjectReference Make_IEnumerableObjRef() + { + global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, iDictionaryObjRef.As(ABI.System.Collections.Generic.IEnumerableMethods>.PIID), null); + return __iEnumerableObjRef; + } + private IObjectReference iEnumerableObjRef => __iEnumerableObjRef ?? Make_IEnumerableObjRef(); + + public void CopyTo(K[] array, int index) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index)); + if (array.Length <= index && this.Count > 0) + throw new ArgumentException(WinRTRuntimeErrorStrings.Arg_IndexOutOfRangeException); + if (array.Length - index < IDictionaryMethods.get_Count(iDictionaryObjRef)) + throw new ArgumentException(WinRTRuntimeErrorStrings.Argument_InsufficientSpaceToCopyCollection); + + int i = index; + foreach (global::System.Collections.Generic.KeyValuePair mapping in (new IEnumerableImpl>(iEnumerableObjRef))) + { + array[i++] = mapping.Key; + } + } + + public int Count => IDictionaryMethods.get_Count(iDictionaryObjRef); + + public bool IsReadOnly => true; + + void global::System.Collections.Generic.ICollection.Add(K item) + { + throw new NotSupportedException(WinRTRuntimeErrorStrings.NotSupported_KeyCollectionSet); + } + + void global::System.Collections.Generic.ICollection.Clear() + { + throw new NotSupportedException(WinRTRuntimeErrorStrings.NotSupported_KeyCollectionSet); + } + + public bool Contains(K item) + { + return IDictionaryMethods.ContainsKey(iDictionaryObjRef, item); + } + + bool global::System.Collections.Generic.ICollection.Remove(K item) + { + throw new NotSupportedException(WinRTRuntimeErrorStrings.NotSupported_KeyCollectionSet); + } + + global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + + public global::System.Collections.Generic.IEnumerator GetEnumerator() => + new DictionaryKeyEnumerator(iEnumerableObjRef); + + private sealed class DictionaryKeyEnumerator : global::System.Collections.Generic.IEnumerator + { + private readonly IObjectReference iEnumerableObjRef; + private global::System.Collections.Generic.IEnumerator> enumeration; + + public DictionaryKeyEnumerator(IObjectReference iEnumerableObjRef) + { + this.iEnumerableObjRef = iEnumerableObjRef; + enumeration = IEnumerableMethods>.GetEnumerator(iEnumerableObjRef); + } + + public void Dispose() + { + enumeration.Dispose(); + } + + public bool MoveNext() + { + return enumeration.MoveNext(); + } + + object IEnumerator.Current => Current; + + public K Current => enumeration.Current.Key; + + public void Reset() + { + enumeration = IEnumerableMethods>.GetEnumerator(iEnumerableObjRef); + } + } + } + + private sealed class DictionaryValueCollection : global::System.Collections.Generic.ICollection + { + private readonly IObjectReference iDictionaryObjRef; + + public DictionaryValueCollection(IObjectReference iDictionaryObjRef) + { + this.iDictionaryObjRef = iDictionaryObjRef; + } + + private volatile IObjectReference __iEnumerableObjRef; + private IObjectReference Make_IEnumerableObjRef() + { + global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, iDictionaryObjRef.As(ABI.System.Collections.Generic.IEnumerableMethods>.PIID), null); + return __iEnumerableObjRef; + } + private IObjectReference iEnumerableObjRef => __iEnumerableObjRef ?? Make_IEnumerableObjRef(); + + public void CopyTo(V[] array, int index) + { + if (array == null) + throw new ArgumentNullException(nameof(array)); + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index)); + if (array.Length <= index && this.Count > 0) + throw new ArgumentException(WinRTRuntimeErrorStrings.Arg_IndexOutOfRangeException); + if (array.Length - index < IDictionaryMethods.get_Count(iDictionaryObjRef)) + throw new ArgumentException(WinRTRuntimeErrorStrings.Argument_InsufficientSpaceToCopyCollection); + + int i = index; + foreach (global::System.Collections.Generic.KeyValuePair mapping in (new IEnumerableImpl>(iEnumerableObjRef))) + { + array[i++] = mapping.Value; + } + } + + public int Count => IDictionaryMethods.get_Count(iDictionaryObjRef); + + public bool IsReadOnly => true; + + void global::System.Collections.Generic.ICollection.Add(V item) + { + throw new NotSupportedException(WinRTRuntimeErrorStrings.NotSupported_ValueCollectionSet); + } + + void global::System.Collections.Generic.ICollection.Clear() + { + throw new NotSupportedException(WinRTRuntimeErrorStrings.NotSupported_ValueCollectionSet); + } + + public bool Contains(V item) + { + EqualityComparer comparer = EqualityComparer.Default; + foreach (V value in this) + if (comparer.Equals(item, value)) + return true; + return false; + } + + bool global::System.Collections.Generic.ICollection.Remove(V item) + { + throw new NotSupportedException(WinRTRuntimeErrorStrings.NotSupported_ValueCollectionSet); + } + + IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + + public global::System.Collections.Generic.IEnumerator GetEnumerator() + { + return new DictionaryValueEnumerator(iEnumerableObjRef); + } + + private sealed class DictionaryValueEnumerator : global::System.Collections.Generic.IEnumerator + { + private readonly IObjectReference iEnumerableObjRef; + private global::System.Collections.Generic.IEnumerator> enumeration; + + public DictionaryValueEnumerator(IObjectReference iEnumerableObjRef) + { + this.iEnumerableObjRef = iEnumerableObjRef; + enumeration = IEnumerableMethods>.GetEnumerator(iEnumerableObjRef); + } + + public void Dispose() + { + enumeration.Dispose(); + } + + public bool MoveNext() + { + return enumeration.MoveNext(); + } + + object IEnumerator.Current => Current; + + public V Current => enumeration.Current.Value; + + public void Reset() + { + enumeration = IEnumerableMethods>.GetEnumerator(iEnumerableObjRef); + } + } + } + + private static IntPtr abiToProjectionVftablePtr; + public static IntPtr AbiToProjectionVftablePtr => abiToProjectionVftablePtr; + + internal static bool TryInitCCWVtable(IntPtr ptr) + { + return global::System.Threading.Interlocked.CompareExchange(ref abiToProjectionVftablePtr, ptr, IntPtr.Zero) == IntPtr.Zero; + } + + internal static readonly Guid PIID = GuidGenerator.CreateIIDUnsafe(typeof(IDictionary)); + public static Guid IID => PIID; + + public static V Abi_Lookup_0(IntPtr thisPtr, K key) + { + return IDictionary.FindAdapter(thisPtr).Lookup(key); + } + + public static bool Abi_HasKey_2(IntPtr thisPtr, K key) + { + return IDictionary.FindAdapter(thisPtr).HasKey(key); + } + + public static global::System.Collections.Generic.IReadOnlyDictionary Abi_GetView_3(IntPtr thisPtr) + { + return IDictionary.FindAdapter(thisPtr).GetView(); + } + + public static bool Abi_Insert_4(IntPtr thisPtr, K key, V value) + { + return IDictionary.FindAdapter(thisPtr).Insert(key, value); + } + + public static void Abi_Remove_5(IntPtr thisPtr, K key) + { + IDictionary.FindAdapter(thisPtr)._Remove(key); + } + + public static void Abi_Clear_6(IntPtr thisPtr) + { + IDictionary.FindAdapter(thisPtr).Clear(); + } + + public static uint Abi_get_Size_1(IntPtr thisPtr) + { + return IDictionary.FindAdapter(thisPtr).Size; + } + } + +#if EMBED + internal +#else + public +#endif + static class IDictionaryMethods where KAbi: unmanaged where VAbi: unmanaged + { + internal unsafe static delegate* _Lookup; + internal unsafe static delegate* _HasKey; + internal unsafe static delegate*> _GetView; + internal unsafe static delegate* _Insert; + internal unsafe static delegate* _Remove; + + public unsafe static bool InitRcwHelper( + delegate* lookup, + delegate* hasKey, + delegate*> getView, + delegate* insert, + delegate* remove) + { + if (IMapMethods._RcwHelperInitialized) + { + return true; + } + + IMapMethods._Lookup = lookup; + IMapMethods._HasKey = hasKey; + IMapMethods._GetView = getView; + IMapMethods._Insert = insert; + IMapMethods._Remove = remove; + + ComWrappersSupport.RegisterTypedRcwFactory( + typeof(global::System.Collections.Generic.IDictionary), + IDictionaryImpl.CreateRcw); + ComWrappersSupport.RegisterHelperType(typeof(global::System.Collections.Generic.IDictionary), typeof(global::ABI.System.Collections.Generic.IDictionary)); + + IMapMethods._RcwHelperInitialized = true; + return true; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private unsafe static bool InitRcwHelperFallback() + { + return InitRcwHelper( + &LookupDynamic, + &HasKeyDynamic, + null, + &InsertDynamic, + &RemoveDynamic); + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe V LookupDynamic(IObjectReference obj, K key) + { + var ThisPtr = obj.ThisPtr; + object __key = default; + VAbi valueAbi = default; + var __params = new object[] { ThisPtr, null, (IntPtr)(void*)&valueAbi }; + try + { + __key = Marshaler.CreateMarshaler2(key); + __params[1] = Marshaler.GetAbi(__key); + DelegateHelper.Get(obj).Lookup.DynamicInvokeAbi(__params); + GC.KeepAlive(obj); + GC.KeepAlive(__params); + return Marshaler.FromAbi(valueAbi); + } + finally + { + Marshaler.DisposeMarshaler(__key); + Marshaler.DisposeAbi(valueAbi); + } + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe bool HasKeyDynamic(IObjectReference obj, K key) + { + var ThisPtr = obj.ThisPtr; + object __key = default; + byte found; + var __params = new object[] { ThisPtr, null, (IntPtr)(void*)&found }; + try + { + __key = Marshaler.CreateMarshaler2(key); + __params[1] = Marshaler.GetAbi(__key); + DelegateHelper.Get(obj).HasKey.DynamicInvokeAbi(__params); + GC.KeepAlive(obj); + GC.KeepAlive(__params); + return found != 0; + } + finally + { + Marshaler.DisposeMarshaler(__key); + } + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe bool InsertDynamic(IObjectReference obj, K key, V value) + { + var ThisPtr = obj.ThisPtr; + object __key = default; + object __value = default; + byte replaced; + var __params = new object[] { ThisPtr, null, null, (IntPtr)(void*)&replaced }; + try + { + __key = Marshaler.CreateMarshaler2(key); + __params[1] = Marshaler.GetAbi(__key); + __value = Marshaler.CreateMarshaler2(value); + __params[2] = Marshaler.GetAbi(__value); + DelegateHelper.Get(obj).Insert.DynamicInvokeAbi(__params); + GC.KeepAlive(obj); + GC.KeepAlive(__params); + return replaced != 0; + } + finally + { + Marshaler.DisposeMarshaler(__key); + Marshaler.DisposeMarshaler(__value); + } + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe void RemoveDynamic(IObjectReference obj, K key) + { + var ThisPtr = obj.ThisPtr; + object __key = default; + var __params = new object[] { ThisPtr, null }; + try + { + __key = Marshaler.CreateMarshaler2(key); + __params[1] = Marshaler.GetAbi(__key); + DelegateHelper.Get(obj).Remove.DynamicInvokeAbi(__params); + GC.KeepAlive(obj); + GC.KeepAlive(__params); + } + finally + { + Marshaler.DisposeMarshaler(__key); + } + } + + public static unsafe bool InitCcw( + delegate* unmanaged[Stdcall] lookup, + delegate* unmanaged[Stdcall] getSize, + delegate* unmanaged[Stdcall] hasKey, + delegate* unmanaged[Stdcall] getView, + delegate* unmanaged[Stdcall] insert, + delegate* unmanaged[Stdcall] remove, + delegate* unmanaged[Stdcall] clear) + { + if (IDictionaryMethods.AbiToProjectionVftablePtr != default) + { + return false; + } + + var abiToProjectionVftablePtr = (IntPtr)NativeMemory.AllocZeroed((nuint)(sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * 7)); + *(IInspectable.Vftbl*)abiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[6] = lookup; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[7] = getSize; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[8] = hasKey; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[9] = getView; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[10] = insert; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[11] = remove; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[12] = clear; + + if (!IDictionaryMethods.TryInitCCWVtable(abiToProjectionVftablePtr)) + { + NativeMemory.Free((void*)abiToProjectionVftablePtr); + return false; + } + + // Register generic helper types referenced in CCW. + ComWrappersSupport.RegisterHelperType(typeof(global::System.Collections.Generic.IReadOnlyDictionary), typeof(global::ABI.System.Collections.Generic.IReadOnlyDictionary)); + + return true; + } + + private static global::System.Delegate[] DelegateCache; + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + internal static unsafe void InitFallbackCCWVtable() + { + DelegateCache = new global::System.Delegate[] + { + global::System.Delegate.CreateDelegate(Lookup_0_Type, typeof(IDictionaryMethods).GetMethod(nameof(Do_Abi_Lookup_0), BindingFlags.NonPublic | BindingFlags.Static)), + new _get_PropertyAsUInt32_Abi(Do_Abi_get_Size_1), + global::System.Delegate.CreateDelegate(HasKey_2_Type, typeof(IDictionaryMethods).GetMethod(nameof(Do_Abi_HasKey_2), BindingFlags.NonPublic | BindingFlags.Static)), + new IDictionary_Delegates.GetView_3_Abi(Do_Abi_GetView_3), + global::System.Delegate.CreateDelegate(Insert_4_Type, typeof(IDictionaryMethods).GetMethod(nameof(Do_Abi_Insert_4), BindingFlags.NonPublic | BindingFlags.Static)), + global::System.Delegate.CreateDelegate(Remove_5_Type, typeof(IDictionaryMethods).GetMethod(nameof(Do_Abi_Remove_5), BindingFlags.NonPublic | BindingFlags.Static)), + new IDictionary_Delegates.Clear_6(Do_Abi_Clear_6) + }; + + InitCcw( + (delegate* unmanaged[Stdcall]) Marshal.GetFunctionPointerForDelegate(DelegateCache[0]), + (delegate* unmanaged[Stdcall]) Marshal.GetFunctionPointerForDelegate(DelegateCache[1]), + (delegate* unmanaged[Stdcall]) Marshal.GetFunctionPointerForDelegate(DelegateCache[2]), + (delegate* unmanaged[Stdcall]) Marshal.GetFunctionPointerForDelegate(DelegateCache[3]), + (delegate* unmanaged[Stdcall]) Marshal.GetFunctionPointerForDelegate(DelegateCache[4]), + (delegate* unmanaged[Stdcall]) Marshal.GetFunctionPointerForDelegate(DelegateCache[5]), + (delegate* unmanaged[Stdcall]) Marshal.GetFunctionPointerForDelegate(DelegateCache[6]) + ); + } + +#if NET + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_Lookup_0(void* thisPtr, KAbi key, VAbi* __return_value__) + { + V ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = IDictionary.FindAdapter(new IntPtr(thisPtr)).Lookup(Marshaler.FromAbi(key)); + *__return_value__ = (VAbi)Marshaler.FromManaged(____return_value__); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + +#if NET + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_HasKey_2(void* thisPtr, KAbi key, byte* __return_value__) + { + bool ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = IDictionary.FindAdapter(new IntPtr(thisPtr)).HasKey(Marshaler.FromAbi(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; + } + + private static unsafe int Do_Abi_GetView_3(IntPtr thisPtr, IntPtr* __return_value__) + { + global::System.Collections.Generic.IReadOnlyDictionary ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = IDictionary.FindAdapter(thisPtr).GetView(); + *__return_value__ = MarshalInterface>.FromManaged(____return_value__); + + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + +#if NET + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_Insert_4(void* thisPtr, KAbi key, VAbi value, byte* __return_value__) + { + bool ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = IDictionary.FindAdapter(new IntPtr(thisPtr)).Insert(Marshaler.FromAbi(key), Marshaler.FromAbi(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; + } + +#if NET + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_Remove_5(void* thisPtr, KAbi key) + { + try + { + IDictionary.FindAdapter(new IntPtr(thisPtr))._Remove(Marshaler.FromAbi(key)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe int Do_Abi_Clear_6(IntPtr thisPtr) + { + try + { + IDictionary.FindAdapter(thisPtr).Clear(); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe int Do_Abi_get_Size_1(IntPtr thisPtr, uint* __return_value__) + { + uint ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = IDictionary.FindAdapter(thisPtr).Size; + *__return_value__ = ____return_value__; + + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static global::System.Type _lookup_0_type; + private static global::System.Type Lookup_0_Type + { +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + get => _lookup_0_type ?? MakeLookupType(); + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + private static global::System.Type MakeLookupType() + { + global::System.Threading.Interlocked.CompareExchange(ref _lookup_0_type, Projections.GetAbiDelegateType(new global::System.Type[] { typeof(void*), typeof(KAbi), typeof(VAbi*), typeof(int) }), null); + return _lookup_0_type; + } + + private static global::System.Type _hasKey_2_type; + private static global::System.Type HasKey_2_Type + { +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + get => _hasKey_2_type ?? MakeHasKeyType(); + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + private static global::System.Type MakeHasKeyType() + { + global::System.Threading.Interlocked.CompareExchange(ref _hasKey_2_type, Projections.GetAbiDelegateType(new global::System.Type[] { typeof(void*), typeof(KAbi), typeof(byte*), typeof(int) }), null); + return _hasKey_2_type; + } + + private static global::System.Type _insert_4_type; + private static global::System.Type Insert_4_Type + { +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + get => _insert_4_type ?? MakeInsertType(); + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + private static global::System.Type MakeInsertType() + { + global::System.Threading.Interlocked.CompareExchange(ref _insert_4_type, Projections.GetAbiDelegateType(new global::System.Type[] { typeof(void*), typeof(KAbi), typeof(VAbi), typeof(byte*), typeof(int) }), null); + return _insert_4_type; + } + + private static global::System.Type _remove_5_type; + private static global::System.Type Remove_5_Type + { +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + get => _remove_5_type ?? MakeRemoveType(); + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + private static global::System.Type MakeRemoveType() + { + global::System.Threading.Interlocked.CompareExchange(ref _remove_5_type, Projections.GetAbiDelegateType(new global::System.Type[] { typeof(void*), typeof(KAbi), typeof(int) }), null); + return _remove_5_type; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + private sealed class DelegateHelper + { + private readonly IntPtr _ptr; + + private Delegate _lookupDelegate; + public Delegate Lookup => _lookupDelegate ?? GenericDelegateHelper.CreateDelegate(_ptr, ref _lookupDelegate, Lookup_0_Type, 6); + + private Delegate _hasKeyDelegate; + public Delegate HasKey => _hasKeyDelegate ?? GenericDelegateHelper.CreateDelegate(_ptr, ref _hasKeyDelegate, HasKey_2_Type, 8); + + private Delegate _insertDelegate; + public Delegate Insert => _insertDelegate ?? GenericDelegateHelper.CreateDelegate(_ptr, ref _insertDelegate, Insert_4_Type, 10); + + private Delegate _removeDelegate; + public Delegate Remove => _removeDelegate ?? GenericDelegateHelper.CreateDelegate(_ptr, ref _removeDelegate, Remove_5_Type, 11); + + private DelegateHelper(IntPtr ptr) + { + _ptr = ptr; + } + + public static DelegateHelper Get(IObjectReference obj) + { + return (DelegateHelper)GenericDelegateHelper.DelegateTable.GetValue(obj, static (objRef) => new DelegateHelper(objRef.ThisPtr)); + } + } + } +} + +namespace ABI.System.Collections.Generic +{ + using global::System; + using global::System.Runtime.CompilerServices; + + //This interface does not need to implement IMapView. Needs to be refactored + [DynamicInterfaceCastableImplementation] + [Guid("3C2925FE-8519-45C1-AA79-197B6718C1C1")] + interface IDictionary : global::System.Collections.Generic.IDictionary + { + public static IObjectReference CreateMarshaler(global::System.Collections.Generic.IDictionary obj) => + obj is null ? null : ComWrappersSupport.CreateCCWForObject(obj, PIID); + + public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.Generic.IDictionary obj) => + ComWrappersSupport.CreateCCWForObjectForMarshaling(obj, PIID); + + public static IntPtr GetAbi(IObjectReference objRef) => + objRef?.ThisPtr ?? IntPtr.Zero; + + public static global::System.Collections.Generic.IDictionary FromAbi(IntPtr thisPtr) + { + if (thisPtr == IntPtr.Zero) + { + return null; + } + + if (!FeatureSwitches.EnableIDynamicInterfaceCastableSupport) + { + throw new NotSupportedException( + "'IDictionary.FromAbi' relies on 'IDynamicInterfaceCastable' support, which is not currently " + + "available. Make sure the 'EnableIDynamicInterfaceCastableSupport' property is not set to 'false'."); + } + + return (global::System.Collections.Generic.IDictionary)(object)new IInspectable(ObjRefFromAbi(thisPtr)); + } + + public static IntPtr FromManaged(global::System.Collections.Generic.IDictionary value) => + (value is null) ? IntPtr.Zero : CreateMarshaler2(value).Detach(); + + public static void DisposeMarshaler(IObjectReference objRef) => objRef?.Dispose(); + + public static void DisposeAbi(IntPtr abi) => + MarshalInterfaceHelper>.DisposeAbi(abi); + + public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IDictionary)); + +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + public sealed class ToAbiHelper : global::Windows.Foundation.Collections.IMap +#pragma warning restore CA2257 + { + private readonly global::System.Collections.Generic.IDictionary _dictionary; + + public ToAbiHelper(global::System.Collections.Generic.IDictionary dictionary) => _dictionary = dictionary; + + global::System.Collections.Generic.IEnumerator> global::Windows.Foundation.Collections.IIterable>.First() => + new KeyValuePair.Enumerator(_dictionary.GetEnumerator()); + + public V Lookup(K key) + { + V value; + bool keyFound = _dictionary.TryGetValue(key, out value); + + if (!keyFound) + { + Debug.Assert(key != null); + Exception e = new KeyNotFoundException(String.Format(WinRTRuntimeErrorStrings.Arg_KeyNotFoundWithKey, key.ToString())); + e.SetHResult(ExceptionHelpers.E_BOUNDS); + throw e; + } + + return value; + } + + public uint Size { get => (uint)_dictionary.Count; } + + public bool HasKey(K key) => _dictionary.ContainsKey(key); + + global::System.Collections.Generic.IReadOnlyDictionary global::Windows.Foundation.Collections.IMap.GetView() + { + if (!(_dictionary is global::System.Collections.Generic.IReadOnlyDictionary roDictionary)) + { + roDictionary = new ReadOnlyDictionary(_dictionary); + } + return roDictionary; + } + + public bool Insert(K key, V value) + { + bool replacing = _dictionary.ContainsKey(key); + _dictionary[key] = value; + return replacing; + } + + public void _Remove(K key) + { + bool removed = _dictionary.Remove(key); + + if (!removed) + { + Debug.Assert(key != null); + Exception e = new KeyNotFoundException(String.Format(WinRTRuntimeErrorStrings.Arg_KeyNotFoundWithKey, key.ToString())); + e.SetHResult(ExceptionHelpers.E_BOUNDS); + throw e; + } + } + + public void Clear() => _dictionary.Clear(); + } + + public static readonly IntPtr AbiToProjectionVftablePtr; + + static IDictionary() + { + if (RuntimeFeature.IsDynamicCodeCompiled) + { + // Simple invocation guarded by a direct runtime feature check to help the linker. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + InitFallbackCCWVTableIfNeeded(); +#pragma warning restore IL3050 + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2080", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] + [UnconditionalSuppressMessage("Trimming", "IL2081", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] +#endif + [MethodImpl(MethodImplOptions.NoInlining)] + static void InitFallbackCCWVTableIfNeeded() + { + if (IDictionaryMethods.AbiToProjectionVftablePtr == default) + { + // Handle the compat scenario where the source generator wasn't used or IDIC was used. + var initFallbackCCWVtable = (Action)typeof(IDictionaryMethods<,,,>).MakeGenericType(typeof(K), Marshaler.AbiType, typeof(V), Marshaler.AbiType). + GetMethod("InitFallbackCCWVtable", BindingFlags.NonPublic | BindingFlags.Static). + CreateDelegate(typeof(Action)); + initFallbackCCWVtable(); + } + } + } + + AbiToProjectionVftablePtr = IDictionaryMethods.AbiToProjectionVftablePtr; + } + + // This is left here for backwards compat purposes where older generated + // projections can be using FindVftblType and using this to cast. + [Guid("3C2925FE-8519-45C1-AA79-197B6718C1C1")] +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + public unsafe struct Vftbl +#pragma warning restore CA2257 + { + internal IInspectable.Vftbl IInspectableVftbl; + + public static readonly IntPtr AbiToProjectionVftablePtr = ABI.System.Collections.Generic.IDictionary.AbiToProjectionVftablePtr; + + public static readonly Guid PIID = ABI.System.Collections.Generic.IDictionary.PIID; + } + + private static readonly ConditionalWeakTable, ToAbiHelper> _adapterTable = new(); + + internal static global::Windows.Foundation.Collections.IMap FindAdapter(IntPtr thisPtr) + { + var __this = global::WinRT.ComWrappersSupport.FindObject>(thisPtr); + return _adapterTable.GetValue(__this, (dictionary) => new ToAbiHelper(dictionary)); + } + + public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) + { + if (thisPtr == IntPtr.Zero) + { + return null; + } + return ObjectReference.FromAbi(thisPtr, PIID); + } + + public static readonly Guid PIID = IDictionaryMethods.PIID; + + global::System.Collections.Generic.ICollection global::System.Collections.Generic.IDictionary.Keys + { + get + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle); + return IDictionaryMethods.get_Keys(_obj); + } + } + + global::System.Collections.Generic.ICollection global::System.Collections.Generic.IDictionary.Values + { + get + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle); + return IDictionaryMethods.get_Values(_obj); + } + } + + int global::System.Collections.Generic.ICollection>.Count + { + get + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle); + return IDictionaryMethods.get_Count(_obj); + } + } + + bool global::System.Collections.Generic.ICollection>.IsReadOnly + { + get + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle); + return IDictionaryMethods.get_IsReadOnly(_obj); + } + } + + V global::System.Collections.Generic.IDictionary.this[K key] + { + get + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle); + return IDictionaryMethods.Indexer_Get(_obj, null, key); + } + set + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle); + IDictionaryMethods.Indexer_Set(_obj, key, value); + } + } + + void global::System.Collections.Generic.IDictionary.Add(K key, V value) + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle); + IDictionaryMethods.Add(_obj, key, value); + } + + bool global::System.Collections.Generic.IDictionary.ContainsKey(K key) + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle); + return IDictionaryMethods.ContainsKey(_obj, key); + } + + bool global::System.Collections.Generic.IDictionary.Remove(K key) + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle); + return IDictionaryMethods.Remove(_obj, key); + } + + bool global::System.Collections.Generic.IDictionary.TryGetValue(K key, out V value) + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle); + return IDictionaryMethods.TryGetValue(_obj, null, key, out value); + } + + void global::System.Collections.Generic.ICollection>.Add(global::System.Collections.Generic.KeyValuePair item) + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle); + IDictionaryMethods.Add(_obj, item); + } + + bool global::System.Collections.Generic.ICollection>.Contains(global::System.Collections.Generic.KeyValuePair item) + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle); + return IDictionaryMethods.Contains(_obj, null, item); + } + + void global::System.Collections.Generic.ICollection>.CopyTo(global::System.Collections.Generic.KeyValuePair[] array, int arrayIndex) + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle); + ((IWinRTObject)this).IsInterfaceImplemented(typeof(global::System.Collections.Generic.IEnumerable>).TypeHandle, true); + var _objEnumerable = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IEnumerable>).TypeHandle); + IDictionaryMethods.CopyTo(_obj, _objEnumerable, array, arrayIndex); + } + + bool global::System.Collections.Generic.ICollection>.Remove(global::System.Collections.Generic.KeyValuePair item) + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle); + return IDictionaryMethods.Remove(_obj, item); + } + + void global::System.Collections.Generic.ICollection>.Clear() + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IDictionary).TypeHandle); + IDictionaryMethods.Clear(_obj); + } + + global::System.Collections.Generic.IEnumerator> global::System.Collections.Generic.IEnumerable>.GetEnumerator() + { + ((IWinRTObject)this).IsInterfaceImplemented(typeof(global::System.Collections.Generic.IEnumerable>).TypeHandle, true); + var _objEnumerable = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IEnumerable>).TypeHandle); + return IEnumerableMethods>.GetEnumerator(_objEnumerable); + } + + IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + } + +#if EMBED + internal +#else + public +#endif + static class IDictionary_Delegates + { + public unsafe delegate int GetView_3(IntPtr thisPtr, out IntPtr __return_value__); + internal unsafe delegate int GetView_3_Abi(IntPtr thisPtr, IntPtr* __return_value__); + public unsafe delegate int Clear_6(IntPtr thisPtr); + } +} diff --git a/src/WinRT.Runtime/Projections/IDictionary.netstandard2.0.cs b/src/WinRT.Runtime/Projections/IDictionary.netstandard2.0.cs index 731c92807..be70a8eef 100644 --- a/src/WinRT.Runtime/Projections/IDictionary.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/IDictionary.netstandard2.0.cs @@ -521,17 +521,17 @@ public struct Vftbl public global::System.Delegate Insert_4; public global::System.Delegate Remove_5; public IDictionary_Delegates.Clear_6 Clear_6; - public static Guid PIID = GuidGenerator.CreateIID(typeof(IDictionary)); - private static readonly Type Lookup_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, Marshaler.AbiType.MakeByRefType(), typeof(int) }); - private static readonly Type HasKey_2_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(byte).MakeByRefType(), typeof(int) }); - private static readonly Type Insert_4_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, Marshaler.AbiType, typeof(byte).MakeByRefType(), typeof(int) }); - private static readonly Type Remove_5_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(int) }); + public static readonly Guid PIID = GuidGenerator.CreateIIDUnsafe(typeof(IDictionary)); + private static readonly Type Lookup_0_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, Marshaler.AbiType.MakeByRefType(), typeof(int) }); + private static readonly Type HasKey_2_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(byte).MakeByRefType(), typeof(int) }); + private static readonly Type Insert_4_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, Marshaler.AbiType, typeof(byte).MakeByRefType(), typeof(int) }); + private static readonly Type Remove_5_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(int) }); internal unsafe Vftbl(IntPtr thisPtr) { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); + var vftblPtr = *(void***)thisPtr; + var vftbl = (IntPtr*)vftblPtr; + IInspectableVftbl = *(IInspectable.Vftbl*)vftblPtr; Lookup_0 = Marshal.GetDelegateForFunctionPointer(vftbl[6], Lookup_0_Type); get_Size_1 = Marshal.GetDelegateForFunctionPointer<_get_PropertyAsUInt32>(vftbl[7]); HasKey_2 = Marshal.GetDelegateForFunctionPointer(vftbl[8], HasKey_2_Type); @@ -709,7 +709,7 @@ public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) var vftblT = new Vftbl(thisPtr); return ObjectReference.FromAbi(thisPtr, vftblT); } - public static Guid PIID = Vftbl.PIID; + public static readonly Guid PIID = Vftbl.PIID; public static implicit operator IDictionary(IObjectReference obj) => (obj != null) ? new IDictionary(obj) : null; public static implicit operator IDictionary(ObjectReference obj) => (obj != null) ? new IDictionary(obj) : null; @@ -736,6 +736,7 @@ public unsafe V Lookup(K key) __key = Marshaler.CreateMarshaler2(key); __params[1] = Marshaler.GetAbi(__key); _obj.Vftbl.Lookup_0.DynamicInvokeAbi(__params); + GC.KeepAlive(_obj); return Marshaler.FromAbi(__params[2]); } finally @@ -754,6 +755,7 @@ public unsafe bool HasKey(K key) __key = Marshaler.CreateMarshaler2(key); __params[1] = Marshaler.GetAbi(__key); _obj.Vftbl.HasKey_2.DynamicInvokeAbi(__params); + GC.KeepAlive(_obj); return (byte)__params[2] != 0; } finally @@ -768,6 +770,7 @@ public unsafe bool HasKey(K key) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetView_3(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return MarshalInterface>.FromAbi(__retval); } finally @@ -788,6 +791,8 @@ public unsafe bool Insert(K key, V value) __value = Marshaler.CreateMarshaler2(value); __params[2] = Marshaler.GetAbi(__value); _obj.Vftbl.Insert_4.DynamicInvokeAbi(__params); + GC.KeepAlive(_obj); + GC.KeepAlive(__params); return (byte)__params[3] != 0; } finally @@ -806,6 +811,8 @@ public unsafe void _Remove(K key) __key = Marshaler.CreateMarshaler2(key); __params[1] = Marshaler.GetAbi(__key); _obj.Vftbl.Remove_5.DynamicInvokeAbi(__params); + GC.KeepAlive(_obj); + GC.KeepAlive(__params); } finally { @@ -816,6 +823,7 @@ public unsafe void _Remove(K key) public unsafe void Clear() { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Clear_6(ThisPtr)); + GC.KeepAlive(_obj); } public unsafe uint Size @@ -823,7 +831,8 @@ public unsafe uint Size get { uint __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Size_1(ThisPtr, out __retval)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Size_1(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } } diff --git a/src/WinRT.Runtime/Projections/IDisposable.net5.cs b/src/WinRT.Runtime/Projections/IDisposable.net5.cs index baca42f8e..4690100d2 100644 --- a/src/WinRT.Runtime/Projections/IDisposable.net5.cs +++ b/src/WinRT.Runtime/Projections/IDisposable.net5.cs @@ -3,74 +3,65 @@ using System; using System.ComponentModel; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using WinRT; - + namespace ABI.System -{ - [DynamicInterfaceCastableImplementation] - [EditorBrowsable(EditorBrowsableState.Never)] - [Guid("30D5A829-7FA4-4026-83BB-D75BAE4EA99E")] +{ #if EMBED internal #else public #endif - unsafe interface IDisposable : global::System.IDisposable - { - [Guid("30D5A829-7FA4-4026-83BB-D75BAE4EA99E")] - public struct Vftbl - { - private delegate int CloseDelegate(IntPtr thisPtr); - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Close_0; - public delegate* unmanaged[Stdcall] Close_0 { get => (delegate* unmanaged[Stdcall])_Close_0; set => _Close_0 = value; } - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - - - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - - _Close_0 = (delegate* unmanaged)&Do_Abi_Close_0 - - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_Close_0(IntPtr thisPtr) - { - - - try - { - global::WinRT.ComWrappersSupport.FindObject(thisPtr).Dispose(); + static class IDisposableMethods + { + public static global::System.Guid IID => global::WinRT.Interop.IID.IID_IDisposable; + + public static IntPtr AbiToProjectionVftablePtr => IDisposable.AbiToProjectionVftablePtr; + + public static unsafe void Dispose(IObjectReference obj) + { + var ThisPtr = obj.ThisPtr; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6](ThisPtr)); + GC.KeepAlive(obj); + } + } + + [DynamicInterfaceCastableImplementation] + [EditorBrowsable(EditorBrowsableState.Never)] + [Guid("30D5A829-7FA4-4026-83BB-D75BAE4EA99E")] + internal unsafe interface IDisposable : global::System.IDisposable + { + public static readonly IntPtr AbiToProjectionVftablePtr; - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } + static unsafe IDisposable() + { + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(IDisposable), sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[6] = &Do_Abi_Close_0; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_Close_0(IntPtr thisPtr) + { + try + { + global::WinRT.ComWrappersSupport.FindObject(thisPtr).Dispose(); + + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); unsafe void global::System.IDisposable.Dispose() { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.IDisposable).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Close_0(ThisPtr)); + var obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.IDisposable).TypeHandle); + IDisposableMethods.Dispose(obj); } } } diff --git a/src/WinRT.Runtime/Projections/IDisposable.netstandard2.0.cs b/src/WinRT.Runtime/Projections/IDisposable.netstandard2.0.cs index ae65878be..f0b5c2076 100644 --- a/src/WinRT.Runtime/Projections/IDisposable.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/IDisposable.netstandard2.0.cs @@ -80,6 +80,7 @@ internal IDisposable(ObjectReference obj) public unsafe void Dispose() { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Close_0(ThisPtr)); + GC.KeepAlive(_obj); } } } diff --git a/src/WinRT.Runtime/Projections/IEnumerable.net5.cs b/src/WinRT.Runtime/Projections/IEnumerable.net5.cs index f8b9b4f03..f0ad6f216 100644 --- a/src/WinRT.Runtime/Projections/IEnumerable.net5.cs +++ b/src/WinRT.Runtime/Projections/IEnumerable.net5.cs @@ -1,727 +1,1276 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using Microsoft.UI.Xaml.Interop; -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.InteropServices; -using WinRT; -using WinRT.Interop; - -#pragma warning disable 0169 // warning CS0169: The field '...' is never used -#pragma warning disable 0649 // warning CS0169: Field '...' is never assigned to - -namespace Windows.Foundation.Collections -{ - - [Guid("FAA585EA-6214-4217-AFDA-7F46DE5869B3")] - internal interface IIterable - { - IEnumerator First(); // Combining IIterable & IEnumerator needs redesign - } - - - [Guid("6A79E863-4300-459A-9966-CBB660963EE1")] - internal interface IIterator - { - bool _MoveNext(); - uint GetMany(ref T[] items); - T _Current { get; } - bool HasCurrent { get; } - } -} - -namespace ABI.Windows.Foundation.Collections -{ - internal static class IIterableMethods - { - public unsafe static IEnumerator First(IObjectReference obj) - { - IntPtr __retval = default; - try - { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.First_0(ThisPtr, out __retval)); - return ABI.System.Collections.Generic.IEnumerator.FromAbi(__retval); - } - finally - { - ABI.System.Collections.Generic.IEnumerator.DisposeAbi(__retval); - } - } - } - - [DynamicInterfaceCastableImplementation] - [Guid("FAA585EA-6214-4217-AFDA-7F46DE5869B3")] - internal interface IIterable : ABI.System.Collections.Generic.IEnumerable - { - public static new Guid PIID = ABI.System.Collections.Generic.IEnumerable.PIID; - } -} - -namespace System.Collections.Generic -{ - internal sealed class IEnumerableImpl : IEnumerable, IWinRTObject - { - private IObjectReference _inner; - - internal IEnumerableImpl(IObjectReference _inner) - { - this._inner = _inner; - } - - public static IEnumerableImpl CreateRcw(IInspectable obj) => new(obj.ObjRef); - - private volatile IObjectReference __iEnumerableObjRef; - private IObjectReference Make_IEnumerableObjRef() - { - global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, _inner.As.Vftbl>(), null); - return __iEnumerableObjRef; - } - private IObjectReference iEnumerableObjRef => __iEnumerableObjRef ?? Make_IEnumerableObjRef(); - - IObjectReference IWinRTObject.NativeObject => _inner; - - bool IWinRTObject.HasUnwrappableNativeObject => true; - - private volatile global::System.Collections.Concurrent.ConcurrentDictionary _queryInterfaceCache; - private global::System.Collections.Concurrent.ConcurrentDictionary MakeQueryInterfaceCache() - { - global::System.Threading.Interlocked.CompareExchange(ref _queryInterfaceCache, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); - return _queryInterfaceCache; - } - global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.QueryInterfaceCache => _queryInterfaceCache ?? MakeQueryInterfaceCache(); - private volatile global::System.Collections.Concurrent.ConcurrentDictionary _additionalTypeData; - private global::System.Collections.Concurrent.ConcurrentDictionary MakeAdditionalTypeData() - { - global::System.Threading.Interlocked.CompareExchange(ref _additionalTypeData, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); - return _additionalTypeData; - } - global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.AdditionalTypeData => _additionalTypeData ?? MakeAdditionalTypeData(); - - public IEnumerator GetEnumerator() - { - return global::ABI.System.Collections.Generic.IEnumerableMethods.GetEnumerator(iEnumerableObjRef); - } - - IEnumerator IEnumerable.GetEnumerator() - { - return GetEnumerator(); - } - } -} - -namespace ABI.System.Collections.Generic -{ - using global::System; - using global::System.Runtime.CompilerServices; - -#if EMBED - internal -#else - public -#endif - static class IEnumerableMethods - { - public static global::System.Collections.Generic.IEnumerator GetEnumerator(IObjectReference obj) - { - var first = ABI.Windows.Foundation.Collections.IIterableMethods.First(obj); - if (first is global::ABI.System.Collections.Generic.IEnumerator iterator) - { - return iterator; - } - throw new InvalidOperationException("Unexpected type for enumerator"); - } - } - - [DynamicInterfaceCastableImplementation] - [Guid("FAA585EA-6214-4217-AFDA-7F46DE5869B3")] - interface IEnumerable : global::System.Collections.Generic.IEnumerable, global::Windows.Foundation.Collections.IIterable - { - public static IObjectReference CreateMarshaler(global::System.Collections.Generic.IEnumerable obj) => - obj is null ? null : ComWrappersSupport.CreateCCWForObject(obj, PIID); - - public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.Generic.IEnumerable obj) => - ComWrappersSupport.CreateCCWForObjectForMarshaling(obj, PIID); - - public static IntPtr GetAbi(IObjectReference objRef) => - objRef?.ThisPtr ?? IntPtr.Zero; - - public static IntPtr FromManaged(global::System.Collections.Generic.IEnumerable value) => - (value is null) ? IntPtr.Zero : CreateMarshaler2(value).Detach(); - - public static void DisposeMarshaler(IObjectReference objRef) => objRef?.Dispose(); - - public static void DisposeAbi(IntPtr abi) => - MarshalInterfaceHelper>.DisposeAbi(abi); - - public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IEnumerable)); - - internal sealed class ToAbiHelper : global::Windows.Foundation.Collections.IIterable - { - private readonly IEnumerable m_enumerable; - - internal ToAbiHelper(IEnumerable enumerable) => m_enumerable = enumerable; - - public global::System.Collections.Generic.IEnumerator First() => m_enumerable.GetEnumerator(); - } - - [Guid("FAA585EA-6214-4217-AFDA-7F46DE5869B3")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _first_0; - public delegate* unmanaged[Stdcall] First_0 { get => (delegate* unmanaged[Stdcall])_first_0; set => _first_0 = (void*)value; } - - public static Guid PIID = GuidGenerator.CreateIID(typeof(IEnumerable)); - - internal unsafe Vftbl(IntPtr thisPtr) : this() - { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); - First_0 = (delegate* unmanaged[Stdcall])vftbl[6]; - } - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - private static readonly Delegate DelegateCache; - - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - _first_0 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache = new IEnumerable_Delegates.First_0(Do_Abi_First_0)), - }; - var nativeVftbl = (IntPtr*)Marshal.AllocCoTaskMem(Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable.IInspectableVftbl, (IntPtr)nativeVftbl, false); - nativeVftbl[6] = (IntPtr)AbiToProjectionVftable.First_0; - - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - private static unsafe int Do_Abi_First_0(IntPtr thisPtr, out IntPtr __return_value__) - { - __return_value__ = default; - try - { - var __this = global::WinRT.ComWrappersSupport.FindObject>(thisPtr); - __return_value__ = MarshalInterface>.FromManaged(__this.GetEnumerator()); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) - { - if (thisPtr == IntPtr.Zero) - { - return null; - } - var vftblT = new Vftbl(thisPtr); - return ObjectReference.FromAbi(thisPtr, vftblT); - } - public static Guid PIID = Vftbl.PIID; - - global::System.Collections.Generic.IEnumerator global::System.Collections.Generic.IEnumerable.GetEnumerator() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IEnumerable).TypeHandle)); - return IEnumerableMethods.GetEnumerator(_obj); - } - - IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); - } -#if EMBED - internal -#else - public -#endif - static class IEnumerable_Delegates - { - public unsafe delegate int First_0(IntPtr thisPtr, out IntPtr __return_value__); - } - - [Guid("6A79E863-4300-459A-9966-CBB660963EE1")] -#if EMBED - internal -#else - public -#endif - class IEnumerator : global::System.Collections.Generic.IEnumerator, global::Windows.Foundation.Collections.IIterator - { - public static IObjectReference CreateMarshaler(global::System.Collections.Generic.IEnumerator obj) => - obj is null ? null : ComWrappersSupport.CreateCCWForObject(obj, PIID); - - public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.Generic.IEnumerator obj) => - ComWrappersSupport.CreateCCWForObjectForMarshaling(obj, PIID); - - public static IntPtr GetAbi(IObjectReference objRef) => - objRef?.ThisPtr ?? IntPtr.Zero; - - public static global::System.Collections.Generic.IEnumerator FromAbi(IntPtr thisPtr) => - thisPtr == IntPtr.Zero ? null : new IEnumerator(ObjRefFromAbi(thisPtr)); - - internal static global::Windows.Foundation.Collections.IIterator FromAbiInternal(IntPtr thisPtr) => - new IEnumerator(ObjRefFromAbi(thisPtr)); - - public static IntPtr FromManaged(global::System.Collections.Generic.IEnumerator value) => - (value is null) ? IntPtr.Zero : CreateMarshaler2(value).Detach(); - - public static void DisposeMarshaler(IObjectReference objRef) => objRef?.Dispose(); - - public static void DisposeAbi(IntPtr abi) => - MarshalInterfaceHelper>.DisposeAbi(abi); - - public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IEnumerator)); - - public class FromAbiHelper : global::System.Collections.Generic.IEnumerator - { - private readonly global::Windows.Foundation.Collections.IIterator _iterator; - - public FromAbiHelper(IObjectReference obj) : - this(new global::ABI.System.Collections.Generic.IEnumerator(obj)) - { - } - - internal FromAbiHelper(global::Windows.Foundation.Collections.IIterator iterator) - { - _iterator = iterator; - } - - private bool m_hadCurrent = true; - private T m_current = default!; - private bool m_isInitialized = false; - - public T Current - { - get - { - // The enumerator has not been advanced to the first element yet. - if (!m_isInitialized) - throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_EnumNotStarted); - // The enumerator has reached the end of the collection - if (!m_hadCurrent) - throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_EnumEnded); - return m_current; - } - } - - object IEnumerator.Current - { - get - { - // The enumerator has not been advanced to the first element yet. - if (!m_isInitialized) - throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_EnumNotStarted); - // The enumerator has reached the end of the collection - if (!m_hadCurrent) - throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_EnumEnded); - return m_current; - } - } - - public bool MoveNext() - { - // If we've passed the end of the iteration, IEnumerable should return false, while - // IIterable will fail the interface call - if (!m_hadCurrent) - { - return false; - } - - // IIterators start at index 0, rather than -1. If this is the first call, we need to just - // check HasCurrent rather than actually moving to the next element - try - { - if (!m_isInitialized) - { - m_hadCurrent = _iterator.HasCurrent; - m_isInitialized = true; - } - else - { - m_hadCurrent = _iterator._MoveNext(); - } - - // We want to save away the current value for two reasons: - // 1. Accessing .Current is cheap on other iterators, so having it be a property which is a - // simple field access preserves the expected performance characteristics (as opposed to - // triggering a COM call every time the property is accessed) - // - // 2. This allows us to preserve the same semantics as generic collection iteration when iterating - // beyond the end of the collection - namely that Current continues to return the last value - // of the collection - if (m_hadCurrent) - { - m_current = _iterator._Current; - } - } - catch (Exception e) - { - // Translate E_CHANGED_STATE into an InvalidOperationException for an updated enumeration - if (Marshal.GetHRForException(e) == ExceptionHelpers.E_CHANGED_STATE) - { - throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_EnumFailedVersion); - } - else - { - throw; - } - } - - return m_hadCurrent; - } - - public void Reset() - { - throw new NotSupportedException(); - } - - public void Dispose() - { - } - } - - public sealed class ToAbiHelper : global::Windows.Foundation.Collections.IIterator, global::Microsoft.UI.Xaml.Interop.IBindableIterator - { - private readonly global::System.Collections.Generic.IEnumerator m_enumerator; - private bool m_firstItem = true; - private bool m_hasCurrent; - - internal ToAbiHelper(global::System.Collections.Generic.IEnumerator enumerator) => m_enumerator = enumerator; - - public T _Current - { - get - { - // IEnumerator starts at item -1, while IIterators start at item 0. Therefore, if this is the - // first access to the iterator we need to advance to the first item. - if (m_firstItem) - { - m_firstItem = false; - _MoveNext(); - } - - if (!m_hasCurrent) - { - ExceptionHelpers.ThrowExceptionForHR(ExceptionHelpers.E_BOUNDS); - } - - return m_enumerator.Current; - } - } - - public bool HasCurrent - { - get - { - // IEnumerator starts at item -1, while IIterators start at item 0. Therefore, if this is the - // first access to the iterator we need to advance to the first item. - if (m_firstItem) - { - m_firstItem = false; - _MoveNext(); - } - - return m_hasCurrent; - } - } - - public bool _MoveNext() - { - try - { - m_hasCurrent = m_enumerator.MoveNext(); - } - catch (InvalidOperationException) - { - ExceptionHelpers.ThrowExceptionForHR(ExceptionHelpers.E_CHANGED_STATE); - } - - return m_hasCurrent; - } - - public uint GetMany(ref T[] items) - { - if (items == null) - { - return 0; - } - - int index = 0; - while (index < items.Length && HasCurrent) - { - items[index] = _Current; - _MoveNext(); - ++index; - } - - if (typeof(T) == typeof(string)) - { - string[] stringItems = (items as string[])!; - - // Fill the rest of the array with string.Empty to avoid marshaling failure - for (int i = index; i < items.Length; ++i) - stringItems[i] = string.Empty; - } - - return (uint)index; - } - - public object Current => _Current; - - public bool MoveNext() => _MoveNext(); - - uint global::Microsoft.UI.Xaml.Interop.IBindableIterator.GetMany(ref object[] items) - { - // Should not be called. - throw new NotImplementedException(); - } - } - - [Guid("6A79E863-4300-459A-9966-CBB660963EE1")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - public global::System.Delegate get_Current_0; - private void* _get_HasCurrent_1; - internal delegate* unmanaged[Stdcall] Get_HasCurrent_1 { get => (delegate* unmanaged[Stdcall])_get_HasCurrent_1; set => _get_HasCurrent_1 = (void*)value; } - public IEnumerator_Delegates.MoveNext_2 MoveNext_2; - public IEnumerator_Delegates.GetMany_3 GetMany_3; - - public static Guid PIID = GuidGenerator.CreateIID(typeof(IEnumerator)); - private static readonly Type get_Current_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType.MakeByRefType(), typeof(int) }); - - internal unsafe Vftbl(IntPtr thisPtr) : this() - { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); - get_Current_0 = Marshal.GetDelegateForFunctionPointer(vftbl[6], get_Current_0_Type); - Get_HasCurrent_1 = (delegate* unmanaged[Stdcall])vftbl[7]; - MoveNext_2 = Marshal.GetDelegateForFunctionPointer(vftbl[8]); - GetMany_3 = Marshal.GetDelegateForFunctionPointer(vftbl[9]); - } - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - private static readonly Delegate DelegateCache; - - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - get_Current_0 = global::System.Delegate.CreateDelegate(get_Current_0_Type, typeof(Vftbl).GetMethod("Do_Abi_get_Current_0", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType)), - _get_HasCurrent_1 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache = new _get_PropertyAsBoolean(Do_Abi_get_HasCurrent_1)), - MoveNext_2 = Do_Abi_MoveNext_2, - GetMany_3 = Do_Abi_GetMany_3 - }; - var nativeVftbl = (IntPtr*)Marshal.AllocCoTaskMem(Marshal.SizeOf() + sizeof(IntPtr) * 4); - Marshal.StructureToPtr(AbiToProjectionVftable.IInspectableVftbl, (IntPtr)nativeVftbl, false); - nativeVftbl[6] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.get_Current_0); - nativeVftbl[7] = (IntPtr)AbiToProjectionVftable.Get_HasCurrent_1; - nativeVftbl[8] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.MoveNext_2); - nativeVftbl[9] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.GetMany_3); - - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - private static ConditionalWeakTable, ToAbiHelper> _adapterTable = - new ConditionalWeakTable, ToAbiHelper>(); - - private static ToAbiHelper FindAdapter(IntPtr thisPtr) - { - var __this = global::WinRT.ComWrappersSupport.FindObject>(thisPtr); - return _adapterTable.GetValue(__this, (enumerator) => new ToAbiHelper(enumerator)); - } - - private static unsafe int Do_Abi_MoveNext_2(IntPtr thisPtr, out byte __return_value__) - { - bool ____return_value__ = default; - - __return_value__ = default; - - try - { - ____return_value__ = FindAdapter(thisPtr)._MoveNext(); - __return_value__ = (byte)(____return_value__ ? 1 : 0); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_GetMany_3(IntPtr thisPtr, int __itemsSize, IntPtr items, out uint __return_value__) - { - uint ____return_value__ = default; - - __return_value__ = default; - T[] __items = Marshaler.FromAbiArray((__itemsSize, items)); - - try - { - ____return_value__ = FindAdapter(thisPtr).GetMany(ref __items); - Marshaler.CopyManagedArray(__items, items); - __return_value__ = ____return_value__; - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_get_Current_0(void* thisPtr, out TAbi __return_value__) - { - T ____return_value__ = default; - - __return_value__ = default; - - try - { - ____return_value__ = FindAdapter(new IntPtr(thisPtr))._Current; - __return_value__ = (TAbi)Marshaler.FromManaged(____return_value__); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - private static unsafe int Do_Abi_get_HasCurrent_1(IntPtr thisPtr, out byte __return_value__) - { - bool ____return_value__ = default; - - __return_value__ = default; - - try - { - ____return_value__ = FindAdapter(thisPtr).HasCurrent; - __return_value__ = (byte)(____return_value__ ? 1 : 0); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) - { - if (thisPtr == IntPtr.Zero) - { - return null; - } - var vftblT = new Vftbl(thisPtr); - return ObjectReference.FromAbi(thisPtr, vftblT); - } - public static Guid PIID = Vftbl.PIID; - - public static implicit operator IEnumerator(IObjectReference obj) => (obj != null) ? new IEnumerator(obj) : null; - public static implicit operator IEnumerator(ObjectReference obj) => (obj != null) ? new IEnumerator(obj) : null; - protected readonly ObjectReference _obj; - public IObjectReference ObjRef { get => _obj; } - - public IntPtr ThisPtr => _obj.ThisPtr; - - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - - public IEnumerator(IObjectReference obj) : this(obj.As()) { } - public IEnumerator(ObjectReference obj) - { - _obj = obj; - _FromIterator = new FromAbiHelper(this); - } - FromAbiHelper _FromIterator; - - public unsafe bool _MoveNext() - { - byte __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.MoveNext_2(ThisPtr, out __retval)); - return __retval != 0; - } - - public unsafe uint GetMany(ref T[] items) - { - object __items = default; - int __items_length = default; - IntPtr __items_data = default; - uint __retval = default; - try - { - __items = Marshaler.CreateMarshalerArray(items); - (__items_length, __items_data) = Marshaler.GetAbiArray(__items); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetMany_3(ThisPtr, __items_length, __items_data, out __retval)); - items = Marshaler.FromAbiArray((__items_length, __items_data)); - return __retval; - } - finally - { - Marshaler.DisposeMarshalerArray(__items); - } - } - - public unsafe T _Current - { - get - { - var __params = new object[] { ThisPtr, null }; - try - { - _obj.Vftbl.get_Current_0.DynamicInvokeAbi(__params); - return Marshaler.FromAbi(__params[1]); - } - finally - { - Marshaler.DisposeAbi(__params[1]); - } - } - } - - public unsafe bool HasCurrent - { - get - { - byte __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Get_HasCurrent_1(ThisPtr, out __retval)); - return __retval != 0; - } - } - - public bool MoveNext() => _FromIterator.MoveNext(); - public void Reset() => _FromIterator.Reset(); - public void Dispose() => _FromIterator.Dispose(); - public T Current => _FromIterator.Current; - object IEnumerator.Current => Current; - } - -#if EMBED - internal -#else - public -#endif - static class IEnumerator_Delegates - { - public unsafe delegate int MoveNext_2(IntPtr thisPtr, out byte __return_value__); - public unsafe delegate int GetMany_3(IntPtr thisPtr, int __itemsSize, IntPtr items, out uint __return_value__); - } -} +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using Windows.Foundation.Collections; +using WinRT; +using WinRT.Interop; + +#pragma warning disable 0169 // warning CS0169: The field '...' is never used +#pragma warning disable 0649 // warning CS0169: Field '...' is never assigned to + +namespace Windows.Foundation.Collections +{ + + [Guid("FAA585EA-6214-4217-AFDA-7F46DE5869B3")] + internal interface IIterable + { + IEnumerator First(); // Combining IIterable & IEnumerator needs redesign + } + + + [Guid("6A79E863-4300-459A-9966-CBB660963EE1")] + internal interface IIterator + { + bool _MoveNext(); + uint GetMany(ref T[] items); + T _Current { get; } + bool HasCurrent { get; } + } +} + +namespace ABI.Windows.Foundation.Collections +{ + internal static class IIterableMethods + { + // These function pointers will be set by IEnumerableMethods + // when it is called by the source generated type or by the fallback + // mechanism if the source generated type wasn't used. + internal volatile unsafe static delegate*> _First; + internal volatile static bool _RcwHelperInitialized; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe void EnsureInitialized() + { + if (RuntimeFeature.IsDynamicCodeCompiled) + { + return; + } + + if (!_RcwHelperInitialized) + { + [DoesNotReturn] + static void ThrowNotInitialized() + { + throw new NotImplementedException( + $"Type '{typeof(global::System.Collections.Generic.IEnumerable)}' was called without initializing the RCW methods using 'IEnumerableMethods.InitRcwHelper'. " + + $"If using 'IDynamicInterfaceCastable' support to do a dynamic cast to this interface, ensure the 'InitRcwHelper' method is called."); + } + + ThrowNotInitialized(); + } + } + + public unsafe static IEnumerator First(IObjectReference obj) + { + // Early return to ensure things are trimmed correctly on NAOT. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + EnsureInitialized(); + return _First(obj); + } + + if (_First != null) + { + return _First(obj); + } + else + { + IntPtr __retval = default; + try + { + var ThisPtr = obj.ThisPtr; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6](ThisPtr, &__retval)); + GC.KeepAlive(obj); + return ABI.System.Collections.Generic.FromAbiEnumerator.FromAbi(__retval); + } + finally + { + ABI.System.Collections.Generic.FromAbiEnumerator.DisposeAbi(__retval); + } + } + } + } + + [DynamicInterfaceCastableImplementation] + [Guid("FAA585EA-6214-4217-AFDA-7F46DE5869B3")] +#pragma warning disable CA2256 // Not implementing IIterable for [DynamicInterfaceCastableImplementation], as we don't expect to need IDIC for WinRT types + internal interface IIterable : ABI.System.Collections.Generic.IEnumerable +#pragma warning restore CA2256 + { + public static new Guid PIID = ABI.System.Collections.Generic.IEnumerableMethods.PIID; + } +} + +namespace System.Collections.Generic +{ + internal sealed class IEnumerableImpl : IEnumerable, IWinRTObject + { + private readonly IObjectReference _inner; + + internal IEnumerableImpl(IObjectReference _inner) + { + this._inner = _inner; + } + + public static IEnumerableImpl CreateRcw(IInspectable obj) => new(obj.ObjRef); + + private volatile IObjectReference __iEnumerableObjRef; + private IObjectReference Make_IEnumerableObjRef() + { + global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, _inner.As(ABI.System.Collections.Generic.IEnumerableMethods.PIID), null); + return __iEnumerableObjRef; + } + private IObjectReference iEnumerableObjRef => __iEnumerableObjRef ?? Make_IEnumerableObjRef(); + + IObjectReference IWinRTObject.NativeObject => _inner; + + bool IWinRTObject.HasUnwrappableNativeObject => true; + + private volatile global::System.Collections.Concurrent.ConcurrentDictionary _queryInterfaceCache; + private global::System.Collections.Concurrent.ConcurrentDictionary MakeQueryInterfaceCache() + { + global::System.Threading.Interlocked.CompareExchange(ref _queryInterfaceCache, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); + return _queryInterfaceCache; + } + global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.QueryInterfaceCache => _queryInterfaceCache ?? MakeQueryInterfaceCache(); + private volatile global::System.Collections.Concurrent.ConcurrentDictionary _additionalTypeData; + private global::System.Collections.Concurrent.ConcurrentDictionary MakeAdditionalTypeData() + { + global::System.Threading.Interlocked.CompareExchange(ref _additionalTypeData, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); + return _additionalTypeData; + } + global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.AdditionalTypeData => _additionalTypeData ?? MakeAdditionalTypeData(); + + public IEnumerator GetEnumerator() + { + return global::ABI.System.Collections.Generic.IEnumerableMethods.GetEnumerator(iEnumerableObjRef); + } + + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + } + + internal sealed class IEnumeratorImpl : IEnumerator, IIterator, IWinRTObject + { + private readonly IObjectReference _inner; + + internal IEnumeratorImpl(IObjectReference _inner) + { + this._inner = _inner; + } + + public static IEnumeratorImpl CreateRcw(IInspectable obj) => new(obj.ObjRef); + + private volatile IObjectReference __iEnumeratorObjRef; + private IObjectReference Make_IEnumeratorObjRef() + { + global::System.Threading.Interlocked.CompareExchange(ref __iEnumeratorObjRef, _inner.As(ABI.System.Collections.Generic.IEnumeratorMethods.PIID), null); + return __iEnumeratorObjRef; + } + private IObjectReference iEnumeratorObjRef => __iEnumeratorObjRef ?? Make_IEnumeratorObjRef(); + + IObjectReference IWinRTObject.NativeObject => _inner; + + bool IWinRTObject.HasUnwrappableNativeObject => true; + + private volatile global::System.Collections.Concurrent.ConcurrentDictionary _queryInterfaceCache; + private global::System.Collections.Concurrent.ConcurrentDictionary MakeQueryInterfaceCache() + { + global::System.Threading.Interlocked.CompareExchange(ref _queryInterfaceCache, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); + return _queryInterfaceCache; + } + global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.QueryInterfaceCache => _queryInterfaceCache ?? MakeQueryInterfaceCache(); + private volatile global::System.Collections.Concurrent.ConcurrentDictionary _additionalTypeData; + private global::System.Collections.Concurrent.ConcurrentDictionary MakeAdditionalTypeData() + { + global::System.Threading.Interlocked.CompareExchange(ref _additionalTypeData, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); + return _additionalTypeData; + } + + global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.AdditionalTypeData => _additionalTypeData ?? MakeAdditionalTypeData(); + + bool IEnumerator.MoveNext() + { + return global::ABI.System.Collections.Generic.IEnumeratorMethods.MoveNext(iEnumeratorObjRef); + } + + void IEnumerator.Reset() + { + global::ABI.System.Collections.Generic.IEnumeratorMethods.Reset(iEnumeratorObjRef); + } + + void IDisposable.Dispose() + { + global::ABI.System.Collections.Generic.IEnumeratorMethods.Dispose(iEnumeratorObjRef); + } + + bool IIterator._MoveNext() + { + return global::ABI.System.Collections.Generic.IIteratorMethods.MoveNext(iEnumeratorObjRef); + } + + uint IIterator.GetMany(ref T[] items) + { + return global::ABI.System.Collections.Generic.IIteratorMethods.GetMany(iEnumeratorObjRef, ref items); + } + + public T Current => global::ABI.System.Collections.Generic.IEnumeratorMethods.get_Current(iEnumeratorObjRef); + + object IEnumerator.Current => Current; + + T IIterator._Current => global::ABI.System.Collections.Generic.IIteratorMethods.get_Current(iEnumeratorObjRef); + + bool IIterator.HasCurrent => global::ABI.System.Collections.Generic.IIteratorMethods.get_HasCurrent(iEnumeratorObjRef); + } +} + +namespace ABI.System.Collections.Generic +{ + using global::System; + using global::System.Diagnostics.CodeAnalysis; + using global::System.Runtime.CompilerServices; + +#if EMBED + internal +#else + public +#endif + static class IEnumerableMethods + { + static IEnumerableMethods() + { + ComWrappersSupport.RegisterHelperType(typeof(global::System.Collections.Generic.IEnumerable), typeof(global::ABI.System.Collections.Generic.IEnumerable)); + } + + public static global::System.Collections.Generic.IEnumerator GetEnumerator(IObjectReference obj) + { + var first = ABI.Windows.Foundation.Collections.IIterableMethods.First(obj); + if (first is global::ABI.System.Collections.Generic.FromAbiEnumerator iterator) + { + return iterator; + } + else if (first is global::System.Collections.Generic.IEnumeratorImpl ienumeratorImpl) + { + return new global::ABI.System.Collections.Generic.FromAbiEnumerator(ienumeratorImpl); + } + else if (first is global::ABI.System.Collections.Generic.ToAbiEnumeratorAdapter toAbiAdapter) + { + // Unwrap to get the C# enumerator if it was originally one. + return toAbiAdapter.m_enumerator; + } + else if (first is IWinRTObject winrtObject) + { + // This is a projected RCW implementing IEnumerator, but since it is being used + // as part of GetEnumerator, we need to remap the enumerator starting index. + // Due to that, we don't return the RCW itself. + return new global::ABI.System.Collections.Generic.FromAbiEnumerator(winrtObject.NativeObject); + } + + throw new InvalidOperationException("Unexpected type for enumerator"); + } + + private static IntPtr abiToProjectionVftablePtr; + public static IntPtr AbiToProjectionVftablePtr => abiToProjectionVftablePtr; + + internal static bool TryInitCCWVtable(IntPtr ptr) + { + return global::System.Threading.Interlocked.CompareExchange(ref abiToProjectionVftablePtr, ptr, IntPtr.Zero) == IntPtr.Zero; + } + + public static global::System.Collections.Generic.IEnumerator Abi_First_0(IntPtr thisPtr) + { + var __this = global::WinRT.ComWrappersSupport.FindObject>(thisPtr); + return new ToAbiEnumeratorAdapter(__this.GetEnumerator()); + } + + internal static readonly Guid PIID = GuidGenerator.CreateIIDUnsafe(typeof(IEnumerable)); + public static Guid IID => PIID; + } + +#if EMBED + internal +#else + public +#endif + static class IEnumerableMethods where TAbi : unmanaged + { + public unsafe static bool InitRcwHelper(delegate*> first) + { + if (ABI.Windows.Foundation.Collections.IIterableMethods._RcwHelperInitialized) + { + return true; + } + + ABI.Windows.Foundation.Collections.IIterableMethods._First = first; + ComWrappersSupport.RegisterTypedRcwFactory( + typeof(global::System.Collections.Generic.IEnumerable), + IEnumerableImpl.CreateRcw); + ComWrappersSupport.RegisterHelperType(typeof(global::System.Collections.Generic.IEnumerable), typeof(global::ABI.System.Collections.Generic.IEnumerable)); + + ABI.Windows.Foundation.Collections.IIterableMethods._RcwHelperInitialized = true; + return true; + } + + public static unsafe bool InitCcw( + delegate* unmanaged[Stdcall] first) + { + if (IEnumerableMethods.AbiToProjectionVftablePtr != default) + { + return false; + } + + var abiToProjectionVftablePtr = (IntPtr)NativeMemory.AllocZeroed((nuint)(sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * 1)); + *(IInspectable.Vftbl*)abiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[6] = first; + + if (!IEnumerableMethods.TryInitCCWVtable(abiToProjectionVftablePtr)) + { + NativeMemory.Free((void*)abiToProjectionVftablePtr); + return false; + } + + // Register generic helper types referenced in CCW. + ComWrappersSupport.RegisterHelperType(typeof(global::System.Collections.Generic.IEnumerator), typeof(global::ABI.System.Collections.Generic.IEnumerator)); + + return true; + } + + private static IEnumerable_Delegates.First_0_Abi DelegateCache; + + internal static unsafe void InitFallbackCCWVtable() + { + DelegateCache = new IEnumerable_Delegates.First_0_Abi(Do_Abi_First_0); + InitCcw((delegate* unmanaged[Stdcall]) Marshal.GetFunctionPointerForDelegate(DelegateCache)); + } + + private static unsafe int Do_Abi_First_0(IntPtr thisPtr, IntPtr* __return_value__) + { + *__return_value__ = default; + try + { + *__return_value__ = MarshalInterface>.FromManaged(IEnumerableMethods.Abi_First_0(thisPtr)); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + + [DynamicInterfaceCastableImplementation] + [Guid("FAA585EA-6214-4217-AFDA-7F46DE5869B3")] +#pragma warning disable CA2256 // Not implementing IIterable for [DynamicInterfaceCastableImplementation], as we don't expect to need IDIC for WinRT types + interface IEnumerable : global::System.Collections.Generic.IEnumerable, global::Windows.Foundation.Collections.IIterable +#pragma warning restore CA2256 + { + public static IObjectReference CreateMarshaler(global::System.Collections.Generic.IEnumerable obj) => + obj is null ? null : ComWrappersSupport.CreateCCWForObject(obj, PIID); + + public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.Generic.IEnumerable obj) => + ComWrappersSupport.CreateCCWForObjectForMarshaling(obj, PIID); + + public static IntPtr GetAbi(IObjectReference objRef) => + objRef?.ThisPtr ?? IntPtr.Zero; + + public static IntPtr FromManaged(global::System.Collections.Generic.IEnumerable value) => + (value is null) ? IntPtr.Zero : CreateMarshaler2(value).Detach(); + + public static void DisposeMarshaler(IObjectReference objRef) => objRef?.Dispose(); + + public static void DisposeAbi(IntPtr abi) => + MarshalInterfaceHelper>.DisposeAbi(abi); + + public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IEnumerable)); + +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + internal sealed class ToAbiHelper : global::Windows.Foundation.Collections.IIterable +#pragma warning restore CA2257 + { + private readonly IEnumerable m_enumerable; + + internal ToAbiHelper(IEnumerable enumerable) => m_enumerable = enumerable; + + public global::System.Collections.Generic.IEnumerator First() => m_enumerable.GetEnumerator(); + } + + public static readonly IntPtr AbiToProjectionVftablePtr; + static IEnumerable() + { + if (RuntimeFeature.IsDynamicCodeCompiled) + { + // Simple invocation guarded by a direct runtime feature check to help the linker. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + InitFallbackCCWVTableIfNeeded(); +#pragma warning restore IL3050 + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2080", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] + [UnconditionalSuppressMessage("Trimming", "IL2081", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] +#endif + [MethodImpl(MethodImplOptions.NoInlining)] + static void InitFallbackCCWVTableIfNeeded() + { + if (IEnumerableMethods.AbiToProjectionVftablePtr == default) + { + // Handle the compat scenario where the source generator wasn't used or IDIC was used. + var initFallbackCCWVtable = (Action)typeof(IEnumerableMethods<,>).MakeGenericType(typeof(T), Marshaler.AbiType). + GetMethod("InitFallbackCCWVtable", BindingFlags.NonPublic | BindingFlags.Static). + CreateDelegate(typeof(Action)); + initFallbackCCWVtable(); + } + } + } + + AbiToProjectionVftablePtr = IEnumerableMethods.AbiToProjectionVftablePtr; + } + + // This is left here for backwards compat purposes where older generated + // projections can be using FindVftblType and using this to cast. + [Guid("FAA585EA-6214-4217-AFDA-7F46DE5869B3")] +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + public unsafe struct Vftbl +#pragma warning restore CA2257 + { + internal IInspectable.Vftbl IInspectableVftbl; + + public static readonly IntPtr AbiToProjectionVftablePtr = ABI.System.Collections.Generic.IEnumerable.AbiToProjectionVftablePtr; + + public static readonly Guid PIID = ABI.System.Collections.Generic.IEnumerable.PIID; + } + + public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) + { + if (thisPtr == IntPtr.Zero) + { + return null; + } + return ObjectReference.FromAbi(thisPtr, PIID); + } + + public static readonly Guid PIID = ABI.System.Collections.Generic.IEnumerableMethods.PIID; + + global::System.Collections.Generic.IEnumerator global::System.Collections.Generic.IEnumerable.GetEnumerator() + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IEnumerable).TypeHandle); + return IEnumerableMethods.GetEnumerator(_obj); + } + + IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + } + +#if EMBED + internal +#else + public +#endif + static class IEnumerable_Delegates + { + public unsafe delegate int First_0(IntPtr thisPtr, out IntPtr __return_value__); + + internal unsafe delegate int First_0_Abi(IntPtr thisPtr, IntPtr* __return_value__); + } + + internal static class IIteratorMethods + { + // These function pointers will be set by IEnumeratorMethods + // when it is called by the source generated type or by the fallback + // mechanism if the source generated type wasn't used. + internal volatile unsafe static delegate* _GetCurrent; + internal volatile unsafe static delegate* _GetMany; + internal volatile static bool _RcwHelperInitialized; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe void EnsureInitialized() + { + if (RuntimeFeature.IsDynamicCodeCompiled) + { + return; + } + + if (!_RcwHelperInitialized) + { + [DoesNotReturn] + static void ThrowNotInitialized() + { + throw new NotImplementedException( + $"'{typeof(global::System.Collections.Generic.IEnumerator)}' was called without initializing the RCW methods using 'IEnumeratorMethods.InitRcwHelper'. " + + $"If using IDynamicInterfaceCastable support to do a dynamic cast to this interface, ensure InitRcwHelper is called."); + } + + ThrowNotInitialized(); + } + } + + public static unsafe bool MoveNext(IObjectReference obj) + { + var ThisPtr = obj.ThisPtr; + byte __retval = default; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[8](ThisPtr, &__retval)); + GC.KeepAlive(obj); + return __retval != 0; + } + + public static unsafe uint GetMany(IObjectReference obj, ref T[] items) + { + // Early return to ensure things are trimmed correctly on NAOT. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + EnsureInitialized(); + return _GetMany(obj, items); + } + + if (_GetMany != null) + { + return _GetMany(obj, items); + } + else + { + var ThisPtr = obj.ThisPtr; + + object __items = default; + int __items_length = default; + IntPtr __items_data = default; + uint __retval = default; +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + try + { + __items = Marshaler.CreateMarshalerArray(items); + (__items_length, __items_data) = Marshaler.GetAbiArray(__items); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[9](ThisPtr, __items_length, __items_data, &__retval)); + GC.KeepAlive(obj); + items = Marshaler.FromAbiArray((__items_length, __items_data)); + return __retval; + } + finally + { + Marshaler.DisposeMarshalerArray(__items); + } +#pragma warning restore IL3050 + } + } + + public static unsafe T get_Current(IObjectReference obj) + { + EnsureInitialized(); + return _GetCurrent(obj); + } + + public static unsafe bool get_HasCurrent(IObjectReference obj) + { + var ThisPtr = obj.ThisPtr; + byte __retval = default; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[7](ThisPtr, &__retval)); + GC.KeepAlive(obj); + return __retval != 0; + } + } + +#if EMBED + internal +#else + public +#endif + static class IEnumeratorMethods + { + unsafe static IEnumeratorMethods() + { + ComWrappersSupport.RegisterHelperType(typeof(global::System.Collections.Generic.IEnumerator), typeof(global::ABI.System.Collections.Generic.IEnumerator)); + + // Early return to ensure things are trimmed correctly on NAOT. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return; + } + +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + InitRcwHelperFallbackIfNeeded(); +#pragma warning restore IL3050 + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2080", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] + [UnconditionalSuppressMessage("Trimming", "IL2081", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] +#endif + [MethodImpl(MethodImplOptions.NoInlining)] + static void InitRcwHelperFallbackIfNeeded() + { + // Handle the compat scenario where the source generator wasn't used and IDIC hasn't been used yet + // and due to that the function pointers haven't been initialized. + if (!IIteratorMethods._RcwHelperInitialized) + { + var initRcwHelperFallback = (Func)typeof(IEnumeratorMethods<,>).MakeGenericType(typeof(T), Marshaler.AbiType). + GetMethod("InitRcwHelperFallback", BindingFlags.NonPublic | BindingFlags.Static). + CreateDelegate(typeof(Func)); + initRcwHelperFallback(); + } + } + } + + public static T get_Current(IObjectReference obj) + { + return IIteratorMethods.get_Current(obj); + } + + public static bool MoveNext(IObjectReference obj) + { + return IIteratorMethods.MoveNext(obj); + } + + public static void Reset(IObjectReference obj) + { + throw new NotSupportedException(); + } + + public static void Dispose(IObjectReference obj) + { + } + + private static IntPtr abiToProjectionVftablePtr; + public static IntPtr AbiToProjectionVftablePtr => abiToProjectionVftablePtr; + + internal static bool TryInitCCWVtable(IntPtr ptr) + { + return global::System.Threading.Interlocked.CompareExchange(ref abiToProjectionVftablePtr, ptr, IntPtr.Zero) == IntPtr.Zero; + } + + public static bool Abi_MoveNext_2(IntPtr thisPtr) + { + return IEnumerator.FindAdapter(thisPtr)._MoveNext(); + } + + public static uint Abi_GetMany_3(IntPtr thisPtr, ref T[] items) + { + return IEnumerator.FindAdapter(thisPtr).GetMany(ref items); + } + + public static T Abi_get_Current_0(IntPtr thisPtr) + { + return IEnumerator.FindAdapter(thisPtr)._Current; + } + + public static bool Abi_get_HasCurrent_1(IntPtr thisPtr) + { + return IEnumerator.FindAdapter(thisPtr).HasCurrent; + } + + internal static readonly Guid PIID = GuidGenerator.CreateIIDUnsafe(typeof(IEnumerator)); + public static Guid IID => PIID; + } + +#if EMBED + internal +#else + public +#endif + static class IEnumeratorMethods where TAbi : unmanaged + { + public unsafe static bool InitRcwHelper( + delegate* getCurrent, + delegate* getMany) + { + if (IIteratorMethods._RcwHelperInitialized) + { + return true; + } + + IIteratorMethods._GetCurrent = getCurrent; + IIteratorMethods._GetMany = getMany; + + ComWrappersSupport.RegisterTypedRcwFactory( + typeof(global::System.Collections.Generic.IEnumerator), + IEnumeratorImpl.CreateRcw); + ComWrappersSupport.RegisterHelperType(typeof(global::System.Collections.Generic.IEnumerator), typeof(global::ABI.System.Collections.Generic.IEnumerator)); + + IIteratorMethods._RcwHelperInitialized = true; + return true; + } + +#if NET + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private unsafe static bool InitRcwHelperFallback() + { + return InitRcwHelper(&get_Current, null); + } + +#if NET + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private unsafe static T get_Current(IObjectReference obj) + { + var ThisPtr = obj.ThisPtr; + TAbi result = default; + try + { + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6](ThisPtr, &result)); + GC.KeepAlive(obj); + return Marshaler.FromAbi(result); + } + finally + { + Marshaler.DisposeAbi(result); + } + } + + public static unsafe bool InitCcw( + delegate* unmanaged[Stdcall] getCurrent, + delegate* unmanaged[Stdcall] hasCurrent, + delegate* unmanaged[Stdcall] moveNext, + delegate* unmanaged[Stdcall] getMany) + { + if (IEnumeratorMethods.AbiToProjectionVftablePtr != default) + { + return false; + } + + var abiToProjectionVftablePtr = (IntPtr)NativeMemory.AllocZeroed((nuint)(sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * 4)); + *(IInspectable.Vftbl*)abiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[6] = getCurrent; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[7] = hasCurrent; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[8] = moveNext; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[9] = getMany; + + if (!IEnumeratorMethods.TryInitCCWVtable(abiToProjectionVftablePtr)) + { + NativeMemory.Free((void*)abiToProjectionVftablePtr); + return false; + } + + return true; + } + + private static global::System.Delegate[] DelegateCache; + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + internal static unsafe void InitFallbackCCWVtable() + { + Type get_Current_0_Type = Projections.GetAbiDelegateType(new Type[] { typeof(IntPtr), typeof(TAbi*), typeof(int) }); + + DelegateCache = new global::System.Delegate[] + { + global::System.Delegate.CreateDelegate(get_Current_0_Type, typeof(IEnumeratorMethods).GetMethod(nameof(Do_Abi_get_Current_0), BindingFlags.NonPublic | BindingFlags.Static)), + new _get_PropertyAsBoolean_Abi(Do_Abi_get_HasCurrent_1), + new IEnumerator_Delegates.MoveNext_2_Abi(Do_Abi_MoveNext_2), + new IEnumerator_Delegates.GetMany_3_Abi(Do_Abi_GetMany_3) + }; + + InitCcw( + (delegate* unmanaged[Stdcall]) Marshal.GetFunctionPointerForDelegate(DelegateCache[0]), + (delegate* unmanaged[Stdcall]) Marshal.GetFunctionPointerForDelegate(DelegateCache[1]), + (delegate* unmanaged[Stdcall]) Marshal.GetFunctionPointerForDelegate(DelegateCache[2]), + (delegate* unmanaged[Stdcall]) Marshal.GetFunctionPointerForDelegate(DelegateCache[3]) + ); + } + + private static unsafe int Do_Abi_MoveNext_2(IntPtr thisPtr, byte* __return_value__) + { + bool ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = IEnumerator.FindAdapter(thisPtr)._MoveNext(); + *__return_value__ = (byte)(____return_value__ ? 1 : 0); + + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + +#if NET + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_GetMany_3(IntPtr thisPtr, int __itemsSize, IntPtr items, uint* __return_value__) + { + uint ____return_value__ = default; + + *__return_value__ = default; + T[] __items = Marshaler.FromAbiArray((__itemsSize, items)); + + try + { + ____return_value__ = IEnumerator.FindAdapter(thisPtr).GetMany(ref __items); + Marshaler.CopyManagedArray(__items, items); + *__return_value__ = ____return_value__; + + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + +#if NET + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_get_Current_0(IntPtr thisPtr, TAbi* __return_value__) + { + T ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = IEnumerator.FindAdapter(thisPtr)._Current; + *__return_value__ = (TAbi)Marshaler.FromManaged(____return_value__); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe int Do_Abi_get_HasCurrent_1(IntPtr thisPtr, byte* __return_value__) + { + bool ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = IEnumerator.FindAdapter(thisPtr).HasCurrent; + *__return_value__ = (byte)(____return_value__ ? 1 : 0); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + + // Used by GetEnumerator to provide an IIterator mapping to C# that follows C# conventions + // such as the enumerator starting at index -1 rather than index 0. + internal sealed class FromAbiEnumerator : global::System.Collections.Generic.IEnumerator + { + private readonly global::Windows.Foundation.Collections.IIterator _iterator; + + public FromAbiEnumerator(IObjectReference obj) : + this(new global::System.Collections.Generic.IEnumeratorImpl(obj)) + { + } + + internal FromAbiEnumerator(global::Windows.Foundation.Collections.IIterator iterator) + { + _iterator = iterator; + } + + public static global::System.Collections.Generic.IEnumerator FromAbi(IntPtr abi) + { + if (abi == IntPtr.Zero) + { + return null; + } + return new FromAbiEnumerator(ObjectReference.FromAbi(abi, IID.IID_IUnknown)); + } + + public static void DisposeAbi(IntPtr abi) => MarshalInterfaceHelper>.DisposeAbi(abi); + + private bool m_hadCurrent = true; + private T m_current = default!; + private bool m_isInitialized = false; + + public T Current + { + get + { + // The enumerator has not been advanced to the first element yet. + if (!m_isInitialized) + throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_EnumNotStarted); + // The enumerator has reached the end of the collection + if (!m_hadCurrent) + throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_EnumEnded); + return m_current; + } + } + + object IEnumerator.Current + { + get + { + // The enumerator has not been advanced to the first element yet. + if (!m_isInitialized) + throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_EnumNotStarted); + // The enumerator has reached the end of the collection + if (!m_hadCurrent) + throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_EnumEnded); + return m_current; + } + } + + public bool MoveNext() + { + // If we've passed the end of the iteration, IEnumerable should return false, while + // IIterable will fail the interface call + if (!m_hadCurrent) + { + return false; + } + + // IIterators start at index 0, rather than -1. If this is the first call, we need to just + // check HasCurrent rather than actually moving to the next element + try + { + if (!m_isInitialized) + { + m_hadCurrent = _iterator.HasCurrent; + m_isInitialized = true; + } + else + { + m_hadCurrent = _iterator._MoveNext(); + } + + // We want to save away the current value for two reasons: + // 1. Accessing .Current is cheap on other iterators, so having it be a property which is a + // simple field access preserves the expected performance characteristics (as opposed to + // triggering a COM call every time the property is accessed) + // + // 2. This allows us to preserve the same semantics as generic collection iteration when iterating + // beyond the end of the collection - namely that Current continues to return the last value + // of the collection + if (m_hadCurrent) + { + m_current = _iterator._Current; + } + } + catch (Exception e) + { + // Translate E_CHANGED_STATE into an InvalidOperationException for an updated enumeration + if (Marshal.GetHRForException(e) == ExceptionHelpers.E_CHANGED_STATE) + { + throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_EnumFailedVersion); + } + else + { + throw; + } + } + + return m_hadCurrent; + } + + public void Reset() + { + throw new NotSupportedException(); + } + + public void Dispose() + { + } + } + + internal sealed class IBindableIteratorTypeDetails : IWinRTExposedTypeDetails + { + public ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + { + return new ComWrappers.ComInterfaceEntry[] + { + new ComWrappers.ComInterfaceEntry + { + IID = typeof(ABI.Microsoft.UI.Xaml.Interop.IBindableIterator).GUID, + Vtable = ABI.Microsoft.UI.Xaml.Interop.IBindableIterator.AbiToProjectionVftablePtr + }, + new ComWrappers.ComInterfaceEntry + { + // IBindableIterator is intentionally used here as the vtable because + // we provide the same functions on the vtable as IIterator + // and this allows to avoid introducing another class to initialize the + // IIterator vtable. + IID = ABI.System.Collections.Generic.IEnumeratorMethods.IID, + Vtable = ABI.Microsoft.UI.Xaml.Interop.IBindableIterator.AbiToProjectionVftablePtr + } + }; + } + } + + // Used to handle the scenario where some enumerators are implemented on internal types and + // we can't make them AOT friendly due to we can't reference them. + public sealed class ToAbiEnumeratorAdapter : global::System.Collections.Generic.IEnumerator, global::System.Collections.IEnumerator + { + internal readonly global::System.Collections.Generic.IEnumerator m_enumerator; + + internal ToAbiEnumeratorAdapter(global::System.Collections.Generic.IEnumerator enumerator) => m_enumerator = enumerator; + + public T Current => m_enumerator.Current; + + object IEnumerator.Current => m_enumerator.Current; + + public void Dispose() => m_enumerator.Dispose(); + + public bool MoveNext() => m_enumerator.MoveNext(); + + public void Reset() => m_enumerator.Reset(); + } + + [DynamicInterfaceCastableImplementation] + [Guid("6A79E863-4300-459A-9966-CBB660963EE1")] +#pragma warning disable CA2256 // Not implementing IIterator for [DynamicInterfaceCastableImplementation], as we don't expect to need IDIC for WinRT types + interface IEnumerator : global::System.Collections.Generic.IEnumerator, global::Windows.Foundation.Collections.IIterator +#pragma warning restore CA2256 + { + public static IObjectReference CreateMarshaler(global::System.Collections.Generic.IEnumerator obj) => + obj is null ? null : ComWrappersSupport.CreateCCWForObject(obj, PIID); + + public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.Generic.IEnumerator obj) => + ComWrappersSupport.CreateCCWForObjectForMarshaling(obj, PIID); + + public static IntPtr GetAbi(IObjectReference objRef) => + objRef?.ThisPtr ?? IntPtr.Zero; + + public static IntPtr FromManaged(global::System.Collections.Generic.IEnumerator value) => + (value is null) ? IntPtr.Zero : CreateMarshaler2(value).Detach(); + + public static void DisposeMarshaler(IObjectReference objRef) => objRef?.Dispose(); + + public static void DisposeAbi(IntPtr abi) => + MarshalInterfaceHelper>.DisposeAbi(abi); + + public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IEnumerator)); + + // Limiting projected surface to IBindableIterator and IIterator as we only create a CCW for it during the IEnumerator scenario. + // In IEnumerator scenarios, we use this as a helper for the implementation and don't actually use it to create a CCW. + // We include IIterator in IEnumerator scenarios due to compat reasons with how WinUI uses it where it treats both as the same. + [global::WinRT.WinRTExposedType(typeof(IBindableIteratorTypeDetails))] +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + public sealed class ToAbiHelper : global::Windows.Foundation.Collections.IIterator, global::Microsoft.UI.Xaml.Interop.IBindableIterator +#pragma warning restore CA2257 + { + private readonly global::System.Collections.Generic.IEnumerator m_enumerator; + private bool m_firstItem = true; + private bool m_hasCurrent; + + internal ToAbiHelper(global::System.Collections.Generic.IEnumerator enumerator) => m_enumerator = enumerator; + + public T _Current + { + get + { + // IEnumerator starts at item -1, while IIterators start at item 0. Therefore, if this is the + // first access to the iterator we need to advance to the first item. + if (m_firstItem) + { + m_firstItem = false; + _MoveNext(); + } + + if (!m_hasCurrent) + { + ExceptionHelpers.ThrowExceptionForHR(ExceptionHelpers.E_BOUNDS); + } + + return m_enumerator.Current; + } + } + + public bool HasCurrent + { + get + { + // IEnumerator starts at item -1, while IIterators start at item 0. Therefore, if this is the + // first access to the iterator we need to advance to the first item. + if (m_firstItem) + { + m_firstItem = false; + _MoveNext(); + } + + return m_hasCurrent; + } + } + + public bool _MoveNext() + { + try + { + m_hasCurrent = m_enumerator.MoveNext(); + } + catch (InvalidOperationException) + { + ExceptionHelpers.ThrowExceptionForHR(ExceptionHelpers.E_CHANGED_STATE); + } + + return m_hasCurrent; + } + + public uint GetMany(ref T[] items) + { + if (items == null) + { + return 0; + } + + int index = 0; + while (index < items.Length && HasCurrent) + { + items[index] = _Current; + _MoveNext(); + ++index; + } + + if (typeof(T) == typeof(string)) + { + string[] stringItems = (items as string[])!; + + // Fill the rest of the array with string.Empty to avoid marshaling failure + for (int i = index; i < items.Length; ++i) + stringItems[i] = string.Empty; + } + + return (uint)index; + } + + public object Current => _Current; + + public bool MoveNext() => _MoveNext(); + + uint global::Microsoft.UI.Xaml.Interop.IBindableIterator.GetMany(ref object[] items) + { + // Should not be called. + throw new NotImplementedException(); + } + } + + public static readonly IntPtr AbiToProjectionVftablePtr; + static IEnumerator() + { + if (RuntimeFeature.IsDynamicCodeCompiled) + { + // Simple invocation guarded by a direct runtime feature check to help the linker. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + InitFallbackCCWVTableIfNeeded(); +#pragma warning restore IL3050 + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2080", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] + [UnconditionalSuppressMessage("Trimming", "IL2081", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] +#endif + [MethodImpl(MethodImplOptions.NoInlining)] + static void InitFallbackCCWVTableIfNeeded() + { + if (IEnumeratorMethods.AbiToProjectionVftablePtr == default) + { + // Handle the compat scenario where the source generator wasn't used or IDIC was used. + var initFallbackCCWVtable = (Action)typeof(IEnumeratorMethods<,>).MakeGenericType(typeof(T), Marshaler.AbiType). + GetMethod("InitFallbackCCWVtable", BindingFlags.NonPublic | BindingFlags.Static). + CreateDelegate(typeof(Action)); + initFallbackCCWVtable(); + } + } + } + + AbiToProjectionVftablePtr = IEnumeratorMethods.AbiToProjectionVftablePtr; + } + + // This is left here for backwards compat purposes where older generated + // projections can be using FindVftblType and using this to cast. + [Guid("6A79E863-4300-459A-9966-CBB660963EE1")] +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + public unsafe struct Vftbl +#pragma warning restore CA2257 + { + internal IInspectable.Vftbl IInspectableVftbl; + + public static readonly IntPtr AbiToProjectionVftablePtr = ABI.System.Collections.Generic.IEnumerator.AbiToProjectionVftablePtr; + + public static readonly Guid PIID = ABI.System.Collections.Generic.IEnumerator.PIID; + } + + private static readonly ConditionalWeakTable, ToAbiHelper> _adapterTable = new(); + + internal static ToAbiHelper FindAdapter(IntPtr thisPtr) + { + var __this = global::WinRT.ComWrappersSupport.FindObject>(thisPtr); + return _adapterTable.GetValue(__this, (enumerator) => new ToAbiHelper(enumerator)); + } + + public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) + { + if (thisPtr == IntPtr.Zero) + { + return null; + } + return ObjectReference.FromAbi(thisPtr, PIID); + } + public static readonly Guid PIID = IEnumeratorMethods.PIID; + + T global::System.Collections.Generic.IEnumerator.Current + { + get + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IEnumerator).TypeHandle); + return IEnumeratorMethods.get_Current(_obj); + } + } + + bool IEnumerator.MoveNext() + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IEnumerator).TypeHandle); + return IEnumeratorMethods.MoveNext(_obj); + } + + void IEnumerator.Reset() + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IEnumerator).TypeHandle); + IEnumeratorMethods.Reset(_obj); + } + + void IDisposable.Dispose() + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IEnumerator).TypeHandle); + IEnumeratorMethods.Dispose(_obj); + } + + object IEnumerator.Current => Current; + } + +#if EMBED + internal +#else + public +#endif + static class IEnumerator_Delegates + { + public unsafe delegate int MoveNext_2(IntPtr thisPtr, out byte __return_value__); + public unsafe delegate int GetMany_3(IntPtr thisPtr, int __itemsSize, IntPtr items, out uint __return_value__); + + internal unsafe delegate int MoveNext_2_Abi(IntPtr thisPtr, byte* __return_value__); + internal unsafe delegate int GetMany_3_Abi(IntPtr thisPtr, int __itemsSize, IntPtr items, uint* __return_value__); + } +} diff --git a/src/WinRT.Runtime/Projections/IEnumerable.netstandard2.0.cs b/src/WinRT.Runtime/Projections/IEnumerable.netstandard2.0.cs index 1c4261256..02517eaf1 100644 --- a/src/WinRT.Runtime/Projections/IEnumerable.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/IEnumerable.netstandard2.0.cs @@ -110,13 +110,13 @@ public struct Vftbl { internal IInspectable.Vftbl IInspectableVftbl; public IEnumerable_Delegates.First_0 First_0; - public static Guid PIID = GuidGenerator.CreateIID(typeof(IEnumerable)); + public static readonly Guid PIID = GuidGenerator.CreateIIDUnsafe(typeof(IEnumerable)); internal unsafe Vftbl(IntPtr thisPtr) { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); + var vftblPtr = *(void***)thisPtr; + var vftbl = (IntPtr*)vftblPtr; + IInspectableVftbl = *(IInspectable.Vftbl*)vftblPtr; First_0 = Marshal.GetDelegateForFunctionPointer(vftbl[6]); } @@ -162,7 +162,7 @@ public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) var vftblT = new Vftbl(thisPtr); return ObjectReference.FromAbi(thisPtr, vftblT); } - public static Guid PIID = Vftbl.PIID; + public static readonly Guid PIID = Vftbl.PIID; public static implicit operator IEnumerable(IObjectReference obj) => (obj != null) ? new IEnumerable(obj) : null; public static implicit operator IEnumerable(ObjectReference obj) => (obj != null) ? new IEnumerable(obj) : null; @@ -186,6 +186,7 @@ public IEnumerable(ObjectReference obj) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.First_0(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return ABI.System.Collections.Generic.IEnumerator.FromAbi(__retval); } finally @@ -453,14 +454,14 @@ public struct Vftbl internal _get_PropertyAsBoolean get_HasCurrent_1; public IEnumerator_Delegates.MoveNext_2 MoveNext_2; public IEnumerator_Delegates.GetMany_3 GetMany_3; - public static Guid PIID = GuidGenerator.CreateIID(typeof(IEnumerator)); - private static readonly Type get_Current_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType.MakeByRefType(), typeof(int) }); + public static readonly Guid PIID = GuidGenerator.CreateIIDUnsafe(typeof(IEnumerator)); + private static readonly Type get_Current_0_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType.MakeByRefType(), typeof(int) }); internal unsafe Vftbl(IntPtr thisPtr) { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); + var vftblPtr = *(void***)thisPtr; + var vftbl = (IntPtr*)vftblPtr; + IInspectableVftbl = *(IInspectable.Vftbl*)vftblPtr; get_Current_0 = Marshal.GetDelegateForFunctionPointer(vftbl[6], get_Current_0_Type); get_HasCurrent_1 = Marshal.GetDelegateForFunctionPointer<_get_PropertyAsBoolean>(vftbl[7]); MoveNext_2 = Marshal.GetDelegateForFunctionPointer(vftbl[8]); @@ -585,7 +586,7 @@ public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) var vftblT = new Vftbl(thisPtr); return ObjectReference.FromAbi(thisPtr, vftblT); } - public static Guid PIID = Vftbl.PIID; + public static readonly Guid PIID = Vftbl.PIID; public static implicit operator IEnumerator(IObjectReference obj) => (obj != null) ? new IEnumerator(obj) : null; public static implicit operator IEnumerator(ObjectReference obj) => (obj != null) ? new IEnumerator(obj) : null; @@ -609,6 +610,7 @@ public unsafe bool _MoveNext() { byte __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.MoveNext_2(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval != 0; } @@ -623,6 +625,7 @@ public unsafe uint GetMany(ref T[] items) __items = Marshaler.CreateMarshalerArray(items); (__items_length, __items_data) = Marshaler.GetAbiArray(__items); global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetMany_3(ThisPtr, __items_length, __items_data, out __retval)); + GC.KeepAlive(_obj); items = Marshaler.FromAbiArray((__items_length, __items_data)); return __retval; } @@ -640,6 +643,7 @@ public unsafe T _Current try { _obj.Vftbl.get_Current_0.DynamicInvokeAbi(__params); + GC.KeepAlive(_obj); return Marshaler.FromAbi(__params[1]); } finally @@ -655,6 +659,7 @@ public unsafe bool HasCurrent { byte __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_HasCurrent_1(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval != 0; } } diff --git a/src/WinRT.Runtime/Projections/IList.net5.cs b/src/WinRT.Runtime/Projections/IList.net5.cs index fd94c4045..1310f9f2d 100644 --- a/src/WinRT.Runtime/Projections/IList.net5.cs +++ b/src/WinRT.Runtime/Projections/IList.net5.cs @@ -5,13 +5,10 @@ using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Linq.Expressions; using System.Reflection; using System.Runtime.InteropServices; using WinRT; using WinRT.Interop; -using System.Diagnostics; -using System.Collections.Concurrent; #pragma warning disable 0169 // warning CS0169: The field '...' is never used #pragma warning disable 0649 // warning CS0169: Field '...' is never assigned to @@ -46,7 +43,7 @@ internal sealed class IListImpl : global::System.Collections.Generic.IList private volatile IObjectReference __iListObjRef; private IObjectReference Make_IListObjRef() { - global::System.Threading.Interlocked.CompareExchange(ref __iListObjRef, _inner.As.Vftbl>(), null); + global::System.Threading.Interlocked.CompareExchange(ref __iListObjRef, _inner.As(ABI.System.Collections.Generic.IListMethods.PIID), null); return __iListObjRef; } private IObjectReference iListObjRef => __iListObjRef ?? Make_IListObjRef(); @@ -54,7 +51,7 @@ private IObjectReference Make_IListObjRef() private volatile IObjectReference __iEnumerableObjRef; private IObjectReference Make_IEnumerableObjRef() { - global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, _inner.As.Vftbl>(), null); + global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, _inner.As(ABI.System.Collections.Generic.IEnumerableMethods.PIID), null); return __iEnumerableObjRef; } private IObjectReference iEnumerableObjRef => __iEnumerableObjRef ?? Make_IEnumerableObjRef(); @@ -62,8 +59,8 @@ private IObjectReference Make_IEnumerableObjRef() internal IListImpl(IObjectReference _inner) { this._inner = _inner; - } - + } + public static IListImpl CreateRcw(IInspectable obj) => new(obj.ObjRef); public T this[int index] @@ -150,6 +147,7 @@ IEnumerator IEnumerable.GetEnumerator() namespace ABI.System.Collections.Generic { using global::System; + using global::System.Diagnostics.CodeAnalysis; using global::System.Runtime.CompilerServices; #if EMBED @@ -157,8 +155,45 @@ namespace ABI.System.Collections.Generic #else public #endif - static class IListMethods { - + static class IListMethods + { + unsafe static IListMethods() + { + ComWrappersSupport.RegisterHelperType(typeof(global::System.Collections.Generic.IList), typeof(global::ABI.System.Collections.Generic.IList)); + + // Early return to ensure things are trimmed correctly on NAOT. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return; + } + +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + InitRcwHelperFallbackIfNeeded(); +#pragma warning restore IL3050 + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2080", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] + [UnconditionalSuppressMessage("Trimming", "IL2081", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] +#endif + [MethodImpl(MethodImplOptions.NoInlining)] + static void InitRcwHelperFallbackIfNeeded() + { + // Handle the compat scenario where the source generator wasn't used and IDIC hasn't been used yet + // and due to that the function pointers haven't been initialized. + if (!IVectorMethods._RcwHelperInitialized) + { + var initRcwHelperFallback = (Func)typeof(IListMethods<,>).MakeGenericType(typeof(T), Marshaler.AbiType). + GetMethod("InitRcwHelperFallback", BindingFlags.NonPublic | BindingFlags.Static). + CreateDelegate(typeof(Func)); + initRcwHelperFallback(); + } + } + } + public static int get_Count(IObjectReference obj) { uint size = IVectorMethods.get_Size(obj); @@ -336,63 +371,177 @@ internal static void RemoveAtHelper(IObjectReference obj, uint index) } } - } + private static IntPtr abiToProjectionVftablePtr; + public static IntPtr AbiToProjectionVftablePtr => abiToProjectionVftablePtr; - internal static class IVectorMethods - { - public static unsafe uint get_Size(IObjectReference obj) + internal static bool TryInitCCWVtable(IntPtr ptr) { - var _obj = (ObjectReference.Vftbl>)obj; - uint __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetSize_1(_obj.ThisPtr, out __retval)); - return __retval; + return global::System.Threading.Interlocked.CompareExchange(ref abiToProjectionVftablePtr, ptr, IntPtr.Zero) == IntPtr.Zero; } - public static unsafe T GetAt(IObjectReference obj, uint index) + public static T Abi_GetAt_0(IntPtr thisPtr, uint index) { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - var __params = new object[] { ThisPtr, index, null }; - try - { - _obj.Vftbl.GetAt_0.DynamicInvokeAbi(__params); - return Marshaler.FromAbi(__params[2]); - } - finally + return IList.FindAdapter(thisPtr).GetAt(index); + } + + public static global::System.Collections.Generic.IReadOnlyList Abi_GetView_2(IntPtr thisPtr) + { + return IList.FindAdapter(thisPtr).GetView(); + } + + public static bool Abi_IndexOf_3(IntPtr thisPtr, T value, out uint index) + { + return IList.FindAdapter(thisPtr).IndexOf(value, out index); + } + + public static void Abi_SetAt_4(IntPtr thisPtr, uint index, T value) + { + IList.FindAdapter(thisPtr).SetAt(index, value); + } + + public static void Abi_InsertAt_5(IntPtr thisPtr, uint index, T value) + { + IList.FindAdapter(thisPtr).InsertAt(index, value); + } + + public static void Abi_RemoveAt_6(IntPtr thisPtr, uint index) + { + IList.FindAdapter(thisPtr).RemoveAt(index); + } + + public static void Abi_Append_7(IntPtr thisPtr, T value) + { + IList.FindAdapter(thisPtr).Append(value); + } + + public static void Abi_RemoveAtEnd_8(IntPtr thisPtr) + { + IList.FindAdapter(thisPtr).RemoveAtEnd(); + } + + public static void Abi_Clear_9(IntPtr thisPtr) + { + IList.FindAdapter(thisPtr)._Clear(); + } + + public static uint Abi_GetMany_10(IntPtr thisPtr, uint startIndex, ref T[] items) + { + return IList.FindAdapter(thisPtr).GetMany(startIndex, ref items); + } + + public static void Abi_ReplaceAll_11(IntPtr thisPtr, T[] items) + { + IList.FindAdapter(thisPtr).ReplaceAll(items); + } + + public static uint Abi_get_Size_1(IntPtr thisPtr) + { + return IList.FindAdapter(thisPtr).Size; + } + + internal static readonly Guid PIID = GuidGenerator.CreateIIDUnsafe(typeof(IList)); + public static Guid IID => PIID; + + internal unsafe static bool EnsureEnumerableInitialized() + { + return IVectorMethods._EnsureEnumerableInitialized(); + } + } + +#if EMBED + internal +#else + public +#endif + static class IListMethods where TAbi : unmanaged + { + public unsafe static bool InitRcwHelper( + delegate* getAt, + delegate*> getView, + delegate* indexOf, + delegate* setAt, + delegate* insertAt, + delegate* append, + delegate* getMany, + delegate* replaceAll) + { + if (IVectorMethods._RcwHelperInitialized) { - Marshaler.DisposeAbi(__params[2]); + return true; } + + IVectorMethods._GetAt = getAt; + IVectorMethods._GetView = getView; + IVectorMethods._IndexOf = indexOf; + IVectorMethods._SetAt = setAt; + IVectorMethods._InsertAt = insertAt; + IVectorMethods._Append = append; + IVectorMethods._GetMany = getMany; + IVectorMethods._ReplaceAll = replaceAll; + + ComWrappersSupport.RegisterTypedRcwFactory( + typeof(global::System.Collections.Generic.IList), + IListImpl.CreateRcw); + ComWrappersSupport.RegisterHelperType(typeof(global::System.Collections.Generic.IList), typeof(global::ABI.System.Collections.Generic.IList)); + + IVectorMethods._RcwHelperInitialized = true; + return true; } - public static unsafe global::System.Collections.Generic.IReadOnlyList GetView(IObjectReference obj) +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private unsafe static bool InitRcwHelperFallback() + { + return InitRcwHelper( + &GetAtDynamic, + null, + &IndexOfDynamic, + &SetAtDynamic, + &InsertAtDynamic, + &AppendDynamic, + null, + null); + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe T GetAtDynamic(IObjectReference obj, uint index) { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - IntPtr __retval = default; + var ThisPtr = obj.ThisPtr; + TAbi result = default; try { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetView_2(ThisPtr, out __retval)); - return MarshalInterface>.FromAbi(__retval); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6](ThisPtr, index, &result)); + GC.KeepAlive(obj); + return Marshaler.FromAbi(result); } finally { - MarshalInterface>.DisposeAbi(__retval); + Marshaler.DisposeAbi(result); } } - public static unsafe bool IndexOf(IObjectReference obj, T value, out uint index) +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe bool IndexOfDynamic(IObjectReference obj, T value, out uint index) { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; + var ThisPtr = obj.ThisPtr; + byte found; + uint _index; object __value = default; - var __params = new object[] { ThisPtr, null, null, null }; + var __params = new object[] { ThisPtr, null, (IntPtr)(void*)&_index, (IntPtr)(void*)&found }; try { __value = Marshaler.CreateMarshaler2(value); __params[1] = Marshaler.GetAbi(__value); - _obj.Vftbl.IndexOf_3.DynamicInvokeAbi(__params); - index = (uint)__params[2]; - return (byte)__params[3] != 0; + DelegateHelper.Get(obj).IndexOf.DynamicInvokeAbi(__params); + GC.KeepAlive(obj); + GC.KeepAlive(__params); + index = _index; + return found != 0; } finally { @@ -400,17 +549,21 @@ public static unsafe bool IndexOf(IObjectReference obj, T value, out uint index) } } - public static unsafe void SetAt(IObjectReference obj, uint index, T value) +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe void SetAtDynamic(IObjectReference obj, uint index, T value) { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; + var ThisPtr = obj.ThisPtr; object __value = default; var __params = new object[] { ThisPtr, index, null }; try { __value = Marshaler.CreateMarshaler2(value); __params[2] = Marshaler.GetAbi(__value); - _obj.Vftbl.SetAt_4.DynamicInvokeAbi(__params); + DelegateHelper.Get(obj).SetAt.DynamicInvokeAbi(__params); + GC.KeepAlive(obj); + GC.KeepAlive(__params); } finally { @@ -418,17 +571,21 @@ public static unsafe void SetAt(IObjectReference obj, uint index, T value) } } - public static unsafe void InsertAt(IObjectReference obj, uint index, T value) +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe void InsertAtDynamic(IObjectReference obj, uint index, T value) { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; + var ThisPtr = obj.ThisPtr; object __value = default; var __params = new object[] { ThisPtr, index, null }; try { __value = Marshaler.CreateMarshaler2(value); __params[2] = Marshaler.GetAbi(__value); - _obj.Vftbl.InsertAt_5.DynamicInvokeAbi(__params); + DelegateHelper.Get(obj).InsertAt.DynamicInvokeAbi(__params); + GC.KeepAlive(obj); + GC.KeepAlive(__params); } finally { @@ -436,24 +593,21 @@ public static unsafe void InsertAt(IObjectReference obj, uint index, T value) } } - public static unsafe void RemoveAt(IObjectReference obj, uint index) - { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RemoveAt_6(ThisPtr, index)); - } - - public static unsafe void Append(IObjectReference obj, T value) +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe void AppendDynamic(IObjectReference obj, T value) { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; + var ThisPtr = obj.ThisPtr; object __value = default; var __params = new object[] { ThisPtr, null }; try { __value = Marshaler.CreateMarshaler2(value); __params[1] = Marshaler.GetAbi(__value); - _obj.Vftbl.Append_7.DynamicInvokeAbi(__params); + DelegateHelper.Get(obj).Append.DynamicInvokeAbi(__params); + GC.KeepAlive(obj); + GC.KeepAlive(__params); } finally { @@ -461,69 +615,613 @@ public static unsafe void Append(IObjectReference obj, T value) } } + public static unsafe bool InitCcw( + delegate* unmanaged[Stdcall] getAt, + delegate* unmanaged[Stdcall] getSize, + delegate* unmanaged[Stdcall] getView, + delegate* unmanaged[Stdcall] indexOf, + delegate* unmanaged[Stdcall] setAt, + delegate* unmanaged[Stdcall] insertAt, + delegate* unmanaged[Stdcall] removeAt, + delegate* unmanaged[Stdcall] append, + delegate* unmanaged[Stdcall] removeAtEnd, + delegate* unmanaged[Stdcall] clear, + delegate* unmanaged[Stdcall] getMany, + delegate* unmanaged[Stdcall] replaceAll) + { + if (IListMethods.AbiToProjectionVftablePtr != default) + { + return false; + } + + var abiToProjectionVftablePtr = (IntPtr)NativeMemory.AllocZeroed((nuint)(sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * 12)); + *(IInspectable.Vftbl*)abiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[6] = getAt; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[7] = getSize; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[8] = getView; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[9] = indexOf; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[10] = setAt; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[11] = insertAt; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[12] = removeAt; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[13] = append; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[14] = removeAtEnd; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[15] = clear; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[16] = getMany; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[17] = replaceAll; + + if (!IListMethods.TryInitCCWVtable(abiToProjectionVftablePtr)) + { + NativeMemory.Free((void*)abiToProjectionVftablePtr); + return false; + } + + ComWrappersSupport.RegisterHelperType(typeof(global::System.Collections.Generic.IReadOnlyList), typeof(global::ABI.System.Collections.Generic.IReadOnlyList)); + + return true; + } + + private static global::System.Delegate[] DelegateCache; + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + internal static unsafe void InitFallbackCCWVtable() + { + Type getAt_0_type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), typeof(uint), typeof(TAbi*), typeof(int) }); + + DelegateCache = new global::System.Delegate[] + { + global::System.Delegate.CreateDelegate(getAt_0_type, typeof(IListMethods).GetMethod(nameof(Do_Abi_GetAt_0), BindingFlags.NonPublic | BindingFlags.Static)), + new _get_PropertyAsUInt32_Abi(Do_Abi_get_Size_1), + new IList_Delegates.GetView_2_Abi(Do_Abi_GetView_2), + global::System.Delegate.CreateDelegate(IndexOf_3_Type, typeof(IListMethods).GetMethod(nameof(Do_Abi_IndexOf_3), BindingFlags.NonPublic | BindingFlags.Static)), + global::System.Delegate.CreateDelegate(SetAtInsertAt_Type, typeof(IListMethods).GetMethod(nameof(Do_Abi_SetAt_4), BindingFlags.NonPublic | BindingFlags.Static)), + global::System.Delegate.CreateDelegate(SetAtInsertAt_Type, typeof(IListMethods).GetMethod(nameof(Do_Abi_InsertAt_5), BindingFlags.NonPublic | BindingFlags.Static)), + new IList_Delegates.RemoveAt_6(Do_Abi_RemoveAt_6), + global::System.Delegate.CreateDelegate(Append_7_Type, typeof(IListMethods).GetMethod(nameof(Do_Abi_Append_7), BindingFlags.NonPublic | BindingFlags.Static)), + new IList_Delegates.RemoveAtEnd_8(Do_Abi_RemoveAtEnd_8), + new IList_Delegates.Clear_9(Do_Abi_Clear_9), + new IList_Delegates.GetMany_10_Abi(Do_Abi_GetMany_10), + new IList_Delegates.ReplaceAll_11(Do_Abi_ReplaceAll_11) + }; + + InitCcw( + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[0]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[1]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[2]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[3]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[4]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[5]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[6]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[7]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[8]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[9]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[10]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[11]) + ); + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_GetAt_0(void* thisPtr, uint index, TAbi* __return_value__) + { + T ____return_value__ = default; + *__return_value__ = default; + try + { + ____return_value__ = IList.FindAdapter(new IntPtr(thisPtr)).GetAt(index); + *__return_value__ = (TAbi)Marshaler.FromManaged(____return_value__); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe int Do_Abi_GetView_2(IntPtr thisPtr, IntPtr* __return_value__) + { + global::System.Collections.Generic.IReadOnlyList ____return_value__ = default; + *__return_value__ = default; + + try + { + ____return_value__ = IList.FindAdapter(thisPtr).GetView(); + *__return_value__ = MarshalInterface>.FromManaged(____return_value__); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_IndexOf_3(void* thisPtr, TAbi value, uint* index, byte* __return_value__) + { + bool ____return_value__ = default; + + *index = default; + *__return_value__ = default; + uint __index = default; + + try + { + ____return_value__ = IList.FindAdapter(new IntPtr(thisPtr)).IndexOf(Marshaler.FromAbi(value), out __index); + *index = __index; + *__return_value__ = (byte)(____return_value__ ? 1 : 0); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_SetAt_4(void* thisPtr, uint index, TAbi value) + { + try + { + IList.FindAdapter(new IntPtr(thisPtr)).SetAt(index, Marshaler.FromAbi(value)); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_InsertAt_5(void* thisPtr, uint index, TAbi value) + { + try + { + IList.FindAdapter(new IntPtr(thisPtr)).InsertAt(index, Marshaler.FromAbi(value)); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe int Do_Abi_RemoveAt_6(IntPtr thisPtr, uint index) + { + try + { + IList.FindAdapter(thisPtr).RemoveAt(index); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_Append_7(void* thisPtr, TAbi value) + { + try + { + IList.FindAdapter(new IntPtr(thisPtr)).Append(Marshaler.FromAbi(value)); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe int Do_Abi_RemoveAtEnd_8(IntPtr thisPtr) + { + try + { + IList.FindAdapter(thisPtr).RemoveAtEnd(); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe int Do_Abi_Clear_9(IntPtr thisPtr) + { + try + { + IList.FindAdapter(thisPtr)._Clear(); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + 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; + T[] __items = Marshaler.FromAbiArray((__itemsSize, items)); + + try + { + ____return_value__ = IList.FindAdapter(thisPtr).GetMany(startIndex, ref __items); + Marshaler.CopyManagedArray(__items, items); + *__return_value__ = ____return_value__; + + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_ReplaceAll_11(IntPtr thisPtr, int __itemsSize, IntPtr items) + { + try + { + IList.FindAdapter(thisPtr).ReplaceAll(Marshaler.FromAbiArray((__itemsSize, items))); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe int Do_Abi_get_Size_1(IntPtr thisPtr, uint* __return_value__) + { + uint ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = IList.FindAdapter(thisPtr).Size; + *__return_value__ = ____return_value__; + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static Type _indexOf_3_type; + private static Type IndexOf_3_Type + { +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + get => _indexOf_3_type ?? MakeIndexOfType(); + } + + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + private static Type MakeIndexOfType() + { + global::System.Threading.Interlocked.CompareExchange(ref _indexOf_3_type, Projections.GetAbiDelegateType(new Type[] { typeof(void*), typeof(TAbi), typeof(uint*), typeof(byte*), typeof(int) }), null); + return _indexOf_3_type; + } + + private static Type _setAtInsertAt_Type; + private static Type SetAtInsertAt_Type + { +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + get => _setAtInsertAt_Type ?? MakeSetAtInsertAtType(); + } + + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + private static Type MakeSetAtInsertAtType() + { + global::System.Threading.Interlocked.CompareExchange(ref _setAtInsertAt_Type, Projections.GetAbiDelegateType(new Type[] { typeof(void*), typeof(uint), typeof(TAbi), typeof(int) }), null); + return _setAtInsertAt_Type; + } + + private static Type _append_7_Type; + private static Type Append_7_Type + { +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + get => _append_7_Type ?? MakeAppendType(); + } + + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + private static Type MakeAppendType() + { + global::System.Threading.Interlocked.CompareExchange(ref _append_7_Type, Projections.GetAbiDelegateType(new Type[] { typeof(void*), typeof(TAbi), typeof(int) }), null); + return _append_7_Type; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + private sealed class DelegateHelper + { + private readonly IntPtr _ptr; + + private Delegate _indexOfDelegate; + public Delegate IndexOf => _indexOfDelegate ?? GenericDelegateHelper.CreateDelegate(_ptr, ref _indexOfDelegate, IndexOf_3_Type, 9); + + private Delegate _setAtDelegate; + public Delegate SetAt => _setAtDelegate ?? GenericDelegateHelper.CreateDelegate(_ptr, ref _setAtDelegate, SetAtInsertAt_Type, 10); + + private Delegate _insertAtDelegate; + public Delegate InsertAt => _insertAtDelegate ?? GenericDelegateHelper.CreateDelegate(_ptr, ref _insertAtDelegate, SetAtInsertAt_Type, 11); + + private Delegate _appendDelegate; + public Delegate Append => _appendDelegate ?? GenericDelegateHelper.CreateDelegate(_ptr, ref _appendDelegate, Append_7_Type, 13); + + private DelegateHelper(IntPtr ptr) + { + _ptr = ptr; + } + + public static DelegateHelper Get(IObjectReference obj) + { + return (DelegateHelper)GenericDelegateHelper.DelegateTable.GetValue(obj, static (objRef) => new DelegateHelper(objRef.ThisPtr)); + } + } + } + + internal static class IVectorMethods + { + // These function pointers will be set by IListMethods + // when it is called by the source generated type or by the fallback + // mechanism if the source generated type wasn't used. + internal volatile unsafe static delegate* _GetAt; + internal volatile unsafe static delegate*> _GetView; + internal volatile unsafe static delegate* _IndexOf; + internal volatile unsafe static delegate* _SetAt; + internal volatile unsafe static delegate* _InsertAt; + internal volatile unsafe static delegate* _Append; + internal volatile unsafe static delegate* _GetMany; + internal volatile unsafe static delegate* _ReplaceAll; + internal volatile static bool _RcwHelperInitialized; + internal volatile unsafe static delegate* _EnsureEnumerableInitialized; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe void EnsureInitialized() + { + if (RuntimeFeature.IsDynamicCodeCompiled) + { + return; + } + + if (!_RcwHelperInitialized) + { + [DoesNotReturn] + static void ThrowNotInitialized() + { + throw new NotImplementedException( + $"Type '{typeof(global::System.Collections.Generic.IList)}' was called without initializing the RCW methods using 'IListMethods.InitRcwHelper'. " + + $"If using 'IDynamicInterfaceCastable' support to do a dynamic cast to this interface, ensure the 'InitRcwHelper' method is called."); + } + + ThrowNotInitialized(); + } + } + + public static unsafe uint get_Size(IObjectReference obj) + { + IntPtr ThisPtr = obj.ThisPtr; + uint __retval = default; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[7](ThisPtr, &__retval)); + GC.KeepAlive(obj); + return __retval; + } + + public static unsafe T GetAt(IObjectReference obj, uint index) + { + EnsureInitialized(); + return _GetAt(obj, index); + } + + public static unsafe global::System.Collections.Generic.IReadOnlyList GetView(IObjectReference obj) + { + // Early return to ensure things are trimmed correctly on NAOT. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + EnsureInitialized(); + return _GetView(obj); + } + + if (_GetView != null) + { + return _GetView(obj); + } + else + { + var ThisPtr = obj.ThisPtr; + IntPtr __retval = default; + try + { + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[8](ThisPtr, &__retval)); + GC.KeepAlive(obj); + return MarshalInterface>.FromAbi(__retval); + } + finally + { + MarshalInterface>.DisposeAbi(__retval); + } + } + } + + public static unsafe bool IndexOf(IObjectReference obj, T value, out uint index) + { + EnsureInitialized(); + return _IndexOf(obj, value, out index); + } + + public static unsafe void SetAt(IObjectReference obj, uint index, T value) + { + EnsureInitialized(); + _SetAt(obj, index, value); + } + + public static unsafe void InsertAt(IObjectReference obj, uint index, T value) + { + EnsureInitialized(); + _InsertAt(obj, index, value); + } + + public static unsafe void RemoveAt(IObjectReference obj, uint index) + { + var ThisPtr = obj.ThisPtr; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[12](ThisPtr, index)); + GC.KeepAlive(obj); + } + + public static unsafe void Append(IObjectReference obj, T value) + { + EnsureInitialized(); + _Append(obj, value); + } + public static unsafe void RemoveAtEnd(IObjectReference obj) { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RemoveAtEnd_8(ThisPtr)); + var ThisPtr = obj.ThisPtr; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[14](ThisPtr)); + GC.KeepAlive(obj); } public static unsafe void Clear(IObjectReference obj) { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Clear_9(ThisPtr)); + var ThisPtr = obj.ThisPtr; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[15](ThisPtr)); + GC.KeepAlive(obj); } public static unsafe uint GetMany(IObjectReference obj, uint startIndex, ref T[] items) { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - object __items = default; - int __items_length = default; - IntPtr __items_data = default; - uint __retval = default; - try + // Early return to ensure things are trimmed correctly on NAOT. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + EnsureInitialized(); + return _GetMany(obj, startIndex, items); + } + + if (_GetMany != null) { - __items = Marshaler.CreateMarshalerArray(items); - (__items_length, __items_data) = Marshaler.GetAbiArray(__items); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetMany_10(ThisPtr, startIndex, __items_length, __items_data, out __retval)); - items = Marshaler.FromAbiArray((__items_length, __items_data)); - return __retval; + return _GetMany(obj, startIndex, items); } - finally + else { - Marshaler.DisposeMarshalerArray(__items); + var ThisPtr = obj.ThisPtr; + object __items = default; + int __items_length = default; + IntPtr __items_data = default; + uint __retval = default; +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + try + { + __items = Marshaler.CreateMarshalerArray(items); + (__items_length, __items_data) = Marshaler.GetAbiArray(__items); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[16](ThisPtr, startIndex, __items_length, __items_data, &__retval)); + GC.KeepAlive(obj); + items = Marshaler.FromAbiArray((__items_length, __items_data)); + return __retval; + } + finally + { + Marshaler.DisposeMarshalerArray(__items); + } +#pragma warning restore IL3050 } } public static unsafe void ReplaceAll(IObjectReference obj, T[] items) { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - object __items = default; - int __items_length = default; - IntPtr __items_data = default; - try + // Early return to ensure things are trimmed correctly on NAOT. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + EnsureInitialized(); + _ReplaceAll(obj, items); + + return; + } + + if (_ReplaceAll != null) { - __items = Marshaler.CreateMarshalerArray(items); - (__items_length, __items_data) = Marshaler.GetAbiArray(__items); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.ReplaceAll_11(ThisPtr, __items_length, __items_data)); + _ReplaceAll(obj, items); } - finally + else { - Marshaler.DisposeMarshalerArray(__items); + var ThisPtr = obj.ThisPtr; + object __items = default; + int __items_length = default; + IntPtr __items_data = default; +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + try + { + __items = Marshaler.CreateMarshalerArray(items); + (__items_length, __items_data) = Marshaler.GetAbiArray(__items); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[17](ThisPtr, __items_length, __items_data)); + GC.KeepAlive(obj); + } + finally + { + Marshaler.DisposeMarshalerArray(__items); + } +#pragma warning restore IL3050 } } } [DynamicInterfaceCastableImplementation] - [Guid("913337E9-11A1-4345-A3A2-4E7F956E222D")] - interface IList : global::System.Collections.Generic.IList, global::Windows.Foundation.Collections.IVector + [Guid("913337E9-11A1-4345-A3A2-4E7F956E222D")] +#pragma warning disable CA2256 // Not implementing IVector for [DynamicInterfaceCastableImplementation], as we don't expect to need IDIC for WinRT types + interface IList : global::System.Collections.Generic.IList, global::Windows.Foundation.Collections.IVector +#pragma warning restore CA2256 { public static IObjectReference CreateMarshaler(global::System.Collections.Generic.IList obj) => - obj is null ? null : ComWrappersSupport.CreateCCWForObject(obj, PIID); - + obj is null ? null : ComWrappersSupport.CreateCCWForObject(obj, PIID); + public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.Generic.IList obj) => ComWrappersSupport.CreateCCWForObjectForMarshaling(obj, PIID); @@ -540,9 +1238,11 @@ public static void DisposeAbi(IntPtr abi) => public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IList)); +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) public sealed class ToAbiHelper : global::Windows.Foundation.Collections.IVector +#pragma warning restore CA2257 { - private global::System.Collections.Generic.IList _list; + private readonly global::System.Collections.Generic.IList _list; public ToAbiHelper(global::System.Collections.Generic.IList list) => _list = list; @@ -725,482 +1425,146 @@ private static uint GetManyHelper(global::System.Collections.Generic.IList so } } - [Guid("913337E9-11A1-4345-A3A2-4E7F956E222D")] - public unsafe struct Vftbl + public static readonly IntPtr AbiToProjectionVftablePtr; + static IList() { - internal IInspectable.Vftbl IInspectableVftbl; - public global::System.Delegate GetAt_0; - private void* _get_Size_1; - internal delegate* unmanaged[Stdcall] GetSize_1 { get => (delegate* unmanaged[Stdcall])_get_Size_1; set => _get_Size_1 = (void*)value; } - private void* _getView_2; - public delegate* unmanaged[Stdcall] GetView_2 { get => (delegate* unmanaged[Stdcall])_getView_2; set => _getView_2 = (void*)value; } - public global::System.Delegate IndexOf_3; - public global::System.Delegate SetAt_4; - public global::System.Delegate InsertAt_5; - private void* _removeAt_6; - public delegate* unmanaged[Stdcall] RemoveAt_6 { get => (delegate* unmanaged[Stdcall])_removeAt_6; set => _removeAt_6 = (void*)value; } - public global::System.Delegate Append_7; - private void* _removeAtEnd_8; - public delegate* unmanaged[Stdcall] RemoveAtEnd_8 { get => (delegate* unmanaged[Stdcall])_removeAtEnd_8; set => _removeAtEnd_8 = (void*)value; } - private void* _clear_9; - public delegate* unmanaged[Stdcall] Clear_9 { get => (delegate* unmanaged[Stdcall])_clear_9; set => _clear_9 = (void*)value; } - private void* _getMany_10; - public delegate* unmanaged[Stdcall] GetMany_10 { get => (delegate* unmanaged[Stdcall])_getMany_10; set => _getMany_10 = (void*)value; } - private void* _replaceAll_11; - public delegate* unmanaged[Stdcall] ReplaceAll_11 { get => (delegate* unmanaged[Stdcall])_replaceAll_11; set => _replaceAll_11 = (void*)value; } - - public static Guid PIID = GuidGenerator.CreateIID(typeof(IList)); - private static readonly Type GetAt_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType.MakeByRefType(), typeof(int) }); - private static readonly Type IndexOf_3_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }); - private static readonly Type SetAt_4_Type = Expression.GetDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType, typeof(int) }); - private static readonly Type InsertAt_5_Type = Expression.GetDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType, typeof(int) }); - private static readonly Type Append_7_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(int) }); - - internal unsafe Vftbl(IntPtr thisPtr) : this() - { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); - GetAt_0 = Marshal.GetDelegateForFunctionPointer(vftbl[6], GetAt_0_Type); - GetSize_1 = (delegate* unmanaged[Stdcall])vftbl[7]; - GetView_2 = (delegate* unmanaged[Stdcall])vftbl[8]; - IndexOf_3 = Marshal.GetDelegateForFunctionPointer(vftbl[9], IndexOf_3_Type); - SetAt_4 = Marshal.GetDelegateForFunctionPointer(vftbl[10], SetAt_4_Type); - InsertAt_5 = Marshal.GetDelegateForFunctionPointer(vftbl[11], InsertAt_5_Type); - RemoveAt_6 = (delegate* unmanaged[Stdcall])vftbl[12]; - Append_7 = Marshal.GetDelegateForFunctionPointer(vftbl[13], Append_7_Type); - RemoveAtEnd_8 = (delegate* unmanaged[Stdcall])vftbl[14]; - Clear_9 = (delegate* unmanaged[Stdcall])vftbl[15]; - GetMany_10 = (delegate* unmanaged[Stdcall])vftbl[16]; - ReplaceAll_11 = (delegate* unmanaged[Stdcall])vftbl[17]; - } - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - private static readonly Delegate[] DelegateCache = new Delegate[7]; - - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - GetAt_0 = global::System.Delegate.CreateDelegate(GetAt_0_Type, typeof(Vftbl).GetMethod("Do_Abi_GetAt_0", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType)), - _get_Size_1 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[0] = new _get_PropertyAsUInt32(Do_Abi_get_Size_1)), - _getView_2 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[1] = new IList_Delegates.GetView_2(Do_Abi_GetView_2)), - IndexOf_3 = global::System.Delegate.CreateDelegate(IndexOf_3_Type, typeof(Vftbl).GetMethod("Do_Abi_IndexOf_3", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType)), - SetAt_4 = global::System.Delegate.CreateDelegate(SetAt_4_Type, typeof(Vftbl).GetMethod("Do_Abi_SetAt_4", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType)), - InsertAt_5 = global::System.Delegate.CreateDelegate(InsertAt_5_Type, typeof(Vftbl).GetMethod("Do_Abi_InsertAt_5", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType)), - _removeAt_6 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[2] = new IList_Delegates.RemoveAt_6(Do_Abi_RemoveAt_6)), - Append_7 = global::System.Delegate.CreateDelegate(Append_7_Type, typeof(Vftbl).GetMethod("Do_Abi_Append_7", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType)), - _removeAtEnd_8 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[3] = new IList_Delegates.RemoveAtEnd_8(Do_Abi_RemoveAtEnd_8)), - _clear_9 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[4] = new IList_Delegates.Clear_9(Do_Abi_Clear_9)), - _getMany_10 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[5] = new IList_Delegates.GetMany_10(Do_Abi_GetMany_10)), - _replaceAll_11 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[6] = new IList_Delegates.ReplaceAll_11(Do_Abi_ReplaceAll_11)), - }; - var nativeVftbl = (IntPtr*)Marshal.AllocCoTaskMem(Marshal.SizeOf() + sizeof(IntPtr) * 12); - Marshal.StructureToPtr(AbiToProjectionVftable.IInspectableVftbl, (IntPtr)nativeVftbl, false); - nativeVftbl[6] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.GetAt_0); - nativeVftbl[7] = (IntPtr)AbiToProjectionVftable.GetSize_1; - nativeVftbl[8] = (IntPtr)AbiToProjectionVftable.GetView_2; - nativeVftbl[9] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.IndexOf_3); - nativeVftbl[10] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.SetAt_4); - nativeVftbl[11] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.InsertAt_5); - nativeVftbl[12] = (IntPtr)AbiToProjectionVftable._removeAt_6; - nativeVftbl[13] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.Append_7); - nativeVftbl[14] = (IntPtr)AbiToProjectionVftable._removeAtEnd_8; - nativeVftbl[15] = (IntPtr)AbiToProjectionVftable._clear_9; - nativeVftbl[16] = (IntPtr)AbiToProjectionVftable._getMany_10; - nativeVftbl[17] = (IntPtr)AbiToProjectionVftable._replaceAll_11; - - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - private static ConditionalWeakTable, ToAbiHelper> _adapterTable = - new ConditionalWeakTable, ToAbiHelper>(); - - private static global::Windows.Foundation.Collections.IVector FindAdapter(IntPtr thisPtr) - { - var __this = global::WinRT.ComWrappersSupport.FindObject>(thisPtr); - return _adapterTable.GetValue(__this, (list) => new ToAbiHelper(list)); - } - - private static unsafe int Do_Abi_GetAt_0(void* thisPtr, uint index, out TAbi __return_value__) - { - T ____return_value__ = default; - __return_value__ = default; - try - { - ____return_value__ = FindAdapter(new IntPtr(thisPtr)).GetAt(index); - __return_value__ = (TAbi)Marshaler.FromManaged(____return_value__); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_GetView_2(IntPtr thisPtr, out IntPtr __return_value__) + if (RuntimeFeature.IsDynamicCodeCompiled) { - global::System.Collections.Generic.IReadOnlyList ____return_value__ = default; - __return_value__ = default; + // Simple invocation guarded by a direct runtime feature check to help the linker. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + InitFallbackCCWVTableIfNeeded(); +#pragma warning restore IL3050 - try - { - ____return_value__ = FindAdapter(thisPtr).GetView(); - __return_value__ = MarshalInterface>.FromManaged(____return_value__); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_IndexOf_3(void* thisPtr, TAbi value, out uint index, out byte __return_value__) - { - bool ____return_value__ = default; - - index = default; - __return_value__ = default; - uint __index = default; - - try - { - ____return_value__ = FindAdapter(new IntPtr(thisPtr)).IndexOf(Marshaler.FromAbi(value), out __index); - index = __index; - __return_value__ = (byte)(____return_value__ ? 1 : 0); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_SetAt_4(void* thisPtr, uint index, TAbi value) - { - try - { - FindAdapter(new IntPtr(thisPtr)).SetAt(index, Marshaler.FromAbi(value)); - } - catch (Exception __exception__) +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2080", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] + [UnconditionalSuppressMessage("Trimming", "IL2081", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] +#endif + [MethodImpl(MethodImplOptions.NoInlining)] + static void InitFallbackCCWVTableIfNeeded() { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + if (IListMethods.AbiToProjectionVftablePtr == default) + { + // Handle the compat scenario where the source generator wasn't used or IDIC was used. + var initFallbackCCWVtable = (Action)typeof(IListMethods<,>).MakeGenericType(typeof(T), Marshaler.AbiType). + GetMethod("InitFallbackCCWVtable", BindingFlags.NonPublic | BindingFlags.Static). + CreateDelegate(typeof(Action)); + initFallbackCCWVtable(); + } } - return 0; } - private static unsafe int Do_Abi_InsertAt_5(void* thisPtr, uint index, TAbi value) - { + AbiToProjectionVftablePtr = IListMethods.AbiToProjectionVftablePtr; + } - try - { - FindAdapter(new IntPtr(thisPtr)).InsertAt(index, Marshaler.FromAbi(value)); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_RemoveAt_6(IntPtr thisPtr, uint index) - { - try - { - FindAdapter(thisPtr).RemoveAt(index); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_Append_7(void* thisPtr, TAbi value) - { - try - { - FindAdapter(new IntPtr(thisPtr)).Append(Marshaler.FromAbi(value)); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_RemoveAtEnd_8(IntPtr thisPtr) - { - try - { - FindAdapter(thisPtr).RemoveAtEnd(); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_Clear_9(IntPtr thisPtr) - { - try - { - FindAdapter(thisPtr)._Clear(); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_GetMany_10(IntPtr thisPtr, uint startIndex, int __itemsSize, IntPtr items, out uint __return_value__) - { - uint ____return_value__ = default; - - __return_value__ = default; - T[] __items = Marshaler.FromAbiArray((__itemsSize, items)); + // This is left here for backwards compat purposes where older generated + // projections can be using FindVftblType and using this to cast. + [Guid("913337E9-11A1-4345-A3A2-4E7F956E222D")] +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + public unsafe struct Vftbl +#pragma warning restore CA2257 + { + internal IInspectable.Vftbl IInspectableVftbl; - try - { - ____return_value__ = FindAdapter(thisPtr).GetMany(startIndex, ref __items); Marshaler.CopyManagedArray(__items, items); - __return_value__ = ____return_value__; + public static readonly IntPtr AbiToProjectionVftablePtr = ABI.System.Collections.Generic.IList.AbiToProjectionVftablePtr; - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_ReplaceAll_11(IntPtr thisPtr, int __itemsSize, IntPtr items) - { - try - { - FindAdapter(thisPtr).ReplaceAll(Marshaler.FromAbiArray((__itemsSize, items))); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_get_Size_1(IntPtr thisPtr, out uint __return_value__) - { - uint ____return_value__ = default; + public static readonly Guid PIID = ABI.System.Collections.Generic.IList.PIID; + } - __return_value__ = default; + private static readonly ConditionalWeakTable, ToAbiHelper> _adapterTable = new(); - try - { - ____return_value__ = FindAdapter(thisPtr).Size; - __return_value__ = ____return_value__; - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } + internal static global::Windows.Foundation.Collections.IVector FindAdapter(IntPtr thisPtr) + { + var __this = global::WinRT.ComWrappersSupport.FindObject>(thisPtr); + return _adapterTable.GetValue(__this, (list) => new ToAbiHelper(list)); } - public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) + + public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) { if (thisPtr == IntPtr.Zero) { return null; } - var vftblT = new Vftbl(thisPtr); - return ObjectReference.FromAbi(thisPtr, vftblT); + return ObjectReference.FromAbi(thisPtr, PIID); } - public static Guid PIID = Vftbl.PIID; + + public static readonly Guid PIID = IListMethods.PIID; unsafe T global::Windows.Foundation.Collections.IVector.GetAt(uint index) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - var __params = new object[] { ThisPtr, index, null }; - try - { - _obj.Vftbl.GetAt_0.DynamicInvokeAbi(__params); - return Marshaler.FromAbi(__params[2]); - } - finally - { - Marshaler.DisposeAbi(__params[2]); - } + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); + return IVectorMethods.GetAt(_obj, index); } unsafe global::System.Collections.Generic.IReadOnlyList global::Windows.Foundation.Collections.IVector.GetView() { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - IntPtr __retval = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetView_2(ThisPtr, out __retval)); - return MarshalInterface>.FromAbi(__retval); - } - finally - { - MarshalInterface>.DisposeAbi(__retval); - } + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); + return IVectorMethods.GetView(_obj); } unsafe bool global::Windows.Foundation.Collections.IVector.IndexOf(T value, out uint index) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - object __value = default; - var __params = new object[] { ThisPtr, null, null, null }; - try - { - __value = Marshaler.CreateMarshaler2(value); - __params[1] = Marshaler.GetAbi(__value); - _obj.Vftbl.IndexOf_3.DynamicInvokeAbi(__params); - index = (uint)__params[2]; - return (byte)__params[3] != 0; - } - finally - { - Marshaler.DisposeMarshaler(__value); - } + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); + return IVectorMethods.IndexOf(_obj, value, out index); } unsafe void global::Windows.Foundation.Collections.IVector.SetAt(uint index, T value) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - object __value = default; - var __params = new object[] { ThisPtr, index, null }; - try - { - __value = Marshaler.CreateMarshaler2(value); - __params[2] = Marshaler.GetAbi(__value); - _obj.Vftbl.SetAt_4.DynamicInvokeAbi(__params); - } - finally - { - Marshaler.DisposeMarshaler(__value); - } + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); + IVectorMethods.SetAt(_obj, index, value); } unsafe void global::Windows.Foundation.Collections.IVector.InsertAt(uint index, T value) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - object __value = default; - var __params = new object[] { ThisPtr, index, null }; - try - { - __value = Marshaler.CreateMarshaler2(value); - __params[2] = Marshaler.GetAbi(__value); - _obj.Vftbl.InsertAt_5.DynamicInvokeAbi(__params); - } - finally - { - Marshaler.DisposeMarshaler(__value); - } + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); + IVectorMethods.InsertAt(_obj, index, value); } unsafe void global::Windows.Foundation.Collections.IVector.RemoveAt(uint index) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RemoveAt_6(ThisPtr, index)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); + IVectorMethods.RemoveAt(_obj, index); } unsafe void global::Windows.Foundation.Collections.IVector.Append(T value) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - object __value = default; - var __params = new object[] { ThisPtr, null }; - try - { - __value = Marshaler.CreateMarshaler2(value); - __params[1] = Marshaler.GetAbi(__value); - _obj.Vftbl.Append_7.DynamicInvokeAbi(__params); - } - finally - { - Marshaler.DisposeMarshaler(__value); - } + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); + IVectorMethods.Append(_obj, value); } unsafe void global::Windows.Foundation.Collections.IVector.RemoveAtEnd() { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RemoveAtEnd_8(ThisPtr)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); + IVectorMethods.RemoveAtEnd(_obj); } unsafe void global::Windows.Foundation.Collections.IVector._Clear() { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Clear_9(ThisPtr)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); + IVectorMethods.Clear(_obj); } unsafe uint global::Windows.Foundation.Collections.IVector.GetMany(uint startIndex, ref T[] items) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - object __items = default; - int __items_length = default; - IntPtr __items_data = default; - uint __retval = default; - try - { - __items = Marshaler.CreateMarshalerArray(items); - (__items_length, __items_data) = Marshaler.GetAbiArray(__items); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetMany_10(ThisPtr, startIndex, __items_length, __items_data, out __retval)); - items = Marshaler.FromAbiArray((__items_length, __items_data)); - return __retval; - } - finally - { - Marshaler.DisposeMarshalerArray(__items); - } + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); + return IVectorMethods.GetMany(_obj, startIndex, ref items); } unsafe void global::Windows.Foundation.Collections.IVector.ReplaceAll(T[] items) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - object __items = default; - int __items_length = default; - IntPtr __items_data = default; - try - { - __items = Marshaler.CreateMarshalerArray(items); - (__items_length, __items_data) = Marshaler.GetAbiArray(__items); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.ReplaceAll_11(ThisPtr, __items_length, __items_data)); - } - finally - { - Marshaler.DisposeMarshalerArray(__items); - } + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); + IVectorMethods.ReplaceAll(_obj, items); } unsafe uint global::Windows.Foundation.Collections.IVector.Size { get { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - uint __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetSize_1(ThisPtr, out __retval)); - return __retval; + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); + return IVectorMethods.get_Size(_obj); } } @@ -1208,7 +1572,7 @@ public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) { get { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); return IListMethods.get_Count(_obj); } } @@ -1217,7 +1581,7 @@ public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) { get { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); return IListMethods.get_IsReadOnly(_obj); } } @@ -1226,68 +1590,68 @@ public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) { get { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); return IListMethods.Indexer_Get(_obj, index); } set { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); IListMethods.Indexer_Set(_obj, index, value); } } int global::System.Collections.Generic.IList.IndexOf(T item) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); return IListMethods.IndexOf(_obj, item); } void global::System.Collections.Generic.IList.Insert(int index, T item) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); IListMethods.Insert(_obj, index, item); } void global::System.Collections.Generic.IList.RemoveAt(int index) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); IListMethods.RemoveAt(_obj, index); } void global::System.Collections.Generic.ICollection.Add(T item) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); IListMethods.Add(_obj, item); } void global::System.Collections.Generic.ICollection.Clear() { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); IListMethods.Clear(_obj); } bool global::System.Collections.Generic.ICollection.Contains(T item) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); return IListMethods.Contains(_obj, item); } void global::System.Collections.Generic.ICollection.CopyTo(T[] array, int arrayIndex) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); IListMethods.CopyTo(_obj, array, arrayIndex); } bool global::System.Collections.Generic.ICollection.Remove(T item) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IList).TypeHandle); return IListMethods.Remove(_obj, item); } global::System.Collections.Generic.IEnumerator global::System.Collections.Generic.IEnumerable.GetEnumerator() { ((IWinRTObject)this).IsInterfaceImplemented(typeof(global::System.Collections.Generic.IEnumerable).TypeHandle, true); - var _objEnumerable = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IEnumerable).TypeHandle)); + var _objEnumerable = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IEnumerable).TypeHandle); return IEnumerableMethods.GetEnumerator(_objEnumerable); } IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); @@ -1301,10 +1665,12 @@ public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) static class IList_Delegates { public unsafe delegate int GetView_2(IntPtr thisPtr, out IntPtr __return_value__); + internal unsafe delegate int GetView_2_Abi(IntPtr thisPtr, IntPtr* __return_value__); public unsafe delegate int RemoveAt_6(IntPtr thisPtr, uint index); public unsafe delegate int RemoveAtEnd_8(IntPtr thisPtr); public unsafe delegate int Clear_9(IntPtr thisPtr); public unsafe delegate int GetMany_10(IntPtr thisPtr, uint startIndex, int __itemsSize, IntPtr items, out uint __return_value__); + internal unsafe delegate int GetMany_10_Abi(IntPtr thisPtr, uint startIndex, int __itemsSize, IntPtr items, uint* __return_value__); public unsafe delegate int ReplaceAll_11(IntPtr thisPtr, int __itemsSize, IntPtr items); } } diff --git a/src/WinRT.Runtime/Projections/IList.netstandard2.0.cs b/src/WinRT.Runtime/Projections/IList.netstandard2.0.cs index c7119443d..e3ff00350 100644 --- a/src/WinRT.Runtime/Projections/IList.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/IList.netstandard2.0.cs @@ -475,18 +475,18 @@ public struct Vftbl public IList_Delegates.Clear_9 Clear_9; public IList_Delegates.GetMany_10 GetMany_10; public IList_Delegates.ReplaceAll_11 ReplaceAll_11; - public static Guid PIID = GuidGenerator.CreateIID(typeof(IList)); - private static readonly Type GetAt_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType.MakeByRefType(), typeof(int) }); - private static readonly Type IndexOf_3_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }); - private static readonly Type SetAt_4_Type = Expression.GetDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType, typeof(int) }); - private static readonly Type InsertAt_5_Type = Expression.GetDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType, typeof(int) }); - private static readonly Type Append_7_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(int) }); + public static readonly Guid PIID = GuidGenerator.CreateIIDUnsafe(typeof(IList)); + private static readonly Type GetAt_0_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType.MakeByRefType(), typeof(int) }); + private static readonly Type IndexOf_3_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }); + private static readonly Type SetAt_4_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType, typeof(int) }); + private static readonly Type InsertAt_5_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType, typeof(int) }); + private static readonly Type Append_7_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(int) }); internal unsafe Vftbl(IntPtr thisPtr) { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); + var vftblPtr = *(void***)thisPtr; + var vftbl = (IntPtr*)vftblPtr; + IInspectableVftbl = *(IInspectable.Vftbl*)vftblPtr; GetAt_0 = Marshal.GetDelegateForFunctionPointer(vftbl[6], GetAt_0_Type); get_Size_1 = Marshal.GetDelegateForFunctionPointer<_get_PropertyAsUInt32>(vftbl[7]); GetView_2 = Marshal.GetDelegateForFunctionPointer(vftbl[8]); @@ -745,7 +745,7 @@ public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) var vftblT = new Vftbl(thisPtr); return ObjectReference.FromAbi(thisPtr, vftblT); } - public static Guid PIID = Vftbl.PIID; + public static readonly Guid PIID = Vftbl.PIID; public static implicit operator IList(IObjectReference obj) => (obj != null) ? new IList(obj) : null; public static implicit operator IList(ObjectReference obj) => (obj != null) ? new IList(obj) : null; @@ -770,6 +770,7 @@ public unsafe T GetAt(uint index) try { _obj.Vftbl.GetAt_0.DynamicInvokeAbi(__params); + GC.KeepAlive(_obj); return Marshaler.FromAbi(__params[2]); } finally @@ -784,6 +785,7 @@ public unsafe T GetAt(uint index) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetView_2(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return MarshalInterface>.FromAbi(__retval); } finally @@ -801,6 +803,7 @@ public unsafe bool IndexOf(T value, out uint index) __value = Marshaler.CreateMarshaler2(value); __params[1] = Marshaler.GetAbi(__value); _obj.Vftbl.IndexOf_3.DynamicInvokeAbi(__params); + GC.KeepAlive(_obj); index = (uint)__params[2]; return (byte)__params[3] != 0; } @@ -819,6 +822,8 @@ public unsafe void SetAt(uint index, T value) __value = Marshaler.CreateMarshaler2(value); __params[2] = Marshaler.GetAbi(__value); _obj.Vftbl.SetAt_4.DynamicInvokeAbi(__params); + GC.KeepAlive(_obj); + GC.KeepAlive(__params); } finally { @@ -835,6 +840,8 @@ public unsafe void InsertAt(uint index, T value) __value = Marshaler.CreateMarshaler2(value); __params[2] = Marshaler.GetAbi(__value); _obj.Vftbl.InsertAt_5.DynamicInvokeAbi(__params); + GC.KeepAlive(_obj); + GC.KeepAlive(__params); } finally { @@ -845,6 +852,7 @@ public unsafe void InsertAt(uint index, T value) public unsafe void RemoveAt(uint index) { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RemoveAt_6(ThisPtr, index)); + GC.KeepAlive(_obj); } public unsafe void Append(T value) @@ -856,6 +864,8 @@ public unsafe void Append(T value) __value = Marshaler.CreateMarshaler2(value); __params[1] = Marshaler.GetAbi(__value); _obj.Vftbl.Append_7.DynamicInvokeAbi(__params); + GC.KeepAlive(_obj); + GC.KeepAlive(__params); } finally { @@ -866,11 +876,13 @@ public unsafe void Append(T value) public unsafe void RemoveAtEnd() { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.RemoveAtEnd_8(ThisPtr)); + GC.KeepAlive(_obj); } public unsafe void _Clear() { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Clear_9(ThisPtr)); + GC.KeepAlive(_obj); } public unsafe uint GetMany(uint startIndex, ref T[] items) @@ -884,6 +896,7 @@ public unsafe uint GetMany(uint startIndex, ref T[] items) __items = Marshaler.CreateMarshalerArray(items); (__items_length, __items_data) = Marshaler.GetAbiArray(__items); global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetMany_10(ThisPtr, startIndex, __items_length, __items_data, out __retval)); + GC.KeepAlive(_obj); items = Marshaler.FromAbiArray((__items_length, __items_data)); return __retval; } @@ -903,6 +916,7 @@ public unsafe void ReplaceAll(T[] items) __items = Marshaler.CreateMarshalerArray(items); (__items_length, __items_data) = Marshaler.GetAbiArray(__items); global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.ReplaceAll_11(ThisPtr, __items_length, __items_data)); + GC.KeepAlive(_obj); } finally { @@ -916,6 +930,7 @@ public unsafe uint Size { uint __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Size_1(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } } diff --git a/src/WinRT.Runtime/Projections/INotifyCollectionChanged.net5.cs b/src/WinRT.Runtime/Projections/INotifyCollectionChanged.net5.cs index a8e6e4baf..a4a15758a 100644 --- a/src/WinRT.Runtime/Projections/INotifyCollectionChanged.net5.cs +++ b/src/WinRT.Runtime/Projections/INotifyCollectionChanged.net5.cs @@ -1,30 +1,60 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; using System.ComponentModel; +using System.Reflection; using System.Runtime.InteropServices; using WinRT; +using WinRT.Interop; namespace ABI.System.Collections.Specialized { - [DynamicInterfaceCastableImplementation] - [EditorBrowsable(EditorBrowsableState.Never)] - [Guid("530155E1-28A5-5693-87CE-30724D95A06D")] #if EMBED - internal + internal #else public #endif - unsafe interface INotifyCollectionChanged : global::System.Collections.Specialized.INotifyCollectionChanged + static class INotifyCollectionChangedMethods + { + private volatile static global::System.Runtime.CompilerServices.ConditionalWeakTable _CollectionChanged; + private static global::System.Runtime.CompilerServices.ConditionalWeakTable MakeCollectionChangedTable() + { + global::System.Threading.Interlocked.CompareExchange(ref _CollectionChanged, new(), null); + return _CollectionChanged; + } + private static global::System.Runtime.CompilerServices.ConditionalWeakTable CollectionChanged => _CollectionChanged ?? MakeCollectionChangedTable(); + + public static unsafe global::ABI.WinRT.Interop.EventSource Get_CollectionChanged2(IObjectReference obj, object thisObj) + { + return CollectionChanged.GetValue(thisObj, (key) => + { + return new NotifyCollectionChangedEventHandlerEventSource(obj, 6); + }); + } + + public static global::System.Guid IID => FeatureSwitches.UseWindowsUIXamlProjections + ? global::WinRT.Interop.IID.IID_WUX_INotifyCollectionChanged + : global::WinRT.Interop.IID.IID_MUX_INotifyCollectionChanged; + + public static IntPtr AbiToProjectionVftablePtr => INotifyCollectionChanged.Vftbl.AbiToProjectionVftablePtr; + } + + [DynamicInterfaceCastableImplementation] + [EditorBrowsable(EditorBrowsableState.Never)] + [Guid("530155E1-28A5-5693-87CE-30724D95A06D")] + [WuxMuxProjectedType] + internal unsafe interface INotifyCollectionChanged : global::System.Collections.Specialized.INotifyCollectionChanged { [Guid("530155E1-28A5-5693-87CE-30724D95A06D")] +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) public struct Vftbl +#pragma warning restore CA2257 { internal IInspectable.Vftbl IInspectableVftbl; private delegate* unmanaged _add_CollectionChanged_0; - public delegate* unmanaged[Stdcall] add_CollectionChanged_0 { get => (delegate* unmanaged[Stdcall])_add_CollectionChanged_0; set => _add_CollectionChanged_0 = (delegate* unmanaged)value; } + public delegate* unmanaged[Stdcall] add_CollectionChanged_0 { get => (delegate* unmanaged[Stdcall])_add_CollectionChanged_0; set => _add_CollectionChanged_0 = (delegate* unmanaged)value; } private delegate* unmanaged _remove_CollectionChanged_1; public delegate* unmanaged[Stdcall] remove_CollectionChanged_1 { get => (delegate* unmanaged[Stdcall])_remove_CollectionChanged_1; set => _remove_CollectionChanged_1 = (delegate* unmanaged)value; } @@ -41,21 +71,21 @@ static unsafe Vftbl() _remove_CollectionChanged_1 = &Do_Abi_remove_CollectionChanged_1, }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 2); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 2); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; } private volatile static global::System.Runtime.CompilerServices.ConditionalWeakTable> _collectionChanged_TokenTables; - - private static global::System.Runtime.CompilerServices.ConditionalWeakTable> MakeConditionalWeakTable() - { - global::System.Threading.Interlocked.CompareExchange(ref _collectionChanged_TokenTables, new(), null); - return _collectionChanged_TokenTables; + + private static global::System.Runtime.CompilerServices.ConditionalWeakTable> MakeConditionalWeakTable() + { + global::System.Threading.Interlocked.CompareExchange(ref _collectionChanged_TokenTables, new(), null); + return _collectionChanged_TokenTables; } - private static global::System.Runtime.CompilerServices.ConditionalWeakTable> _CollectionChanged_TokenTables => _collectionChanged_TokenTables ?? MakeConditionalWeakTable(); - + private static global::System.Runtime.CompilerServices.ConditionalWeakTable> _CollectionChanged_TokenTables => _collectionChanged_TokenTables ?? MakeConditionalWeakTable(); + [UnmanagedCallersOnly] private static unsafe int Do_Abi_add_CollectionChanged_0(IntPtr thisPtr, IntPtr handler, global::WinRT.EventRegistrationToken* token) { @@ -92,14 +122,11 @@ private static unsafe int Do_Abi_remove_CollectionChanged_1(IntPtr thisPtr, glob } } } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - private static NotifyCollectionChangedEventSource _CollectionChanged(IWinRTObject _this) + private static global::ABI.WinRT.Interop.EventSource _CollectionChanged(IWinRTObject _this) { - var _obj = ((ObjectReference)((IWinRTObject)_this).GetObjectReferenceForType(typeof(global::System.Collections.Specialized.INotifyCollectionChanged).TypeHandle)); - - return (NotifyCollectionChangedEventSource)_this.GetOrCreateTypeHelperData(typeof(global::System.Collections.Specialized.INotifyCollectionChanged).TypeHandle, - () => new NotifyCollectionChangedEventSource(_obj, _obj.Vftbl.add_CollectionChanged_0, _obj.Vftbl.remove_CollectionChanged_1)); + var _obj = _this.GetObjectReferenceForType(typeof(global::System.Collections.Specialized.INotifyCollectionChanged).TypeHandle); + return INotifyCollectionChangedMethods.Get_CollectionChanged2(_obj, _this); } event global::System.Collections.Specialized.NotifyCollectionChangedEventHandler global::System.Collections.Specialized.INotifyCollectionChanged.CollectionChanged @@ -107,5 +134,5 @@ private static NotifyCollectionChangedEventSource _CollectionChanged(IWinRTObjec add => _CollectionChanged((IWinRTObject)this).Subscribe(value); remove => _CollectionChanged((IWinRTObject)this).Unsubscribe(value); } - } + } } diff --git a/src/WinRT.Runtime/Projections/INotifyCollectionChanged.netstandard2.0.cs b/src/WinRT.Runtime/Projections/INotifyCollectionChanged.netstandard2.0.cs index 6c381a4c6..20ec01bc0 100644 --- a/src/WinRT.Runtime/Projections/INotifyCollectionChanged.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/INotifyCollectionChanged.netstandard2.0.cs @@ -110,11 +110,7 @@ public INotifyCollectionChanged(IObjectReference obj) : this(obj.As()) { internal INotifyCollectionChanged(ObjectReference obj) { _obj = obj; - - _CollectionChanged = - new NotifyCollectionChangedEventSource(_obj, - _obj.Vftbl.add_CollectionChanged_0, - _obj.Vftbl.remove_CollectionChanged_1); + _CollectionChanged = new NotifyCollectionChangedEventHandlerEventSource(_obj, 6); } public event global::System.Collections.Specialized.NotifyCollectionChangedEventHandler CollectionChanged @@ -123,6 +119,6 @@ internal INotifyCollectionChanged(ObjectReference obj) remove => _CollectionChanged.Unsubscribe(value); } - private NotifyCollectionChangedEventSource _CollectionChanged; + private NotifyCollectionChangedEventHandlerEventSource _CollectionChanged; } } diff --git a/src/WinRT.Runtime/Projections/INotifyDataErrorInfo.net5.cs b/src/WinRT.Runtime/Projections/INotifyDataErrorInfo.net5.cs index 3e6eecd83..1c8d7ac66 100644 --- a/src/WinRT.Runtime/Projections/INotifyDataErrorInfo.net5.cs +++ b/src/WinRT.Runtime/Projections/INotifyDataErrorInfo.net5.cs @@ -1,26 +1,94 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using ABI.System.Collections.Specialized; using System; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; +using ABI.WinRT.Interop; using WinRT; +using WinRT.Interop; namespace ABI.System.ComponentModel { - [DynamicInterfaceCastableImplementation] - [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] - [Guid("0EE6C2CC-273E-567D-BC0A-1DD87EE51EBA")] #if EMBED internal #else - public -#endif - unsafe interface INotifyDataErrorInfo : global::System.ComponentModel.INotifyDataErrorInfo + public +#endif + static class INotifyDataErrorInfoMethods + { + public static global::System.Guid IID => global::WinRT.Interop.IID.IID_INotifyDataErrorInfo; + + public static IntPtr AbiToProjectionVftablePtr => INotifyDataErrorInfo.Vftbl.AbiToProjectionVftablePtr; + + public static unsafe bool get_HasErrors(IObjectReference obj) + { + var ThisPtr = obj.ThisPtr; + byte __retval = default; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6](ThisPtr, &__retval)); + GC.KeepAlive(obj); + return __retval != 0; + } + + public static unsafe global::System.Collections.IEnumerable GetErrors(IObjectReference obj, string propertyName) + { + if (!FeatureSwitches.EnableIDynamicInterfaceCastableSupport) + { + throw new NotSupportedException( + "'INotifyDataErrorInfo.GetErrors' relies on 'IDynamicInterfaceCastable' support, which is not currently " + + "available. Make sure the 'EnableIDynamicInterfaceCastableSupport' property is not set to 'false'."); + } + + var ThisPtr = obj.ThisPtr; + IntPtr __retval = default; + try + { + MarshalString.Pinnable __propertyName = new(propertyName); + fixed (void* ___propertyName = __propertyName) + { + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[9]( + ThisPtr, + MarshalString.GetAbi(ref __propertyName), + &__retval)); + GC.KeepAlive(obj); + return (global::ABI.System.Collections.Generic.IEnumerable)(object)IInspectable.FromAbi(__retval); + } + } + finally + { + global::ABI.System.Collections.Generic.IEnumerable.DisposeAbi(__retval); + } + } + + private volatile static global::System.Runtime.CompilerServices.ConditionalWeakTable> _ErrorsChanged; + private static global::System.Runtime.CompilerServices.ConditionalWeakTable> MakeErrorsChangedTable() + { + global::System.Threading.Interlocked.CompareExchange(ref _ErrorsChanged, new(), null); + return _ErrorsChanged; + } + private static global::System.Runtime.CompilerServices.ConditionalWeakTable> ErrorsChanged => _ErrorsChanged ?? MakeErrorsChangedTable(); + + + public static unsafe EventHandlerEventSource Get_ErrorsChanged2(IObjectReference obj, object thisObj) + { + return ErrorsChanged.GetValue(thisObj, (key) => + { + return new EventHandlerEventSource(obj, 7); + }); + } + } + + [DynamicInterfaceCastableImplementation] + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + [Guid("0EE6C2CC-273E-567D-BC0A-1DD87EE51EBA")] + internal unsafe interface INotifyDataErrorInfo : global::System.ComponentModel.INotifyDataErrorInfo { [Guid("0EE6C2CC-273E-567D-BC0A-1DD87EE51EBA")] +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) public struct Vftbl +#pragma warning restore CA2247 { internal IInspectable.Vftbl IInspectableVftbl; public delegate* unmanaged get_HasErrors_0; @@ -33,7 +101,7 @@ public struct Vftbl static unsafe Vftbl() { - AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 4); + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 4); (*(Vftbl*)AbiToProjectionVftablePtr) = new Vftbl { IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, @@ -134,48 +202,26 @@ private static unsafe int Do_Abi_remove_ErrorsChanged_2(IntPtr thisPtr, global:: } } } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); + internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr, IID.IID_INotifyDataErrorInfo); - private static EventSource__EventHandler _ErrorsChanged(IWinRTObject _this) + private static EventHandlerEventSource _ErrorsChanged(IWinRTObject _this) { - var _obj = ((ObjectReference)((IWinRTObject)_this).GetObjectReferenceForType(typeof(global::System.ComponentModel.INotifyDataErrorInfo).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - return (EventSource__EventHandler)_this.GetOrCreateTypeHelperData(typeof(global::System.Collections.Specialized.INotifyCollectionChanged).TypeHandle, - () => new EventSource__EventHandler(_obj, - (delegate* unmanaged[Stdcall])(delegate* unmanaged[Stdcall])_obj.Vftbl.add_ErrorsChanged_1, - _obj.Vftbl.remove_ErrorsChanged_2, - 0)); + var _obj = _this.GetObjectReferenceForType(typeof(global::System.ComponentModel.INotifyDataErrorInfo).TypeHandle); + return INotifyDataErrorInfoMethods.Get_ErrorsChanged2(_obj, _this); } unsafe global::System.Collections.IEnumerable global::System.ComponentModel.INotifyDataErrorInfo.GetErrors(string propertyName) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.ComponentModel.INotifyDataErrorInfo).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - IntPtr __retval = default; - try - { - MarshalString.Pinnable __propertyName = new(propertyName); - fixed (void* ___propertyName = __propertyName) - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetErrors_3(ThisPtr, MarshalString.GetAbi(ref __propertyName), &__retval)); - return (global::ABI.System.Collections.Generic.IEnumerable)(object)IInspectable.FromAbi(__retval); - } - } - finally - { - global::ABI.System.Collections.Generic.IEnumerable.DisposeAbi(__retval); - } + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.ComponentModel.INotifyDataErrorInfo).TypeHandle); + return INotifyDataErrorInfoMethods.GetErrors(_obj, propertyName); } unsafe bool global::System.ComponentModel.INotifyDataErrorInfo.HasErrors { get { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.ComponentModel.INotifyDataErrorInfo).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - byte __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_HasErrors_0(ThisPtr, &__retval)); - return __retval != 0; + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.ComponentModel.INotifyDataErrorInfo).TypeHandle); + return INotifyDataErrorInfoMethods.get_HasErrors(_obj); } } diff --git a/src/WinRT.Runtime/Projections/INotifyDataErrorInfo.netstandard2.0.cs b/src/WinRT.Runtime/Projections/INotifyDataErrorInfo.netstandard2.0.cs index 6855b742c..44ba871ad 100644 --- a/src/WinRT.Runtime/Projections/INotifyDataErrorInfo.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/INotifyDataErrorInfo.netstandard2.0.cs @@ -4,7 +4,9 @@ using System; using System.Linq; using System.Runtime.InteropServices; +using ABI.WinRT.Interop; using WinRT; +using WinRT.Interop; namespace ABI.System.ComponentModel { @@ -168,12 +170,7 @@ public INotifyDataErrorInfo(IObjectReference obj) : this(obj.As()) { } internal INotifyDataErrorInfo(ObjectReference obj) { _obj = obj; - - _ErrorsChanged = - new EventSource__EventHandler(_obj, - (delegate* unmanaged[Stdcall])(delegate* unmanaged[Stdcall])_obj.Vftbl.add_ErrorsChanged_1, - _obj.Vftbl.remove_ErrorsChanged_2, - 0); + _ErrorsChanged = new EventHandlerEventSource(_obj, 7); } public unsafe global::System.Collections.IEnumerable GetErrors(string propertyName) @@ -185,6 +182,7 @@ internal INotifyDataErrorInfo(ObjectReference obj) fixed (void* ___propertyName = __propertyName) { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetErrors_3(ThisPtr, MarshalString.GetAbi(ref __propertyName), &__retval)); + GC.KeepAlive(_obj); return (global::ABI.System.Collections.Generic.IEnumerable)(object)IInspectable.FromAbi(__retval); } } @@ -200,6 +198,7 @@ public unsafe bool HasErrors { byte __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_HasErrors_0(ThisPtr, &__retval)); + GC.KeepAlive(_obj); return __retval != 0; } } @@ -210,7 +209,7 @@ public unsafe bool HasErrors remove => _ErrorsChanged.Unsubscribe(value); } - private EventSource__EventHandler _ErrorsChanged; + private EventHandlerEventSource _ErrorsChanged; } internal static class INotifyDataErrorInfo_Delegates diff --git a/src/WinRT.Runtime/Projections/INotifyPropertyChanged.net5.cs b/src/WinRT.Runtime/Projections/INotifyPropertyChanged.net5.cs index 4514212bd..ea98d5ee1 100644 --- a/src/WinRT.Runtime/Projections/INotifyPropertyChanged.net5.cs +++ b/src/WinRT.Runtime/Projections/INotifyPropertyChanged.net5.cs @@ -4,21 +4,56 @@ using System; using System.Runtime.InteropServices; using WinRT; +using WinRT.Interop; namespace ABI.System.ComponentModel { +#if EMBED + internal +#else + public +#endif + static class INotifyPropertyChangedMethods + { + public static global::System.Guid IID => FeatureSwitches.UseWindowsUIXamlProjections + ? global::WinRT.Interop.IID.IID_WUX_INotifyPropertyChanged + : global::WinRT.Interop.IID.IID_MUX_INotifyPropertyChanged; + + public static IntPtr AbiToProjectionVftablePtr => INotifyPropertyChanged.Vftbl.AbiToProjectionVftablePtr; + + private volatile static global::System.Runtime.CompilerServices.ConditionalWeakTable _PropertyChanged; + private static global::System.Runtime.CompilerServices.ConditionalWeakTable MakePropertyChangedTable() + { + global::System.Threading.Interlocked.CompareExchange(ref _PropertyChanged, new(), null); + return _PropertyChanged; + } + private static global::System.Runtime.CompilerServices.ConditionalWeakTable PropertyChanged => _PropertyChanged ?? MakePropertyChangedTable(); + + public static unsafe global::ABI.WinRT.Interop.EventSource Get_PropertyChanged2(IObjectReference obj, object thisObj) + { + return PropertyChanged.GetValue(thisObj, (key) => + { + return new PropertyChangedEventSource(obj, 6); + }); + } + } + [DynamicInterfaceCastableImplementation] [Guid("90B17601-B065-586E-83D9-9ADC3A695284")] - unsafe interface INotifyPropertyChanged : global::System.ComponentModel.INotifyPropertyChanged + [WuxMuxProjectedType] + internal unsafe interface INotifyPropertyChanged : global::System.ComponentModel.INotifyPropertyChanged { +#pragma warning disable CA2257 [Guid("90B17601-B065-586E-83D9-9ADC3A695284")] [StructLayout(LayoutKind.Sequential)] public struct Vftbl +#pragma warning restore CA2257 { + internal IInspectable.Vftbl IInspectableVftbl; private delegate* unmanaged _add_PropertyChanged_0; - public delegate* unmanaged[Stdcall] add_PropertyChanged_0 { get => (delegate* unmanaged[Stdcall])_add_PropertyChanged_0; set => _add_PropertyChanged_0 = (delegate* unmanaged)value; } + public delegate* unmanaged[Stdcall] add_PropertyChanged_0 { get => (delegate* unmanaged[Stdcall])_add_PropertyChanged_0; set => _add_PropertyChanged_0 = (delegate* unmanaged)value; } private delegate* unmanaged _remove_PropertyChanged_1; public delegate* unmanaged[Stdcall] remove_PropertyChanged_1 { get => (delegate* unmanaged[Stdcall])_remove_PropertyChanged_1; set => _remove_PropertyChanged_1 = (delegate* unmanaged)value; } @@ -35,8 +70,8 @@ static unsafe Vftbl() _remove_PropertyChanged_1 = &Do_Abi_remove_PropertyChanged_1, }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 2); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 2); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; } private volatile static global::System.Runtime.CompilerServices.ConditionalWeakTable> _PropertyChanged_TokenTablesLazy = null; @@ -85,14 +120,11 @@ private static unsafe int Do_Abi_remove_PropertyChanged_1(IntPtr thisPtr, global } } } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - private static PropertyChangedEventSource _PropertyChanged(IWinRTObject _this) + private static global::ABI.WinRT.Interop.EventSource _PropertyChanged(IWinRTObject _this) { - var _obj = (ObjectReference)_this.GetObjectReferenceForType(typeof(global::System.ComponentModel.INotifyPropertyChanged).TypeHandle); - - return (PropertyChangedEventSource)_this.GetOrCreateTypeHelperData(typeof(global::System.ComponentModel.INotifyPropertyChanged).TypeHandle, - () => new PropertyChangedEventSource(_obj, _obj.Vftbl.add_PropertyChanged_0, _obj.Vftbl.remove_PropertyChanged_1)); + var _obj = _this.GetObjectReferenceForType(typeof(global::System.ComponentModel.INotifyPropertyChanged).TypeHandle); + return INotifyPropertyChangedMethods.Get_PropertyChanged2(_obj, _this); } event global::System.ComponentModel.PropertyChangedEventHandler global::System.ComponentModel.INotifyPropertyChanged.PropertyChanged @@ -100,6 +132,5 @@ private static PropertyChangedEventSource _PropertyChanged(IWinRTObject _this) add => _PropertyChanged((IWinRTObject)this).Subscribe(value); remove => _PropertyChanged((IWinRTObject)this).Unsubscribe(value); } - } } diff --git a/src/WinRT.Runtime/Projections/INotifyPropertyChanged.netstandard2.0.cs b/src/WinRT.Runtime/Projections/INotifyPropertyChanged.netstandard2.0.cs index 255593fae..4c82c296b 100644 --- a/src/WinRT.Runtime/Projections/INotifyPropertyChanged.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/INotifyPropertyChanged.netstandard2.0.cs @@ -109,11 +109,7 @@ public INotifyPropertyChanged(IObjectReference obj) : this(obj.As()) { } internal INotifyPropertyChanged(ObjectReference obj) { _obj = obj; - - _PropertyChanged = - new PropertyChangedEventSource(_obj, - _obj.Vftbl.add_PropertyChanged_0, - _obj.Vftbl.remove_PropertyChanged_1); + _PropertyChanged = new PropertyChangedEventSource(_obj, 6); } diff --git a/src/WinRT.Runtime/Projections/IPropertyValue.net5.cs b/src/WinRT.Runtime/Projections/IPropertyValue.net5.cs index 918f63db3..b88ce0add 100644 --- a/src/WinRT.Runtime/Projections/IPropertyValue.net5.cs +++ b/src/WinRT.Runtime/Projections/IPropertyValue.net5.cs @@ -1,1916 +1,1280 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using WinRT; - -namespace Windows.Foundation -{ - internal enum PropertyType : uint - { - Empty = 0, - UInt8 = 0x1, - Int16 = 0x2, - UInt16 = 0x3, - Int32 = 0x4, - UInt32 = 0x5, - Int64 = 0x6, - UInt64 = 0x7, - Single = 0x8, - Double = 0x9, - Char16 = 0xa, - Boolean = 0xb, - String = 0xc, - Inspectable = 0xd, - DateTime = 0xe, - TimeSpan = 0xf, - Guid = 0x10, - Point = 0x11, - Size = 0x12, - Rect = 0x13, - OtherType = 0x14, - UInt8Array = 0x401, - Int16Array = 0x402, - UInt16Array = 0x403, - Int32Array = 0x404, - UInt32Array = 0x405, - Int64Array = 0x406, - UInt64Array = 0x407, - SingleArray = 0x408, - DoubleArray = 0x409, - Char16Array = 0x40a, - BooleanArray = 0x40b, - StringArray = 0x40c, - InspectableArray = 0x40d, - DateTimeArray = 0x40e, - TimeSpanArray = 0x40f, - GuidArray = 0x410, - PointArray = 0x411, - SizeArray = 0x412, - RectArray = 0x413, - OtherTypeArray = 0x414, - } - - [Guid("4BD682DD-7554-40E9-9A9B-82654EDE7E62")] - internal interface IPropertyValue - { - byte GetUInt8(); - short GetInt16(); - ushort GetUInt16(); - int GetInt32(); - uint GetUInt32(); - long GetInt64(); - ulong GetUInt64(); - float GetSingle(); - double GetDouble(); - char GetChar16(); - bool GetBoolean(); - string GetString(); - Guid GetGuid(); - global::System.DateTimeOffset GetDateTime(); - global::System.TimeSpan GetTimeSpan(); - Point GetPoint(); - Size GetSize(); - Rect GetRect(); - void GetUInt8Array(out byte[] value); - void GetInt16Array(out short[] value); - void GetUInt16Array(out ushort[] value); - void GetInt32Array(out int[] value); - void GetUInt32Array(out uint[] value); - void GetInt64Array(out long[] value); - void GetUInt64Array(out ulong[] value); - void GetSingleArray(out float[] value); - void GetDoubleArray(out double[] value); - void GetChar16Array(out char[] value); - void GetBooleanArray(out bool[] value); - void GetStringArray(out string[] value); - void GetInspectableArray(out object[] value); - void GetGuidArray(out Guid[] value); - void GetDateTimeArray(out global::System.DateTimeOffset[] value); - void GetTimeSpanArray(out global::System.TimeSpan[] value); - void GetPointArray(out Point[] value); - void GetSizeArray(out Size[] value); - void GetRectArray(out Rect[] value); - bool IsNumericScalar { get; } - PropertyType Type { get; } - } -} - -namespace ABI.Windows.Foundation -{ - internal static class ManagedIPropertyValueImpl - { - private const int TYPE_E_TYPEMISMATCH = unchecked((int)0x80028CA0); - private const int DISP_E_OVERFLOW = unchecked((int)0x8002000A); - private static IPropertyValue.Vftbl AbiToProjectionVftable; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using WinRT; + +namespace Windows.Foundation +{ + internal enum PropertyType : uint + { + Empty = 0, + UInt8 = 0x1, + Int16 = 0x2, + UInt16 = 0x3, + Int32 = 0x4, + UInt32 = 0x5, + Int64 = 0x6, + UInt64 = 0x7, + Single = 0x8, + Double = 0x9, + Char16 = 0xa, + Boolean = 0xb, + String = 0xc, + Inspectable = 0xd, + DateTime = 0xe, + TimeSpan = 0xf, + Guid = 0x10, + Point = 0x11, + Size = 0x12, + Rect = 0x13, + OtherType = 0x14, + UInt8Array = 0x401, + Int16Array = 0x402, + UInt16Array = 0x403, + Int32Array = 0x404, + UInt32Array = 0x405, + Int64Array = 0x406, + UInt64Array = 0x407, + SingleArray = 0x408, + DoubleArray = 0x409, + Char16Array = 0x40a, + BooleanArray = 0x40b, + StringArray = 0x40c, + InspectableArray = 0x40d, + DateTimeArray = 0x40e, + TimeSpanArray = 0x40f, + GuidArray = 0x410, + PointArray = 0x411, + SizeArray = 0x412, + RectArray = 0x413, + OtherTypeArray = 0x414, + } +} + +namespace ABI.Windows.Foundation +{ + internal static class ManagedIPropertyValueImpl + { + private const int TYPE_E_TYPEMISMATCH = unchecked((int)0x80028CA0); + private const int DISP_E_OVERFLOW = unchecked((int)0x8002000A); + private static IPropertyValue.Vftbl AbiToProjectionVftable; public static IntPtr AbiToProjectionVftablePtr; - internal static readonly Guid IID = new(0x4BD682DD, 0x7554, 0x40E9, 0x9A, 0x9B, 0x82, 0x65, 0x4E, 0xDE, 0x7E, 0x62); - - static unsafe ManagedIPropertyValueImpl() - { - AbiToProjectionVftable = new IPropertyValue.Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - - _get_Type_0 = (delegate* unmanaged)&Do_Abi_get_Type_0, - _get_IsNumericScalar_1 = (delegate* unmanaged)&Do_Abi_get_IsNumericScalar_1, - _GetUInt8_2 = (delegate* unmanaged)&Do_Abi_GetUInt8_2, - _GetInt16_3 = (delegate* unmanaged)&Do_Abi_GetInt16_3, - _GetUInt16_4 = (delegate* unmanaged)&Do_Abi_GetUInt16_4, - _GetInt32_5 = (delegate* unmanaged)&Do_Abi_GetInt32_5, - _GetUInt32_6 = (delegate* unmanaged)&Do_Abi_GetUInt32_6, - _GetInt64_7 = (delegate* unmanaged)&Do_Abi_GetInt64_7, - _GetUInt64_8 = (delegate* unmanaged)&Do_Abi_GetUInt64_8, - _GetSingle_9 = (delegate* unmanaged)&Do_Abi_GetSingle_9, - _GetDouble_10 = (delegate* unmanaged)&Do_Abi_GetDouble_10, - _GetChar16_11 = (delegate* unmanaged)&Do_Abi_GetChar16_11, - _GetBoolean_12 = (delegate* unmanaged)&Do_Abi_GetBoolean_12, - _GetString_13 = (delegate* unmanaged)&Do_Abi_GetString_13, - _GetGuid_14 = (delegate* unmanaged)&Do_Abi_GetGuid_14, - _GetDateTime_15 = (delegate* unmanaged)&Do_Abi_GetDateTime_15, - _GetTimeSpan_16 = (delegate* unmanaged)&Do_Abi_GetTimeSpan_16, - _GetPoint_17 = (delegate* unmanaged)&Do_Abi_GetPoint_17, - _GetSize_18 = (delegate* unmanaged)&Do_Abi_GetSize_18, - _GetRect_19 = (delegate* unmanaged)&Do_Abi_GetRect_19, - _GetUInt8Array_20 = (delegate* unmanaged)&Do_Abi_GetUInt8Array_20, - _GetInt16Array_21 = (delegate* unmanaged)&Do_Abi_GetInt16Array_21, - _GetUInt16Array_22 = (delegate* unmanaged)&Do_Abi_GetUInt16Array_22, - _GetInt32Array_23 = (delegate* unmanaged)&Do_Abi_GetInt32Array_23, - _GetUInt32Array_24 = (delegate* unmanaged)&Do_Abi_GetUInt32Array_24, - _GetInt64Array_25 = (delegate* unmanaged)&Do_Abi_GetInt64Array_25, - _GetUInt64Array_26 = (delegate* unmanaged)&Do_Abi_GetUInt64Array_26, - _GetSingleArray_27 = (delegate* unmanaged)&Do_Abi_GetSingleArray_27, - _GetDoubleArray_28 = (delegate* unmanaged)&Do_Abi_GetDoubleArray_28, - _GetChar16Array_29 = (delegate* unmanaged)&Do_Abi_GetChar16Array_29, - _GetBooleanArray_30 = (delegate* unmanaged)&Do_Abi_GetBooleanArray_30, - _GetStringArray_31 = (delegate* unmanaged)&Do_Abi_GetStringArray_31, - _GetInspectableArray_32 = (delegate* unmanaged)&Do_Abi_GetInspectableArray_32, - _GetGuidArray_33 = (delegate* unmanaged)&Do_Abi_GetGuidArray_33, - _GetDateTimeArray_34 = (delegate* unmanaged)&Do_Abi_GetDateTimeArray_34, - _GetTimeSpanArray_35 = (delegate* unmanaged)&Do_Abi_GetTimeSpanArray_35, - _GetPointArray_36 = (delegate* unmanaged)&Do_Abi_GetPointArray_36, - _GetSizeArray_37 = (delegate* unmanaged)&Do_Abi_GetSizeArray_37, - _GetRectArray_38 = (delegate* unmanaged)&Do_Abi_GetRectArray_38, - - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(ManagedIPropertyValueImpl), Marshal.SizeOf() + sizeof(IntPtr) * 39); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - private static volatile Dictionary s_numericScalarTypes; - - private static Dictionary NumericScalarTypes - { - get - { - if (s_numericScalarTypes == null) - { - var numericScalarTypes = new Dictionary { - { typeof(byte), global::Windows.Foundation.PropertyType.UInt8 }, - { typeof(short), global::Windows.Foundation.PropertyType.Int16 }, - { typeof(ushort), global::Windows.Foundation.PropertyType.UInt16 }, - { typeof(int), global::Windows.Foundation.PropertyType.Int32 }, - { typeof(uint), global::Windows.Foundation.PropertyType.UInt32 }, - { typeof(long), global::Windows.Foundation.PropertyType.Int64 }, - { typeof(ulong), global::Windows.Foundation.PropertyType.UInt64 }, - { typeof(float), global::Windows.Foundation.PropertyType.Single }, - { typeof(double), global::Windows.Foundation.PropertyType.Double } - }; - - s_numericScalarTypes = numericScalarTypes; - } - - return s_numericScalarTypes; - } - } - - /// - /// Unbox a value of a projected Windows.Foundation struct type - /// to a structurally equivalent type with the same name. - /// - /// The target type. - /// The object to unbox. - /// The unboxed value. - private static T UnboxValue(object value) - where T : struct - { - Type valueType = value.GetType(); - - if (valueType.FullName == typeof(T).FullName && Marshal.SizeOf(valueType) == Marshal.SizeOf()) - { - return Unsafe.As>(value).Value; - } - - throw new InvalidCastException("", TYPE_E_TYPEMISMATCH); - } - -#pragma warning disable CS0649 - private sealed class Boxed - where T : struct - { - public T Value; - } -#pragma warning restore CS0649 - - private static T[] UnboxArray(object value) - where T : struct - { - if (!(value is Array dataArray)) - { - throw new InvalidCastException(); - } - - // If we do not have the correct array type, then we need to convert the array element-by-element - // to a new array of the requested type - T[] coercedArray = new T[dataArray.Length]; - for (int i = 0; i < dataArray.Length; ++i) - { - try - { - coercedArray[i] = UnboxValue(dataArray.GetValue(i)); - } - catch (InvalidCastException elementCastException) - { - //global::System.Exception e = new InvalidCastException(string.Format(SR.InvalidCast_WinRTIPropertyValueArrayCoersion, value.GetType(), typeof(T[]).Name, i, elementCastException.Message), elementCastException.HResult); - global::System.Exception e = new InvalidCastException("", elementCastException.HResult); - throw e; - } - } - return coercedArray; - } - - private static bool IsCoercable(object value) - { - // String <--> Guid is allowed - // Converting from an object to a string, Guid, or numeric scalar is allowed. - if (value.GetType() == typeof(string) || value.GetType() == typeof(Guid) || value.GetType() != typeof(object)) - { - return true; - } - - // All numeric scalars can also be coerced - return NumericScalarTypes.TryGetValue(value.GetType(), out _); - } - - /// - /// Coerce the managd object to an object of type . - /// - /// The target type. - /// The value. - /// The coerced value. - private static T CoerceValue(object value) - { - if (value is T u) - { - return u; - } - - if (value is global::Windows.Foundation.IPropertyValue ipv) - { - if (typeof(T) == typeof(byte)) - { - return (T)(object)ipv.GetUInt8(); - } - if (typeof(T) == typeof(short)) - { - return (T)(object)ipv.GetInt16(); - } - if (typeof(T) == typeof(ushort)) - { - return (T)(object)ipv.GetUInt16(); - } - if (typeof(T) == typeof(int)) - { - return (T)(object)ipv.GetInt32(); - } - if (typeof(T) == typeof(uint)) - { - return (T)(object)ipv.GetUInt32(); - } - if (typeof(T) == typeof(long)) - { - return (T)(object)ipv.GetInt64(); - } - if (typeof(T) == typeof(ulong)) - { - return (T)(object)ipv.GetUInt64(); - } - if (typeof(T) == typeof(float)) - { - return (T)(object)ipv.GetSingle(); - } - if (typeof(T) == typeof(double)) - { - return (T)(object)ipv.GetDouble(); - } - } - - if (!IsCoercable(value)) - { - throw new InvalidCastException(); - } - - try - { - if (value is string str && typeof(T) == typeof(Guid)) - { - return (T)(object)Guid.Parse(str); - } - else if (value is Guid guid && typeof(T) == typeof(string)) - { - return (T)(object)guid.ToString("D", global::System.Globalization.CultureInfo.InvariantCulture); - } - else - { - if (NumericScalarTypes.TryGetValue(typeof(T), out _)) - { - return (T)Convert.ChangeType(value, typeof(T), global::System.Globalization.CultureInfo.InvariantCulture); - } - } - } - catch (FormatException) - { - // throw new InvalidCastException(string.Format(SR.InvalidCast_WinRTIPropertyValueElement, value.GetType(), typeof(T).Name), TYPE_E_TYPEMISMATCH); - throw new InvalidCastException("", TYPE_E_TYPEMISMATCH); - } - catch (InvalidCastException) - { - // throw new InvalidCastException(string.Format(SR.InvalidCast_WinRTIPropertyValueElement, value.GetType(), typeof(T).Name), TYPE_E_TYPEMISMATCH); - throw new InvalidCastException("", TYPE_E_TYPEMISMATCH); - } - catch (OverflowException) - { - // throw new InvalidCastException(string.Format(SR.InvalidCast_WinRTIPropertyValueCoersion, value.GetType(), value, typeof(T).Name), DISP_E_OVERFLOW); - throw new InvalidCastException("", DISP_E_OVERFLOW); - } - - throw new InvalidCastException(); - } - - private static T[] CoerceArray(object value) - { - if (value is T[] arr) - { - return arr; - } - - if (!(value is Array dataArray)) - { - throw new InvalidCastException(); - } - - // If we do not have the correct array type, then we need to convert the array element-by-element - // to a new array of the requested type - T[] coercedArray = new T[dataArray.Length]; - for (int i = 0; i < dataArray.Length; ++i) - { - try - { - coercedArray[i] = CoerceValue(dataArray.GetValue(i)); - } - catch (InvalidCastException elementCastException) - { - //global::System.Exception e = new InvalidCastException(string.Format(SR.InvalidCast_WinRTIPropertyValueArrayCoersion, value.GetType(), typeof(T[]).Name, i, elementCastException.Message), elementCastException.HResult); - global::System.Exception e = new InvalidCastException("", elementCastException.HResult); - throw e; - } - } - return coercedArray; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetUInt8_2(IntPtr thisPtr, byte* value) - { - try - { - *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetInt16_3(IntPtr thisPtr, short* value) - { - try - { - *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetUInt16_4(IntPtr thisPtr, ushort* value) - { - try - { - *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetInt32_5(IntPtr thisPtr, int* value) - { - try - { - *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetUInt32_6(IntPtr thisPtr, uint* value) - { - try - { - *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetInt64_7(IntPtr thisPtr, long* value) - { - try - { - *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetUInt64_8(IntPtr thisPtr, ulong* value) - { - try - { - *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetSingle_9(IntPtr thisPtr, float* value) - { - try - { - *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetDouble_10(IntPtr thisPtr, double* value) - { - try - { - *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetChar16_11(IntPtr thisPtr, ushort* value) - { - - try - { - *value = (ushort)CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetBoolean_12(IntPtr thisPtr, byte* value) - { - - try - { - *value = (byte)(CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)) ? 1 : 0); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetString_13(IntPtr thisPtr, IntPtr* value) - { - - try - { - *value = MarshalString.FromManaged(CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr))); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetGuid_14(IntPtr thisPtr, Guid* value) - { - - try - { - *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetDateTime_15(IntPtr thisPtr, global::ABI.System.DateTimeOffset* value) - { - - try - { - *value = ABI.System.DateTimeOffset.FromManaged(CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr))); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetTimeSpan_16(IntPtr thisPtr, global::ABI.System.TimeSpan* value) - { - - try - { - *value = ABI.System.TimeSpan.FromManaged(CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr))); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetPoint_17(IntPtr thisPtr, global::Windows.Foundation.Point* value) - { - - try - { - *value = UnboxValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetSize_18(IntPtr thisPtr, global::Windows.Foundation.Size* value) - { - - try - { - *value = UnboxValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetRect_19(IntPtr thisPtr, global::Windows.Foundation.Rect* value) - { - - try - { - *value = UnboxValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetUInt8Array_20(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - byte[] __value = default; - - try - { - __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetInt16Array_21(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - short[] __value = default; - - try - { - __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetUInt16Array_22(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - ushort[] __value = default; - - try - { - __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetInt32Array_23(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - int[] __value = default; - - try - { - __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetUInt32Array_24(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - uint[] __value = default; - - try - { - __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetInt64Array_25(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - long[] __value = default; - - try - { - __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetUInt64Array_26(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - ulong[] __value = default; - - try - { - __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetSingleArray_27(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - float[] __value = default; - - try - { - __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetDoubleArray_28(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - double[] __value = default; - - try - { - __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetChar16Array_29(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - char[] __value = default; - - try - { - __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalNonBlittable.FromManagedArray(__value); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetBooleanArray_30(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - bool[] __value = default; - - try - { - __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalNonBlittable.FromManagedArray(__value); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetStringArray_31(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - string[] __value = default; - - try - { - __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalString.FromManagedArray(__value); - - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetInspectableArray_32(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - object[] __value = default; - - try - { - __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalInspectable.FromManagedArray(__value); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetGuidArray_33(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - Guid[] __value = default; - - try - { - __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); - - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetDateTimeArray_34(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - global::System.DateTimeOffset[] __value = default; - - try - { - __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalNonBlittable.FromManagedArray(__value); - - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetTimeSpanArray_35(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - global::System.TimeSpan[] __value = default; - - try - { - __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalNonBlittable.FromManagedArray(__value); - - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetPointArray_36(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - global::Windows.Foundation.Point[] __value = default; - - try - { - __value = UnboxArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); - - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetSizeArray_37(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - global::Windows.Foundation.Size[] __value = default; - - try - { - __value = UnboxArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); - - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_GetRectArray_38(IntPtr thisPtr, int* __valueSize, IntPtr* value) - { - - - - global::Windows.Foundation.Rect[] __value = default; - - try - { - __value = UnboxArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); - - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_get_IsNumericScalar_1(IntPtr thisPtr, byte* value) - { - - try - { - *value = (byte)(NumericScalarTypes.TryGetValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr).GetType(), out _) ? 1 : 0); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - - [UnmanagedCallersOnly] - - private static unsafe int Do_Abi_get_Type_0(IntPtr thisPtr, global::Windows.Foundation.PropertyType* value) - { - - try - { - *value = GetPropertyTypeOfObject(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - - private static unsafe global::Windows.Foundation.PropertyType GetPropertyTypeOfObject(object obj) - { - global::Windows.Foundation.PropertyType value; - global::System.Type managedType = obj.GetType(); - bool isArray = managedType.IsArray; - if (isArray) - { - managedType = managedType.GetElementType(); - } - if (!NumericScalarTypes.TryGetValue(managedType, out value)) - { - if (managedType == typeof(string)) - { - value = global::Windows.Foundation.PropertyType.String; - } - else if (managedType == typeof(char)) - { - value = global::Windows.Foundation.PropertyType.Char16; - } - else if (managedType == typeof(bool)) - { - value = global::Windows.Foundation.PropertyType.Boolean; - } - else if (managedType == typeof(global::System.DateTimeOffset)) - { - value = global::Windows.Foundation.PropertyType.DateTime; - } - else if (managedType == typeof(global::System.TimeSpan)) - { - value = global::Windows.Foundation.PropertyType.TimeSpan; - } - else if (managedType == typeof(global::System.Guid)) - { - value = global::Windows.Foundation.PropertyType.Guid; - } - else if (string.CompareOrdinal(managedType.FullName, "Windows.Foundation.Point") == 0) - { - value = global::Windows.Foundation.PropertyType.Point; - } - else if (string.CompareOrdinal(managedType.FullName, "Windows.Foundation.Rect") == 0) - { - value = global::Windows.Foundation.PropertyType.Rect; - } - else if (string.CompareOrdinal(managedType.FullName, "Windows.Foundation.Size") == 0) - { - value = global::Windows.Foundation.PropertyType.Size; - } - else if (managedType == typeof(object)) - { - value = global::Windows.Foundation.PropertyType.Inspectable; - } - else if (typeof(Delegate).IsAssignableFrom(managedType)) - { - value = global::Windows.Foundation.PropertyType.OtherType; - } - else if (!managedType.IsValueType && managedType != typeof(Type) && isArray) - { - // Treat arrays of interfaces as though they are arrays of object. - value = global::Windows.Foundation.PropertyType.Inspectable; - } - else - { - value = global::Windows.Foundation.PropertyType.OtherType; - } - } - if (isArray) - { - // The array values for Windows.Foundation.PropertyType are all 1024 above their scalar equivalents - value = (global::Windows.Foundation.PropertyType)((int)value + 1024); - } - - return value; - } - } - - - - [DynamicInterfaceCastableImplementation] - [Guid("4BD682DD-7554-40E9-9A9B-82654EDE7E62")] - internal unsafe interface IPropertyValue : global::Windows.Foundation.IPropertyValue - { - [Guid("4BD682DD-7554-40E9-9A9B-82654EDE7E62")] - public struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - internal void* _get_Type_0; - public delegate* unmanaged[Stdcall] get_Type_0 { get => (delegate* unmanaged[Stdcall])_get_Type_0; set => _get_Type_0 = value; } - public void* _get_IsNumericScalar_1; - public delegate* unmanaged[Stdcall] get_IsNumericScalar_1 { get => (delegate* unmanaged[Stdcall])_get_IsNumericScalar_1; set => _get_IsNumericScalar_1 = value; } - internal void* _GetUInt8_2; - public delegate* unmanaged[Stdcall] GetUInt8_2 { get => (delegate* unmanaged[Stdcall])_GetUInt8_2; set => _GetUInt8_2 = value; } - internal void* _GetInt16_3; - public delegate* unmanaged[Stdcall] GetInt16_3 { get => (delegate* unmanaged[Stdcall])_GetInt16_3; set => _GetInt16_3 = value; } - internal void* _GetUInt16_4; - public delegate* unmanaged[Stdcall] GetUInt16_4 { get => (delegate* unmanaged[Stdcall])_GetUInt16_4; set => _GetUInt16_4 = value; } - internal void* _GetInt32_5; - public delegate* unmanaged[Stdcall] GetInt32_5 { get => (delegate* unmanaged[Stdcall])_GetInt32_5; set => _GetInt32_5 = value; } - internal void* _GetUInt32_6; - public delegate* unmanaged[Stdcall] GetUInt32_6 { get => (delegate* unmanaged[Stdcall])_GetUInt32_6; set => _GetUInt32_6 = value; } - internal void* _GetInt64_7; - public delegate* unmanaged[Stdcall] GetInt64_7 { get => (delegate* unmanaged[Stdcall])_GetInt64_7; set => _GetInt64_7 = value; } - internal void* _GetUInt64_8; - public delegate* unmanaged[Stdcall] GetUInt64_8 { get => (delegate* unmanaged[Stdcall])_GetUInt64_8; set => _GetUInt64_8 = value; } - internal void* _GetSingle_9; - public delegate* unmanaged[Stdcall] GetSingle_9 { get => (delegate* unmanaged[Stdcall])_GetSingle_9; set => _GetSingle_9 = value; } - internal void* _GetDouble_10; - public delegate* unmanaged[Stdcall] GetDouble_10 { get => (delegate* unmanaged[Stdcall])_GetDouble_10; set => _GetDouble_10 = value; } - internal void* _GetChar16_11; - public delegate* unmanaged[Stdcall] GetChar16_11 { get => (delegate* unmanaged[Stdcall])_GetChar16_11; set => _GetChar16_11 = value; } - internal void* _GetBoolean_12; - public delegate* unmanaged[Stdcall] GetBoolean_12 { get => (delegate* unmanaged[Stdcall])_GetBoolean_12; set => _GetBoolean_12 = value; } - internal void* _GetString_13; - public delegate* unmanaged[Stdcall] GetString_13 { get => (delegate* unmanaged[Stdcall])_GetString_13; set => _GetString_13 = value; } - internal void* _GetGuid_14; - public delegate* unmanaged[Stdcall] GetGuid_14 { get => (delegate* unmanaged[Stdcall])_GetGuid_14; set => _GetGuid_14 = value; } - internal void* _GetDateTime_15; - public delegate* unmanaged[Stdcall] GetDateTime_15 { get => (delegate* unmanaged[Stdcall])_GetDateTime_15; set => _GetDateTime_15 = value; } - internal void* _GetTimeSpan_16; - public delegate* unmanaged[Stdcall] GetTimeSpan_16 { get => (delegate* unmanaged[Stdcall])_GetTimeSpan_16; set => _GetTimeSpan_16 = value; } - internal void* _GetPoint_17; - public delegate* unmanaged[Stdcall] GetPoint_17 { get => (delegate* unmanaged[Stdcall])_GetPoint_17; set => _GetPoint_17 = value; } - internal void* _GetSize_18; - public delegate* unmanaged[Stdcall] GetSize_18 { get => (delegate* unmanaged[Stdcall])_GetSize_18; set => _GetSize_18 = value; } - internal void* _GetRect_19; - public delegate* unmanaged[Stdcall] GetRect_19 { get => (delegate* unmanaged[Stdcall])_GetRect_19; set => _GetRect_19 = value; } - internal void* _GetUInt8Array_20; - public delegate* unmanaged[Stdcall] GetUInt8Array_20 { get => (delegate* unmanaged[Stdcall])_GetUInt8Array_20; set => _GetUInt8Array_20 = value; } - internal void* _GetInt16Array_21; - public delegate* unmanaged[Stdcall] GetInt16Array_21 { get => (delegate* unmanaged[Stdcall])_GetInt16Array_21; set => _GetInt16Array_21 = value; } - internal void* _GetUInt16Array_22; - public delegate* unmanaged[Stdcall] GetUInt16Array_22 { get => (delegate* unmanaged[Stdcall])_GetUInt16Array_22; set => _GetUInt16Array_22 = value; } - internal void* _GetInt32Array_23; - public delegate* unmanaged[Stdcall] GetInt32Array_23 { get => (delegate* unmanaged[Stdcall])_GetInt32Array_23; set => _GetInt32Array_23 = value; } - internal void* _GetUInt32Array_24; - public delegate* unmanaged[Stdcall] GetUInt32Array_24 { get => (delegate* unmanaged[Stdcall])_GetUInt32Array_24; set => _GetUInt32Array_24 = value; } - internal void* _GetInt64Array_25; - public delegate* unmanaged[Stdcall] GetInt64Array_25 { get => (delegate* unmanaged[Stdcall])_GetInt64Array_25; set => _GetInt64Array_25 = value; } - internal void* _GetUInt64Array_26; - public delegate* unmanaged[Stdcall] GetUInt64Array_26 { get => (delegate* unmanaged[Stdcall])_GetUInt64Array_26; set => _GetUInt64Array_26 = value; } - internal void* _GetSingleArray_27; - public delegate* unmanaged[Stdcall] GetSingleArray_27 { get => (delegate* unmanaged[Stdcall])_GetSingleArray_27; set => _GetSingleArray_27 = value; } - internal void* _GetDoubleArray_28; - public delegate* unmanaged[Stdcall] GetDoubleArray_28 { get => (delegate* unmanaged[Stdcall])_GetDoubleArray_28; set => _GetDoubleArray_28 = value; } - internal void* _GetChar16Array_29; - public delegate* unmanaged[Stdcall] GetChar16Array_29 { get => (delegate* unmanaged[Stdcall])_GetChar16Array_29; set => _GetChar16Array_29 = value; } - internal void* _GetBooleanArray_30; - public delegate* unmanaged[Stdcall] GetBooleanArray_30 { get => (delegate* unmanaged[Stdcall])_GetBooleanArray_30; set => _GetBooleanArray_30 = value; } - internal void* _GetStringArray_31; - public delegate* unmanaged[Stdcall] GetStringArray_31 { get => (delegate* unmanaged[Stdcall])_GetStringArray_31; set => _GetStringArray_31 = value; } - internal void* _GetInspectableArray_32; - public delegate* unmanaged[Stdcall] GetInspectableArray_32 { get => (delegate* unmanaged[Stdcall])_GetInspectableArray_32; set => _GetInspectableArray_32 = value; } - internal void* _GetGuidArray_33; - public delegate* unmanaged[Stdcall] GetGuidArray_33 { get => (delegate* unmanaged[Stdcall])_GetGuidArray_33; set => _GetGuidArray_33 = value; } - internal void* _GetDateTimeArray_34; - public delegate* unmanaged[Stdcall] GetDateTimeArray_34 { get => (delegate* unmanaged[Stdcall])_GetDateTimeArray_34; set => _GetDateTimeArray_34 = value; } - internal void* _GetTimeSpanArray_35; - public delegate* unmanaged[Stdcall] GetTimeSpanArray_35 { get => (delegate* unmanaged[Stdcall])_GetTimeSpanArray_35; set => _GetTimeSpanArray_35 = value; } - internal void* _GetPointArray_36; - public delegate* unmanaged[Stdcall] GetPointArray_36 { get => (delegate* unmanaged[Stdcall])_GetPointArray_36; set => _GetPointArray_36 = value; } - internal void* _GetSizeArray_37; - public delegate* unmanaged[Stdcall] GetSizeArray_37 { get => (delegate* unmanaged[Stdcall])_GetSizeArray_37; set => _GetSizeArray_37 = value; } - internal void* _GetRectArray_38; - public delegate* unmanaged[Stdcall] GetRectArray_38 { get => (delegate* unmanaged[Stdcall])_GetRectArray_38; set => _GetRectArray_38 = value; } - } - - unsafe byte global::Windows.Foundation.IPropertyValue.GetUInt8() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - byte __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetUInt8_2(ThisPtr, out __retval)); - return __retval; - } - - unsafe short global::Windows.Foundation.IPropertyValue.GetInt16() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - short __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInt16_3(ThisPtr, out __retval)); - return __retval; - } - - unsafe ushort global::Windows.Foundation.IPropertyValue.GetUInt16() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - ushort __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetUInt16_4(ThisPtr, out __retval)); - return __retval; - } - - unsafe int global::Windows.Foundation.IPropertyValue.GetInt32() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInt32_5(ThisPtr, out __retval)); - return __retval; - } - - unsafe uint global::Windows.Foundation.IPropertyValue.GetUInt32() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - uint __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetUInt32_6(ThisPtr, out __retval)); - return __retval; - } - - unsafe long global::Windows.Foundation.IPropertyValue.GetInt64() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - long __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInt64_7(ThisPtr, out __retval)); - return __retval; - } - - unsafe ulong global::Windows.Foundation.IPropertyValue.GetUInt64() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - ulong __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetUInt64_8(ThisPtr, out __retval)); - return __retval; - } - - unsafe float global::Windows.Foundation.IPropertyValue.GetSingle() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - float __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetSingle_9(ThisPtr, out __retval)); - return __retval; - } - - unsafe double global::Windows.Foundation.IPropertyValue.GetDouble() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - double __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetDouble_10(ThisPtr, out __retval)); - return __retval; - } - - unsafe char global::Windows.Foundation.IPropertyValue.GetChar16() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - ushort __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetChar16_11(ThisPtr, out __retval)); - return (char)__retval; - } - - unsafe bool global::Windows.Foundation.IPropertyValue.GetBoolean() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - byte __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetBoolean_12(ThisPtr, out __retval)); - return __retval != 0; - } - - unsafe string global::Windows.Foundation.IPropertyValue.GetString() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - IntPtr __retval = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetString_13(ThisPtr, out __retval)); - return MarshalString.FromAbi(__retval); - } - finally - { - MarshalString.DisposeAbi(__retval); - } - } - - unsafe Guid global::Windows.Foundation.IPropertyValue.GetGuid() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - Guid __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetGuid_14(ThisPtr, out __retval)); - return __retval; - } - - unsafe global::System.DateTimeOffset global::Windows.Foundation.IPropertyValue.GetDateTime() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - global::ABI.System.DateTimeOffset __retval = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetDateTime_15(ThisPtr, out __retval)); - return global::ABI.System.DateTimeOffset.FromAbi(__retval); - } - finally - { - global::ABI.System.DateTimeOffset.DisposeAbi(__retval); - } - } - - unsafe global::System.TimeSpan global::Windows.Foundation.IPropertyValue.GetTimeSpan() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - global::ABI.System.TimeSpan __retval = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetTimeSpan_16(ThisPtr, out __retval)); - return global::ABI.System.TimeSpan.FromAbi(__retval); - } - finally - { - global::ABI.System.TimeSpan.DisposeAbi(__retval); - } - } - - unsafe global::Windows.Foundation.Point global::Windows.Foundation.IPropertyValue.GetPoint() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - global::Windows.Foundation.Point __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetPoint_17(ThisPtr, out __retval)); - return __retval; - } - - unsafe global::Windows.Foundation.Size global::Windows.Foundation.IPropertyValue.GetSize() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - global::Windows.Foundation.Size __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetSize_18(ThisPtr, out __retval)); - return __retval; - } - - unsafe global::Windows.Foundation.Rect global::Windows.Foundation.IPropertyValue.GetRect() - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - global::Windows.Foundation.Rect __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetRect_19(ThisPtr, out __retval)); - return __retval; - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetUInt8Array(out byte[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetUInt8Array_20(ThisPtr, out __value_length, out __value_data)); - value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetInt16Array(out short[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInt16Array_21(ThisPtr, out __value_length, out __value_data)); - value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetUInt16Array(out ushort[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetUInt16Array_22(ThisPtr, out __value_length, out __value_data)); - value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetInt32Array(out int[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInt32Array_23(ThisPtr, out __value_length, out __value_data)); - value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetUInt32Array(out uint[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetUInt32Array_24(ThisPtr, out __value_length, out __value_data)); - value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetInt64Array(out long[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInt64Array_25(ThisPtr, out __value_length, out __value_data)); - value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetUInt64Array(out ulong[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetUInt64Array_26(ThisPtr, out __value_length, out __value_data)); - value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetSingleArray(out float[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetSingleArray_27(ThisPtr, out __value_length, out __value_data)); - value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetDoubleArray(out double[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetDoubleArray_28(ThisPtr, out __value_length, out __value_data)); - value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetChar16Array(out char[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetChar16Array_29(ThisPtr, out __value_length, out __value_data)); - value = MarshalNonBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalNonBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetBooleanArray(out bool[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetBooleanArray_30(ThisPtr, out __value_length, out __value_data)); - value = MarshalNonBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalNonBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetStringArray(out string[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetStringArray_31(ThisPtr, out __value_length, out __value_data)); - value = MarshalString.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalString.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetInspectableArray(out object[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInspectableArray_32(ThisPtr, out __value_length, out __value_data)); - value = MarshalInspectable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalInspectable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetGuidArray(out Guid[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetGuidArray_33(ThisPtr, out __value_length, out __value_data)); - value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetDateTimeArray(out global::System.DateTimeOffset[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetDateTimeArray_34(ThisPtr, out __value_length, out __value_data)); - value = MarshalNonBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalNonBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetTimeSpanArray(out global::System.TimeSpan[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetTimeSpanArray_35(ThisPtr, out __value_length, out __value_data)); - value = MarshalNonBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalNonBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetPointArray(out global::Windows.Foundation.Point[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetPointArray_36(ThisPtr, out __value_length, out __value_data)); - value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetSizeArray(out global::Windows.Foundation.Size[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetSizeArray_37(ThisPtr, out __value_length, out __value_data)); - value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe void global::Windows.Foundation.IPropertyValue.GetRectArray(out global::Windows.Foundation.Rect[] value) - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - int __value_length = default; - IntPtr __value_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetRectArray_38(ThisPtr, out __value_length, out __value_data)); - value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); - } - finally - { - MarshalBlittable.DisposeAbiArray((__value_length, __value_data)); - } - } - - unsafe bool global::Windows.Foundation.IPropertyValue.IsNumericScalar - { - get - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - byte __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_IsNumericScalar_1(ThisPtr, out __retval)); - return __retval != 0; - } - } - - unsafe global::Windows.Foundation.PropertyType global::Windows.Foundation.IPropertyValue.Type - { - get - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Foundation.IPropertyValue).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - global::Windows.Foundation.PropertyType __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Type_0(ThisPtr, out __retval)); - return __retval; - } - } - } - - internal static class IPropertyValue_Delegates - { - public unsafe delegate int get_Type_0(IntPtr thisPtr, global::Windows.Foundation.PropertyType* value); - public unsafe delegate int get_IsNumericScalar_1(IntPtr thisPtr, byte* value); - public unsafe delegate int GetUInt8_2(IntPtr thisPtr, byte* value); - public unsafe delegate int GetInt16_3(IntPtr thisPtr, short* value); - public unsafe delegate int GetUInt16_4(IntPtr thisPtr, ushort* value); - public unsafe delegate int GetInt32_5(IntPtr thisPtr, int* value); - public unsafe delegate int GetUInt32_6(IntPtr thisPtr, uint* value); - public unsafe delegate int GetInt64_7(IntPtr thisPtr, long* value); - public unsafe delegate int GetUInt64_8(IntPtr thisPtr, ulong* value); - public unsafe delegate int GetSingle_9(IntPtr thisPtr, float* value); - public unsafe delegate int GetDouble_10(IntPtr thisPtr, double* value); - public unsafe delegate int GetChar16_11(IntPtr thisPtr, ushort* value); - public unsafe delegate int GetBoolean_12(IntPtr thisPtr, byte* value); - public unsafe delegate int GetString_13(IntPtr thisPtr, IntPtr* value); - public unsafe delegate int GetGuid_14(IntPtr thisPtr, Guid* value); - public unsafe delegate int GetDateTime_15(IntPtr thisPtr, global::ABI.System.DateTimeOffset* value); - public unsafe delegate int GetTimeSpan_16(IntPtr thisPtr, global::ABI.System.TimeSpan* value); - public unsafe delegate int GetPoint_17(IntPtr thisPtr, global::Windows.Foundation.Point* value); - public unsafe delegate int GetSize_18(IntPtr thisPtr, global::Windows.Foundation.Size* value); - public unsafe delegate int GetRect_19(IntPtr thisPtr, global::Windows.Foundation.Rect* value); - public unsafe delegate int GetUInt8Array_20(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetInt16Array_21(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetUInt16Array_22(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetInt32Array_23(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetUInt32Array_24(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetInt64Array_25(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetUInt64Array_26(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetSingleArray_27(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetDoubleArray_28(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetChar16Array_29(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetBooleanArray_30(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetStringArray_31(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetInspectableArray_32(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetGuidArray_33(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetDateTimeArray_34(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetTimeSpanArray_35(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetPointArray_36(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetSizeArray_37(IntPtr thisPtr, int* __valueSize, IntPtr* value); - public unsafe delegate int GetRectArray_38(IntPtr thisPtr, int* __valueSize, IntPtr* value); - } -} + static unsafe ManagedIPropertyValueImpl() + { + AbiToProjectionVftable = new IPropertyValue.Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, + + _get_Type_0 = (delegate* unmanaged)&Do_Abi_get_Type_0, + _get_IsNumericScalar_1 = (delegate* unmanaged)&Do_Abi_get_IsNumericScalar_1, + _GetUInt8_2 = (delegate* unmanaged)&Do_Abi_GetUInt8_2, + _GetInt16_3 = (delegate* unmanaged)&Do_Abi_GetInt16_3, + _GetUInt16_4 = (delegate* unmanaged)&Do_Abi_GetUInt16_4, + _GetInt32_5 = (delegate* unmanaged)&Do_Abi_GetInt32_5, + _GetUInt32_6 = (delegate* unmanaged)&Do_Abi_GetUInt32_6, + _GetInt64_7 = (delegate* unmanaged)&Do_Abi_GetInt64_7, + _GetUInt64_8 = (delegate* unmanaged)&Do_Abi_GetUInt64_8, + _GetSingle_9 = (delegate* unmanaged)&Do_Abi_GetSingle_9, + _GetDouble_10 = (delegate* unmanaged)&Do_Abi_GetDouble_10, + _GetChar16_11 = (delegate* unmanaged)&Do_Abi_GetChar16_11, + _GetBoolean_12 = (delegate* unmanaged)&Do_Abi_GetBoolean_12, + _GetString_13 = (delegate* unmanaged)&Do_Abi_GetString_13, + _GetGuid_14 = (delegate* unmanaged)&Do_Abi_GetGuid_14, + _GetDateTime_15 = (delegate* unmanaged)&Do_Abi_GetDateTime_15, + _GetTimeSpan_16 = (delegate* unmanaged)&Do_Abi_GetTimeSpan_16, + _GetPoint_17 = (delegate* unmanaged)&Do_Abi_GetPoint_17, + _GetSize_18 = (delegate* unmanaged)&Do_Abi_GetSize_18, + _GetRect_19 = (delegate* unmanaged)&Do_Abi_GetRect_19, + _GetUInt8Array_20 = (delegate* unmanaged)&Do_Abi_GetUInt8Array_20, + _GetInt16Array_21 = (delegate* unmanaged)&Do_Abi_GetInt16Array_21, + _GetUInt16Array_22 = (delegate* unmanaged)&Do_Abi_GetUInt16Array_22, + _GetInt32Array_23 = (delegate* unmanaged)&Do_Abi_GetInt32Array_23, + _GetUInt32Array_24 = (delegate* unmanaged)&Do_Abi_GetUInt32Array_24, + _GetInt64Array_25 = (delegate* unmanaged)&Do_Abi_GetInt64Array_25, + _GetUInt64Array_26 = (delegate* unmanaged)&Do_Abi_GetUInt64Array_26, + _GetSingleArray_27 = (delegate* unmanaged)&Do_Abi_GetSingleArray_27, + _GetDoubleArray_28 = (delegate* unmanaged)&Do_Abi_GetDoubleArray_28, + _GetChar16Array_29 = (delegate* unmanaged)&Do_Abi_GetChar16Array_29, + _GetBooleanArray_30 = (delegate* unmanaged)&Do_Abi_GetBooleanArray_30, + _GetStringArray_31 = (delegate* unmanaged)&Do_Abi_GetStringArray_31, + _GetInspectableArray_32 = (delegate* unmanaged)&Do_Abi_GetInspectableArray_32, + _GetGuidArray_33 = (delegate* unmanaged)&Do_Abi_GetGuidArray_33, + _GetDateTimeArray_34 = (delegate* unmanaged)&Do_Abi_GetDateTimeArray_34, + _GetTimeSpanArray_35 = (delegate* unmanaged)&Do_Abi_GetTimeSpanArray_35, + _GetPointArray_36 = (delegate* unmanaged)&Do_Abi_GetPointArray_36, + _GetSizeArray_37 = (delegate* unmanaged)&Do_Abi_GetSizeArray_37, + _GetRectArray_38 = (delegate* unmanaged)&Do_Abi_GetRectArray_38, + + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(ManagedIPropertyValueImpl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 39); + *(IPropertyValue.Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + + private static volatile Dictionary s_numericScalarTypes; + + private static Dictionary NumericScalarTypes + { + get + { + if (s_numericScalarTypes == null) + { + var numericScalarTypes = new Dictionary { + { typeof(byte), global::Windows.Foundation.PropertyType.UInt8 }, + { typeof(short), global::Windows.Foundation.PropertyType.Int16 }, + { typeof(ushort), global::Windows.Foundation.PropertyType.UInt16 }, + { typeof(int), global::Windows.Foundation.PropertyType.Int32 }, + { typeof(uint), global::Windows.Foundation.PropertyType.UInt32 }, + { typeof(long), global::Windows.Foundation.PropertyType.Int64 }, + { typeof(ulong), global::Windows.Foundation.PropertyType.UInt64 }, + { typeof(float), global::Windows.Foundation.PropertyType.Single }, + { typeof(double), global::Windows.Foundation.PropertyType.Double } + }; + + s_numericScalarTypes = numericScalarTypes; + } + + return s_numericScalarTypes; + } + } + + /// + /// Unbox a value of a projected Windows.Foundation struct type. + /// + /// The target type. + /// The object to unbox. + /// The unboxed value. + private static T UnboxValue(object value) + where T : struct + { + if (value.GetType() == typeof(T)) + { + return (T)value; + } + + throw new InvalidCastException("", TYPE_E_TYPEMISMATCH); + } + + private static T[] UnboxArray(object value) + where T : struct + { + if (!(value is Array dataArray)) + { + throw new InvalidCastException(); + } + + // If we do not have the correct array type, then we need to convert the array element-by-element + // to a new array of the requested type + T[] coercedArray = new T[dataArray.Length]; + for (int i = 0; i < dataArray.Length; ++i) + { + try + { + coercedArray[i] = UnboxValue(dataArray.GetValue(i)); + } + catch (InvalidCastException elementCastException) + { + //global::System.Exception e = new InvalidCastException(string.Format(SR.InvalidCast_WinRTIPropertyValueArrayCoersion, value.GetType(), typeof(T[]).Name, i, elementCastException.Message), elementCastException.HResult); + global::System.Exception e = new InvalidCastException("", elementCastException.HResult); + throw e; + } + } + return coercedArray; + } + + private static bool IsCoercable(object value) + { + // String <--> Guid is allowed + // Converting from an object to a string, Guid, or numeric scalar is allowed. + if (value.GetType() == typeof(string) || value.GetType() == typeof(Guid) || value.GetType() != typeof(object)) + { + return true; + } + + // All numeric scalars can also be coerced + return NumericScalarTypes.TryGetValue(value.GetType(), out _); + } + + /// + /// Coerce the managd object to an object of type . + /// + /// The target type. + /// The value. + /// The coerced value. + private static T CoerceValue(object value) + { + if (value is T u) + { + return u; + } + + if (!IsCoercable(value)) + { + throw new InvalidCastException(); + } + + try + { + if (value is string str && typeof(T) == typeof(Guid)) + { + return (T)(object)Guid.Parse(str); + } + else if (value is Guid guid && typeof(T) == typeof(string)) + { + return (T)(object)guid.ToString("D", global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(byte)) + { + return (T)(object)Convert.ToByte(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(short)) + { + return (T)(object)Convert.ToInt16(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(ushort)) + { + return (T)(object)Convert.ToUInt16(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(int)) + { + return (T)(object)Convert.ToInt32(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(uint)) + { + return (T)(object)Convert.ToUInt32(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(long)) + { + return (T)(object)Convert.ToInt64(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(ulong)) + { + return (T)(object)Convert.ToUInt64(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(float)) + { + return (T)(object)Convert.ToSingle(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(double)) + { + return (T)(object)Convert.ToDouble(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else + { + Debug.Assert(!NumericScalarTypes.ContainsKey(typeof(T))); + throw new InvalidCastException("", TYPE_E_TYPEMISMATCH); + } + } + catch (FormatException) + { + // throw new InvalidCastException(string.Format(SR.InvalidCast_WinRTIPropertyValueElement, value.GetType(), typeof(T).Name), TYPE_E_TYPEMISMATCH); + throw new InvalidCastException("", TYPE_E_TYPEMISMATCH); + } + catch (InvalidCastException) + { + // throw new InvalidCastException(string.Format(SR.InvalidCast_WinRTIPropertyValueElement, value.GetType(), typeof(T).Name), TYPE_E_TYPEMISMATCH); + throw new InvalidCastException("", TYPE_E_TYPEMISMATCH); + } + catch (OverflowException) + { + // throw new InvalidCastException(string.Format(SR.InvalidCast_WinRTIPropertyValueCoersion, value.GetType(), value, typeof(T).Name), DISP_E_OVERFLOW); + throw new InvalidCastException("", DISP_E_OVERFLOW); + } + + throw new InvalidCastException(); + } + + private static T[] CoerceArray(object value) + { + if (value is T[] arr) + { + return arr; + } + + if (!(value is Array dataArray)) + { + throw new InvalidCastException(); + } + + // If we do not have the correct array type, then we need to convert the array element-by-element + // to a new array of the requested type + T[] coercedArray = new T[dataArray.Length]; + for (int i = 0; i < dataArray.Length; ++i) + { + try + { + coercedArray[i] = CoerceValue(dataArray.GetValue(i)); + } + catch (InvalidCastException elementCastException) + { + //global::System.Exception e = new InvalidCastException(string.Format(SR.InvalidCast_WinRTIPropertyValueArrayCoersion, value.GetType(), typeof(T[]).Name, i, elementCastException.Message), elementCastException.HResult); + global::System.Exception e = new InvalidCastException("", elementCastException.HResult); + throw e; + } + } + return coercedArray; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetUInt8_2(IntPtr thisPtr, byte* value) + { + try + { + *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetInt16_3(IntPtr thisPtr, short* value) + { + try + { + *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetUInt16_4(IntPtr thisPtr, ushort* value) + { + try + { + *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetInt32_5(IntPtr thisPtr, int* value) + { + try + { + *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetUInt32_6(IntPtr thisPtr, uint* value) + { + try + { + *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetInt64_7(IntPtr thisPtr, long* value) + { + try + { + *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetUInt64_8(IntPtr thisPtr, ulong* value) + { + try + { + *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetSingle_9(IntPtr thisPtr, float* value) + { + try + { + *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetDouble_10(IntPtr thisPtr, double* value) + { + try + { + *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetChar16_11(IntPtr thisPtr, ushort* value) + { + + try + { + *value = (ushort)CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetBoolean_12(IntPtr thisPtr, byte* value) + { + + try + { + *value = (byte)(CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)) ? 1 : 0); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetString_13(IntPtr thisPtr, IntPtr* value) + { + + try + { + *value = MarshalString.FromManaged(CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr))); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetGuid_14(IntPtr thisPtr, Guid* value) + { + + try + { + *value = CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetDateTime_15(IntPtr thisPtr, global::ABI.System.DateTimeOffset* value) + { + + try + { + *value = ABI.System.DateTimeOffset.FromManaged(CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr))); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetTimeSpan_16(IntPtr thisPtr, global::ABI.System.TimeSpan* value) + { + + try + { + *value = ABI.System.TimeSpan.FromManaged(CoerceValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr))); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetPoint_17(IntPtr thisPtr, global::Windows.Foundation.Point* value) + { + + try + { + *value = UnboxValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetSize_18(IntPtr thisPtr, global::Windows.Foundation.Size* value) + { + + try + { + *value = UnboxValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetRect_19(IntPtr thisPtr, global::Windows.Foundation.Rect* value) + { + + try + { + *value = UnboxValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetUInt8Array_20(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + byte[] __value = default; + + try + { + __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetInt16Array_21(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + short[] __value = default; + + try + { + __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetUInt16Array_22(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + ushort[] __value = default; + + try + { + __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetInt32Array_23(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + int[] __value = default; + + try + { + __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetUInt32Array_24(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + uint[] __value = default; + + try + { + __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetInt64Array_25(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + long[] __value = default; + + try + { + __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetUInt64Array_26(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + ulong[] __value = default; + + try + { + __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetSingleArray_27(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + float[] __value = default; + + try + { + __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetDoubleArray_28(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + double[] __value = default; + + try + { + __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetChar16Array_29(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + char[] __value = default; + + try + { + __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalNonBlittable.FromManagedArray(__value); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetBooleanArray_30(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + bool[] __value = default; + + try + { + __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalNonBlittable.FromManagedArray(__value); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetStringArray_31(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + string[] __value = default; + + try + { + __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalString.FromManagedArray(__value); + + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetInspectableArray_32(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + object[] __value = default; + + try + { + __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalInspectable.FromManagedArray(__value); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetGuidArray_33(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + Guid[] __value = default; + + try + { + __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); + + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetDateTimeArray_34(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + global::System.DateTimeOffset[] __value = default; + + try + { + __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalNonBlittable.FromManagedArray(__value); + + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetTimeSpanArray_35(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + global::System.TimeSpan[] __value = default; + + try + { + __value = CoerceArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalNonBlittable.FromManagedArray(__value); + + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetPointArray_36(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + global::Windows.Foundation.Point[] __value = default; + + try + { + __value = UnboxArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); + + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetSizeArray_37(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + global::Windows.Foundation.Size[] __value = default; + + try + { + __value = UnboxArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); + + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_GetRectArray_38(IntPtr thisPtr, int* __valueSize, IntPtr* value) + { + + + + global::Windows.Foundation.Rect[] __value = default; + + try + { + __value = UnboxArray(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + (*__valueSize, *value) = MarshalBlittable.FromManagedArray(__value); + + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_get_IsNumericScalar_1(IntPtr thisPtr, byte* value) + { + + try + { + *value = (byte)(NumericScalarTypes.TryGetValue(global::WinRT.ComWrappersSupport.FindObject(thisPtr).GetType(), out _) ? 1 : 0); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + + [UnmanagedCallersOnly] + + private static unsafe int Do_Abi_get_Type_0(IntPtr thisPtr, global::Windows.Foundation.PropertyType* value) + { + + try + { + *value = GetPropertyTypeOfObject(global::WinRT.ComWrappersSupport.FindObject(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe global::Windows.Foundation.PropertyType GetPropertyTypeOfObject(object obj) + { + global::Windows.Foundation.PropertyType value; + global::System.Type managedType = obj.GetType(); + bool isArray = managedType.IsArray; + if (isArray) + { + managedType = managedType.GetElementType(); + } + if (!NumericScalarTypes.TryGetValue(managedType, out value)) + { + if (managedType == typeof(string)) + { + value = global::Windows.Foundation.PropertyType.String; + } + else if (managedType == typeof(char)) + { + value = global::Windows.Foundation.PropertyType.Char16; + } + else if (managedType == typeof(bool)) + { + value = global::Windows.Foundation.PropertyType.Boolean; + } + else if (managedType == typeof(global::System.DateTimeOffset)) + { + value = global::Windows.Foundation.PropertyType.DateTime; + } + else if (managedType == typeof(global::System.TimeSpan)) + { + value = global::Windows.Foundation.PropertyType.TimeSpan; + } + else if (managedType == typeof(global::System.Guid)) + { + value = global::Windows.Foundation.PropertyType.Guid; + } + else if (string.CompareOrdinal(managedType.FullName, "Windows.Foundation.Point") == 0) + { + value = global::Windows.Foundation.PropertyType.Point; + } + else if (string.CompareOrdinal(managedType.FullName, "Windows.Foundation.Rect") == 0) + { + value = global::Windows.Foundation.PropertyType.Rect; + } + else if (string.CompareOrdinal(managedType.FullName, "Windows.Foundation.Size") == 0) + { + value = global::Windows.Foundation.PropertyType.Size; + } + else if (managedType == typeof(object)) + { + value = global::Windows.Foundation.PropertyType.Inspectable; + } + else if (typeof(Delegate).IsAssignableFrom(managedType)) + { + value = global::Windows.Foundation.PropertyType.OtherType; + } + else if (!managedType.IsValueType && managedType != typeof(Type) && isArray) + { + // Treat arrays of interfaces as though they are arrays of object. + value = global::Windows.Foundation.PropertyType.Inspectable; + } + else + { + value = global::Windows.Foundation.PropertyType.OtherType; + } + } + if (isArray) + { + // The array values for Windows.Foundation.PropertyType are all 1024 above their scalar equivalents + value = (global::Windows.Foundation.PropertyType)((int)value + 1024); + } + + return value; + } + } + + [Guid("4BD682DD-7554-40E9-9A9B-82654EDE7E62")] + internal unsafe interface IPropertyValue + { + [Guid("4BD682DD-7554-40E9-9A9B-82654EDE7E62")] +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + public struct Vftbl +#pragma warning restore CA2257 + { + internal IInspectable.Vftbl IInspectableVftbl; + internal void* _get_Type_0; + public delegate* unmanaged[Stdcall] get_Type_0 { get => (delegate* unmanaged[Stdcall])_get_Type_0; set => _get_Type_0 = value; } + public void* _get_IsNumericScalar_1; + public delegate* unmanaged[Stdcall] get_IsNumericScalar_1 { get => (delegate* unmanaged[Stdcall])_get_IsNumericScalar_1; set => _get_IsNumericScalar_1 = value; } + internal void* _GetUInt8_2; + public delegate* unmanaged[Stdcall] GetUInt8_2 { get => (delegate* unmanaged[Stdcall])_GetUInt8_2; set => _GetUInt8_2 = value; } + internal void* _GetInt16_3; + public delegate* unmanaged[Stdcall] GetInt16_3 { get => (delegate* unmanaged[Stdcall])_GetInt16_3; set => _GetInt16_3 = value; } + internal void* _GetUInt16_4; + public delegate* unmanaged[Stdcall] GetUInt16_4 { get => (delegate* unmanaged[Stdcall])_GetUInt16_4; set => _GetUInt16_4 = value; } + internal void* _GetInt32_5; + public delegate* unmanaged[Stdcall] GetInt32_5 { get => (delegate* unmanaged[Stdcall])_GetInt32_5; set => _GetInt32_5 = value; } + internal void* _GetUInt32_6; + public delegate* unmanaged[Stdcall] GetUInt32_6 { get => (delegate* unmanaged[Stdcall])_GetUInt32_6; set => _GetUInt32_6 = value; } + internal void* _GetInt64_7; + public delegate* unmanaged[Stdcall] GetInt64_7 { get => (delegate* unmanaged[Stdcall])_GetInt64_7; set => _GetInt64_7 = value; } + internal void* _GetUInt64_8; + public delegate* unmanaged[Stdcall] GetUInt64_8 { get => (delegate* unmanaged[Stdcall])_GetUInt64_8; set => _GetUInt64_8 = value; } + internal void* _GetSingle_9; + public delegate* unmanaged[Stdcall] GetSingle_9 { get => (delegate* unmanaged[Stdcall])_GetSingle_9; set => _GetSingle_9 = value; } + internal void* _GetDouble_10; + public delegate* unmanaged[Stdcall] GetDouble_10 { get => (delegate* unmanaged[Stdcall])_GetDouble_10; set => _GetDouble_10 = value; } + internal void* _GetChar16_11; + public delegate* unmanaged[Stdcall] GetChar16_11 { get => (delegate* unmanaged[Stdcall])_GetChar16_11; set => _GetChar16_11 = value; } + internal void* _GetBoolean_12; + public delegate* unmanaged[Stdcall] GetBoolean_12 { get => (delegate* unmanaged[Stdcall])_GetBoolean_12; set => _GetBoolean_12 = value; } + internal void* _GetString_13; + public delegate* unmanaged[Stdcall] GetString_13 { get => (delegate* unmanaged[Stdcall])_GetString_13; set => _GetString_13 = value; } + internal void* _GetGuid_14; + public delegate* unmanaged[Stdcall] GetGuid_14 { get => (delegate* unmanaged[Stdcall])_GetGuid_14; set => _GetGuid_14 = value; } + internal void* _GetDateTime_15; + public delegate* unmanaged[Stdcall] GetDateTime_15 { get => (delegate* unmanaged[Stdcall])_GetDateTime_15; set => _GetDateTime_15 = value; } + internal void* _GetTimeSpan_16; + public delegate* unmanaged[Stdcall] GetTimeSpan_16 { get => (delegate* unmanaged[Stdcall])_GetTimeSpan_16; set => _GetTimeSpan_16 = value; } + internal void* _GetPoint_17; + public delegate* unmanaged[Stdcall] GetPoint_17 { get => (delegate* unmanaged[Stdcall])_GetPoint_17; set => _GetPoint_17 = value; } + internal void* _GetSize_18; + public delegate* unmanaged[Stdcall] GetSize_18 { get => (delegate* unmanaged[Stdcall])_GetSize_18; set => _GetSize_18 = value; } + internal void* _GetRect_19; + public delegate* unmanaged[Stdcall] GetRect_19 { get => (delegate* unmanaged[Stdcall])_GetRect_19; set => _GetRect_19 = value; } + internal void* _GetUInt8Array_20; + public delegate* unmanaged[Stdcall] GetUInt8Array_20 { get => (delegate* unmanaged[Stdcall])_GetUInt8Array_20; set => _GetUInt8Array_20 = value; } + internal void* _GetInt16Array_21; + public delegate* unmanaged[Stdcall] GetInt16Array_21 { get => (delegate* unmanaged[Stdcall])_GetInt16Array_21; set => _GetInt16Array_21 = value; } + internal void* _GetUInt16Array_22; + public delegate* unmanaged[Stdcall] GetUInt16Array_22 { get => (delegate* unmanaged[Stdcall])_GetUInt16Array_22; set => _GetUInt16Array_22 = value; } + internal void* _GetInt32Array_23; + public delegate* unmanaged[Stdcall] GetInt32Array_23 { get => (delegate* unmanaged[Stdcall])_GetInt32Array_23; set => _GetInt32Array_23 = value; } + internal void* _GetUInt32Array_24; + public delegate* unmanaged[Stdcall] GetUInt32Array_24 { get => (delegate* unmanaged[Stdcall])_GetUInt32Array_24; set => _GetUInt32Array_24 = value; } + internal void* _GetInt64Array_25; + public delegate* unmanaged[Stdcall] GetInt64Array_25 { get => (delegate* unmanaged[Stdcall])_GetInt64Array_25; set => _GetInt64Array_25 = value; } + internal void* _GetUInt64Array_26; + public delegate* unmanaged[Stdcall] GetUInt64Array_26 { get => (delegate* unmanaged[Stdcall])_GetUInt64Array_26; set => _GetUInt64Array_26 = value; } + internal void* _GetSingleArray_27; + public delegate* unmanaged[Stdcall] GetSingleArray_27 { get => (delegate* unmanaged[Stdcall])_GetSingleArray_27; set => _GetSingleArray_27 = value; } + internal void* _GetDoubleArray_28; + public delegate* unmanaged[Stdcall] GetDoubleArray_28 { get => (delegate* unmanaged[Stdcall])_GetDoubleArray_28; set => _GetDoubleArray_28 = value; } + internal void* _GetChar16Array_29; + public delegate* unmanaged[Stdcall] GetChar16Array_29 { get => (delegate* unmanaged[Stdcall])_GetChar16Array_29; set => _GetChar16Array_29 = value; } + internal void* _GetBooleanArray_30; + public delegate* unmanaged[Stdcall] GetBooleanArray_30 { get => (delegate* unmanaged[Stdcall])_GetBooleanArray_30; set => _GetBooleanArray_30 = value; } + internal void* _GetStringArray_31; + public delegate* unmanaged[Stdcall] GetStringArray_31 { get => (delegate* unmanaged[Stdcall])_GetStringArray_31; set => _GetStringArray_31 = value; } + internal void* _GetInspectableArray_32; + public delegate* unmanaged[Stdcall] GetInspectableArray_32 { get => (delegate* unmanaged[Stdcall])_GetInspectableArray_32; set => _GetInspectableArray_32 = value; } + internal void* _GetGuidArray_33; + public delegate* unmanaged[Stdcall] GetGuidArray_33 { get => (delegate* unmanaged[Stdcall])_GetGuidArray_33; set => _GetGuidArray_33 = value; } + internal void* _GetDateTimeArray_34; + public delegate* unmanaged[Stdcall] GetDateTimeArray_34 { get => (delegate* unmanaged[Stdcall])_GetDateTimeArray_34; set => _GetDateTimeArray_34 = value; } + internal void* _GetTimeSpanArray_35; + public delegate* unmanaged[Stdcall] GetTimeSpanArray_35 { get => (delegate* unmanaged[Stdcall])_GetTimeSpanArray_35; set => _GetTimeSpanArray_35 = value; } + internal void* _GetPointArray_36; + public delegate* unmanaged[Stdcall] GetPointArray_36 { get => (delegate* unmanaged[Stdcall])_GetPointArray_36; set => _GetPointArray_36 = value; } + internal void* _GetSizeArray_37; + public delegate* unmanaged[Stdcall] GetSizeArray_37 { get => (delegate* unmanaged[Stdcall])_GetSizeArray_37; set => _GetSizeArray_37 = value; } + internal void* _GetRectArray_38; + public delegate* unmanaged[Stdcall] GetRectArray_38 { get => (delegate* unmanaged[Stdcall])_GetRectArray_38; set => _GetRectArray_38 = value; } + } + } +} diff --git a/src/WinRT.Runtime/Projections/IPropertyValue.netstandard2.0.cs b/src/WinRT.Runtime/Projections/IPropertyValue.netstandard2.0.cs index 4e1b4ff08..13ed1301e 100644 --- a/src/WinRT.Runtime/Projections/IPropertyValue.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/IPropertyValue.netstandard2.0.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using WinRT; @@ -106,8 +107,8 @@ internal static class ManagedIPropertyValueImpl private const int TYPE_E_TYPEMISMATCH = unchecked((int)0x80028CA0); private const int DISP_E_OVERFLOW = unchecked((int)0x8002000A); private static IPropertyValue.Vftbl AbiToProjectionVftable; - public static IntPtr AbiToProjectionVftablePtr; - + public static IntPtr AbiToProjectionVftablePtr; + internal static readonly Guid IID = new(0x4BD682DD, 0x7554, 0x40E9, 0x9A, 0x9B, 0x82, 0x65, 0x4E, 0xDE, 0x7E, 0x62); private static readonly Delegate[] DelegateCache = new Delegate[39]; @@ -327,12 +328,46 @@ private static T CoerceValue(object value) { return (T)(object)guid.ToString("D", global::System.Globalization.CultureInfo.InvariantCulture); } + else if (typeof(T) == typeof(byte)) + { + return (T)(object)Convert.ToByte(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(short)) + { + return (T)(object)Convert.ToInt16(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(ushort)) + { + return (T)(object)Convert.ToUInt16(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(int)) + { + return (T)(object)Convert.ToInt32(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(uint)) + { + return (T)(object)Convert.ToUInt32(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(long)) + { + return (T)(object)Convert.ToInt64(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(ulong)) + { + return (T)(object)Convert.ToUInt64(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(float)) + { + return (T)(object)Convert.ToSingle(value, global::System.Globalization.CultureInfo.InvariantCulture); + } + else if (typeof(T) == typeof(double)) + { + return (T)(object)Convert.ToDouble(value, global::System.Globalization.CultureInfo.InvariantCulture); + } else { - if (NumericScalarTypes.TryGetValue(typeof(T), out _)) - { - return (T)Convert.ChangeType(value, typeof(T), global::System.Globalization.CultureInfo.InvariantCulture); - } + Debug.Assert(!NumericScalarTypes.ContainsKey(typeof(T))); + throw new InvalidCastException("", TYPE_E_TYPEMISMATCH); } } catch (FormatException) @@ -1283,6 +1318,7 @@ public unsafe byte GetUInt8() { byte __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetUInt8_2(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } @@ -1290,6 +1326,7 @@ public unsafe short GetInt16() { short __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInt16_3(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } @@ -1297,6 +1334,7 @@ public unsafe ushort GetUInt16() { ushort __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetUInt16_4(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } @@ -1304,6 +1342,7 @@ public unsafe int GetInt32() { int __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInt32_5(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } @@ -1311,6 +1350,7 @@ public unsafe uint GetUInt32() { uint __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetUInt32_6(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } @@ -1318,6 +1358,7 @@ public unsafe long GetInt64() { long __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInt64_7(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } @@ -1325,6 +1366,7 @@ public unsafe ulong GetUInt64() { ulong __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetUInt64_8(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } @@ -1332,6 +1374,7 @@ public unsafe float GetSingle() { float __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetSingle_9(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } @@ -1339,6 +1382,7 @@ public unsafe double GetDouble() { double __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetDouble_10(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } @@ -1346,6 +1390,7 @@ public unsafe char GetChar16() { ushort __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetChar16_11(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return (char)__retval; } @@ -1353,6 +1398,7 @@ public unsafe bool GetBoolean() { byte __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetBoolean_12(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval != 0; } @@ -1362,6 +1408,7 @@ public unsafe string GetString() try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetString_13(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return MarshalString.FromAbi(__retval); } finally @@ -1374,6 +1421,7 @@ public unsafe Guid GetGuid() { Guid __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetGuid_14(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } @@ -1383,6 +1431,7 @@ public unsafe Guid GetGuid() try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetDateTime_15(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return global::ABI.System.DateTimeOffset.FromAbi(__retval); } finally @@ -1397,6 +1446,7 @@ public unsafe Guid GetGuid() try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetTimeSpan_16(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return global::ABI.System.TimeSpan.FromAbi(__retval); } finally @@ -1409,6 +1459,7 @@ public unsafe Guid GetGuid() { global::Windows.Foundation.Point __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetPoint_17(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } @@ -1416,6 +1467,7 @@ public unsafe Guid GetGuid() { global::Windows.Foundation.Size __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetSize_18(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } @@ -1423,6 +1475,7 @@ public unsafe Guid GetGuid() { global::Windows.Foundation.Rect __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetRect_19(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } @@ -1433,6 +1486,7 @@ public unsafe void GetUInt8Array(out byte[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetUInt8Array_20(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1448,6 +1502,7 @@ public unsafe void GetInt16Array(out short[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInt16Array_21(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1463,6 +1518,7 @@ public unsafe void GetUInt16Array(out ushort[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetUInt16Array_22(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1478,6 +1534,7 @@ public unsafe void GetInt32Array(out int[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInt32Array_23(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1493,6 +1550,7 @@ public unsafe void GetUInt32Array(out uint[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetUInt32Array_24(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1508,6 +1566,7 @@ public unsafe void GetInt64Array(out long[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInt64Array_25(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1523,6 +1582,7 @@ public unsafe void GetUInt64Array(out ulong[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetUInt64Array_26(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1538,6 +1598,7 @@ public unsafe void GetSingleArray(out float[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetSingleArray_27(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1553,6 +1614,7 @@ public unsafe void GetDoubleArray(out double[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetDoubleArray_28(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1568,6 +1630,7 @@ public unsafe void GetChar16Array(out char[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetChar16Array_29(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalNonBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1583,6 +1646,7 @@ public unsafe void GetBooleanArray(out bool[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetBooleanArray_30(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalNonBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1598,6 +1662,7 @@ public unsafe void GetStringArray(out string[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetStringArray_31(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalString.FromAbiArray((__value_length, __value_data)); } finally @@ -1613,6 +1678,7 @@ public unsafe void GetInspectableArray(out object[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetInspectableArray_32(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalInspectable.FromAbiArray((__value_length, __value_data)); } finally @@ -1628,6 +1694,7 @@ public unsafe void GetGuidArray(out Guid[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetGuidArray_33(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1643,6 +1710,7 @@ public unsafe void GetDateTimeArray(out global::System.DateTimeOffset[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetDateTimeArray_34(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalNonBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1658,6 +1726,7 @@ public unsafe void GetTimeSpanArray(out global::System.TimeSpan[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetTimeSpanArray_35(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalNonBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1673,6 +1742,7 @@ public unsafe void GetPointArray(out global::Windows.Foundation.Point[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetPointArray_36(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1688,6 +1758,7 @@ public unsafe void GetSizeArray(out global::Windows.Foundation.Size[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetSizeArray_37(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1703,6 +1774,7 @@ public unsafe void GetRectArray(out global::Windows.Foundation.Rect[] value) try { global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetRectArray_38(ThisPtr, out __value_length, out __value_data)); + GC.KeepAlive(_obj); value = MarshalBlittable.FromAbiArray((__value_length, __value_data)); } finally @@ -1717,6 +1789,7 @@ public unsafe bool IsNumericScalar { byte __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_IsNumericScalar_1(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval != 0; } } @@ -1727,6 +1800,7 @@ public unsafe bool IsNumericScalar { global::Windows.Foundation.PropertyType __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Type_0(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } } diff --git a/src/WinRT.Runtime/Projections/IReadOnlyDictionary.net5.cs b/src/WinRT.Runtime/Projections/IReadOnlyDictionary.net5.cs index f60b48ca1..f3951c361 100644 --- a/src/WinRT.Runtime/Projections/IReadOnlyDictionary.net5.cs +++ b/src/WinRT.Runtime/Projections/IReadOnlyDictionary.net5.cs @@ -4,12 +4,10 @@ using System; using System.Collections; using System.Collections.Generic; -using System.Linq.Expressions; using System.Reflection; using System.Runtime.InteropServices; using WinRT; using WinRT.Interop; -using System.Diagnostics; using System.Diagnostics.CodeAnalysis; #pragma warning disable 0169 // warning CS0169: The field '...' is never used @@ -32,19 +30,19 @@ namespace System.Collections.Generic { internal sealed class IReadOnlyDictionaryImpl : IReadOnlyDictionary, IWinRTObject { - private IObjectReference _inner; + private readonly IObjectReference _inner; internal IReadOnlyDictionaryImpl(IObjectReference _inner) { this._inner = _inner; - } - + } + public static IReadOnlyDictionaryImpl CreateRcw(IInspectable obj) => new(obj.ObjRef); private volatile IObjectReference __iReadOnlyDictionaryObjRef; private IObjectReference Make_IDictionaryObjRef() { - global::System.Threading.Interlocked.CompareExchange(ref __iReadOnlyDictionaryObjRef, _inner.As.Vftbl>(), null); + global::System.Threading.Interlocked.CompareExchange(ref __iReadOnlyDictionaryObjRef, _inner.As(ABI.System.Collections.Generic.IReadOnlyDictionaryMethods.PIID), null); return __iReadOnlyDictionaryObjRef; } private IObjectReference iReadOnlyDictionaryObjRef => __iReadOnlyDictionaryObjRef ?? Make_IDictionaryObjRef(); @@ -52,7 +50,7 @@ private IObjectReference Make_IDictionaryObjRef() private volatile IObjectReference __iEnumerableObjRef; private IObjectReference Make_IEnumerableObjRef() { - global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, _inner.As>.Vftbl>(), null); + global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, _inner.As(ABI.System.Collections.Generic.IEnumerableMethods>.PIID), null); return __iEnumerableObjRef; } private IObjectReference iEnumerableObjRef => __iEnumerableObjRef ?? Make_IEnumerableObjRef(); @@ -104,74 +102,95 @@ namespace ABI.Windows.Foundation.Collections internal static class IMapViewMethods { + // These function pointers will be set by IReadOnlyDictionaryMethods + // when it is called by the source generated type or by the fallback + // mechanism if the source generated type wasn't used. + internal volatile unsafe static delegate* _Lookup; + internal volatile unsafe static delegate* _HasKey; + internal volatile unsafe static delegate*< + IObjectReference, + out global::Windows.Foundation.Collections.IMapView, + out global::Windows.Foundation.Collections.IMapView, + void> _Split; + internal volatile static bool _RcwHelperInitialized; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static unsafe void EnsureInitialized() + { + if (RuntimeFeature.IsDynamicCodeCompiled) + { + return; + } + + if (!_RcwHelperInitialized) + { + [DoesNotReturn] + static void ThrowNotInitialized() + { + throw new NotImplementedException( + $"Type '{typeof(global::System.Collections.Generic.IReadOnlyDictionary)}' was called without initializing the RCW methods using 'IReadOnlyDictionaryMethods.InitRcwHelper'. " + + $"If using 'IDynamicInterfaceCastable' support to do a dynamic cast to this interface, ensure the 'InitRcwHelper' method is called."); + } + + ThrowNotInitialized(); + } + } + public static unsafe V Lookup(IObjectReference obj, K key) { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - - object __key = default; - var __params = new object[] { ThisPtr, null, null }; - try - { - __key = Marshaler.CreateMarshaler2(key); - __params[1] = Marshaler.GetAbi(__key); - _obj.Vftbl.Lookup_0.DynamicInvokeAbi(__params); - return Marshaler.FromAbi(__params[2]); - } - finally - { - Marshaler.DisposeMarshaler(__key); - Marshaler.DisposeAbi(__params[2]); - } + EnsureInitialized(); + return _Lookup(obj, key); } public static unsafe bool HasKey(IObjectReference obj, K key) - { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - - object __key = default; - var __params = new object[] { ThisPtr, null, null }; - try - { - __key = Marshaler.CreateMarshaler2(key); - __params[1] = Marshaler.GetAbi(__key); - _obj.Vftbl.HasKey_2.DynamicInvokeAbi(__params); - return (byte)__params[2] != 0; - } - finally - { - Marshaler.DisposeMarshaler(__key); - } + { + EnsureInitialized(); + return _HasKey(obj, key); } public static unsafe void Split(IObjectReference obj, out global::Windows.Foundation.Collections.IMapView first, out global::Windows.Foundation.Collections.IMapView second) { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; + // Early return to ensure things are trimmed correctly on NAOT. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + EnsureInitialized(); + _Split(obj, out first, out second); + return; + } - IntPtr __first = default; - IntPtr __second = default; - try + if (_Split != null) { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Split_3(ThisPtr, out __first, out __second)); - first = MarshalInterface>.FromAbi(__first); - second = MarshalInterface>.FromAbi(__second); + _Split(obj, out first, out second); } - finally + else { - MarshalInterface>.DisposeAbi(__first); - MarshalInterface>.DisposeAbi(__second); + var ThisPtr = obj.ThisPtr; + + IntPtr __first = default; + IntPtr __second = default; + try + { + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[9](ThisPtr, &__first, &__second)); + GC.KeepAlive(obj); + first = MarshalInterface>.FromAbi(__first); + second = MarshalInterface>.FromAbi(__second); + } + finally + { + MarshalInterface>.DisposeAbi(__first); + MarshalInterface>.DisposeAbi(__second); + } } } public static unsafe uint get_Size(IObjectReference obj) { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; + var ThisPtr = obj.ThisPtr; uint __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetSize_1(ThisPtr, out __retval)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[7](ThisPtr, &__retval)); + GC.KeepAlive(obj); return __retval; } } @@ -189,6 +208,44 @@ namespace ABI.System.Collections.Generic #endif static class IReadOnlyDictionaryMethods { + unsafe static IReadOnlyDictionaryMethods() + { + ComWrappersSupport.RegisterHelperType(typeof(global::System.Collections.Generic.IReadOnlyDictionary), typeof(global::ABI.System.Collections.Generic.IReadOnlyDictionary)); + ComWrappersSupport.RegisterHelperType(typeof(global::Windows.Foundation.Collections.IMapView), typeof(global::ABI.System.Collections.Generic.IReadOnlyDictionary)); + + // Early return to ensure things are trimmed correctly on NAOT. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return; + } + +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + InitRcwHelperFallbackIfNeeded(); +#pragma warning restore IL3050 + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2080", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] + [UnconditionalSuppressMessage("Trimming", "IL2081", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] +#endif + [MethodImpl(MethodImplOptions.NoInlining)] + static void InitRcwHelperFallbackIfNeeded() + { + // Handle the compat scenario where the source generator wasn't used and IDIC hasn't been used yet + // and due to that the function pointers haven't been initialized. + if (!ABI.Windows.Foundation.Collections.IMapViewMethods._RcwHelperInitialized) + { + var initRcwHelperFallback = (Func)typeof(IReadOnlyDictionaryMethods<,,,>).MakeGenericType(typeof(K), Marshaler.AbiType, typeof(V), Marshaler.AbiType). + GetMethod("InitRcwHelperFallback", BindingFlags.NonPublic | BindingFlags.Static). + CreateDelegate(typeof(Func)); + initRcwHelperFallback(); + } + } + } + public static int get_Count(IObjectReference obj) { uint size = ABI.Windows.Foundation.Collections.IMapViewMethods.get_Size(obj); @@ -283,7 +340,8 @@ public ReadOnlyDictionaryKeyCollection(IObjectReference iReadOnlyDictionaryObjRe private volatile IObjectReference __iEnumerableObjRef; private IObjectReference Make_IEnumerableObjRef() { - global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, iReadOnlyDictionaryObjRef.As>.Vftbl>(), null); + global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, iReadOnlyDictionaryObjRef.As( + ABI.System.Collections.Generic.IEnumerableMethods>.PIID), null); return __iEnumerableObjRef; } private IObjectReference iEnumerableObjRef => __iEnumerableObjRef ?? Make_IEnumerableObjRef(); @@ -336,7 +394,7 @@ public ReadOnlyDictionaryValueCollection(IObjectReference iDictionaryObjRef) private volatile IObjectReference __iEnumerableObjRef; private IObjectReference Make_IEnumerableObjRef() { - global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, iDictionaryObjRef.As>.Vftbl>(), null); + global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, iDictionaryObjRef.As(ABI.System.Collections.Generic.IEnumerableMethods>.PIID), null); return __iEnumerableObjRef; } private IObjectReference iEnumerableObjRef => __iEnumerableObjRef ?? Make_IEnumerableObjRef(); @@ -378,521 +436,756 @@ public void Reset() } } } - } - + private static IntPtr abiToProjectionVftablePtr; + public static IntPtr AbiToProjectionVftablePtr => abiToProjectionVftablePtr; - [DynamicInterfaceCastableImplementation] - [Guid("E480CE40-A338-4ADA-ADCF-272272E48CB9")] - interface IReadOnlyDictionary : global::System.Collections.Generic.IReadOnlyDictionary, global::Windows.Foundation.Collections.IMapView - { - public static IObjectReference CreateMarshaler(global::System.Collections.Generic.IReadOnlyDictionary obj) => - obj is null ? null : ComWrappersSupport.CreateCCWForObject(obj, PIID); - - public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.Generic.IReadOnlyDictionary obj) => - ComWrappersSupport.CreateCCWForObjectForMarshaling(obj, PIID); + internal static bool TryInitCCWVtable(IntPtr ptr) + { + return global::System.Threading.Interlocked.CompareExchange(ref abiToProjectionVftablePtr, ptr, IntPtr.Zero) == IntPtr.Zero; + } - public static IntPtr GetAbi(IObjectReference objRef) => - objRef?.ThisPtr ?? IntPtr.Zero; + internal static readonly Guid PIID = GuidGenerator.CreateIIDUnsafe(typeof(IReadOnlyDictionary)); + public static Guid IID => PIID; - public static IntPtr FromManaged(global::System.Collections.Generic.IReadOnlyDictionary value) => - (value is null) ? IntPtr.Zero : CreateMarshaler2(value).Detach(); + public static V Abi_Lookup_0(IntPtr thisPtr, K key) + { + return IReadOnlyDictionary.FindAdapter(thisPtr).Lookup(key); + } - public static void DisposeMarshaler(IObjectReference objRef) => objRef?.Dispose(); + public static bool Abi_HasKey_2(IntPtr thisPtr, K key) + { + return IReadOnlyDictionary.FindAdapter(thisPtr).HasKey(key); + } - public static void DisposeAbi(IntPtr abi) => - MarshalInterfaceHelper>.DisposeAbi(abi); + // Modified signature due to Windows.Foundation.Collections.IMapView is not public + public static void Abi_Split_3(IntPtr thisPtr, out IntPtr first, out IntPtr second) + { + global::Windows.Foundation.Collections.IMapView __first; + global::Windows.Foundation.Collections.IMapView __second; - public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IReadOnlyDictionary)); + IReadOnlyDictionary.FindAdapter(thisPtr).Split(out __first, out __second); - private sealed class ReadOnlyDictionaryKeyCollection : global::System.Collections.Generic.IEnumerable + first = MarshalInterface>.FromManaged(__first); + second = MarshalInterface>.FromManaged(__second); + } + + public static uint Abi_get_Size_1(IntPtr thisPtr) { - private readonly global::System.Collections.Generic.IReadOnlyDictionary dictionary; + return IReadOnlyDictionary.FindAdapter(thisPtr).Size; + } + } - public ReadOnlyDictionaryKeyCollection(global::System.Collections.Generic.IReadOnlyDictionary dictionary) +#if EMBED + internal +#else + public +#endif + static class IReadOnlyDictionaryMethods where KAbi : unmanaged where VAbi : unmanaged + { + public unsafe static bool InitRcwHelper( + delegate* lookup, + delegate* hasKey, + delegate*, + out global::System.Collections.Generic.IReadOnlyDictionary, + void> _) + { + if (ABI.Windows.Foundation.Collections.IMapViewMethods._RcwHelperInitialized) { - if (dictionary == null) - throw new ArgumentNullException(nameof(dictionary)); + return true; + } + + ABI.Windows.Foundation.Collections.IMapViewMethods._Lookup = lookup; + ABI.Windows.Foundation.Collections.IMapViewMethods._HasKey = hasKey; + + ComWrappersSupport.RegisterTypedRcwFactory( + typeof(global::System.Collections.Generic.IReadOnlyDictionary), + IReadOnlyDictionaryImpl.CreateRcw); + ComWrappersSupport.RegisterHelperType(typeof(global::System.Collections.Generic.IReadOnlyDictionary), typeof(global::ABI.System.Collections.Generic.IReadOnlyDictionary)); + ComWrappersSupport.RegisterHelperType(typeof(global::Windows.Foundation.Collections.IMapView), typeof(global::ABI.System.Collections.Generic.IReadOnlyDictionary)); + + ABI.Windows.Foundation.Collections.IMapViewMethods._RcwHelperInitialized = true; + return true; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private unsafe static bool InitRcwHelperFallback() + { + return InitRcwHelper(&LookupDynamic, &HasKeyDynamic, null); + } - this.dictionary = dictionary; +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe V LookupDynamic(IObjectReference obj, K key) + { + var ThisPtr = obj.ThisPtr; + object __key = default; + VAbi valueAbi = default; + var __params = new object[] { ThisPtr, null, (IntPtr)(void*)&valueAbi }; + try + { + __key = Marshaler.CreateMarshaler2(key); + __params[1] = Marshaler.GetAbi(__key); + DelegateHelper.Get(obj).Lookup.DynamicInvokeAbi(__params); + GC.KeepAlive(obj); + GC.KeepAlive(__params); + return Marshaler.FromAbi(valueAbi); } + finally + { + Marshaler.DisposeMarshaler(__key); + Marshaler.DisposeAbi(valueAbi); + } + } - public global::System.Collections.Generic.IEnumerator GetEnumerator() +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe bool HasKeyDynamic(IObjectReference obj, K key) + { + var ThisPtr = obj.ThisPtr; + object __key = default; + byte found; + var __params = new object[] { ThisPtr, null, (IntPtr)(void*)&found }; + try { - return new ReadOnlyDictionaryKeyEnumerator(dictionary); + __key = Marshaler.CreateMarshaler2(key); + __params[1] = Marshaler.GetAbi(__key); + DelegateHelper.Get(obj).HasKey.DynamicInvokeAbi(__params); + GC.KeepAlive(obj); + GC.KeepAlive(__params); + return found != 0; } - IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + finally + { + Marshaler.DisposeMarshaler(__key); + } + } - private sealed class ReadOnlyDictionaryKeyEnumerator : global::System.Collections.Generic.IEnumerator + public static unsafe bool InitCcw( + delegate* unmanaged[Stdcall] lookup, + delegate* unmanaged[Stdcall] getSize, + delegate* unmanaged[Stdcall] hasKey, + delegate* unmanaged[Stdcall] split) + { + if (IReadOnlyDictionaryMethods.AbiToProjectionVftablePtr != default) { - private readonly global::System.Collections.Generic.IReadOnlyDictionary dictionary; - private global::System.Collections.Generic.IEnumerator> enumeration; + return false; + } - public ReadOnlyDictionaryKeyEnumerator(global::System.Collections.Generic.IReadOnlyDictionary dictionary) - { - if (dictionary == null) - throw new ArgumentNullException(nameof(dictionary)); + var abiToProjectionVftablePtr = (IntPtr)NativeMemory.AllocZeroed((nuint)(sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * 4)); + *(IInspectable.Vftbl*)abiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[6] = lookup; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[7] = getSize; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[8] = hasKey; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[9] = split; - this.dictionary = dictionary; - enumeration = dictionary.GetEnumerator(); - } + if (!IReadOnlyDictionaryMethods.TryInitCCWVtable(abiToProjectionVftablePtr)) + { + NativeMemory.Free((void*)abiToProjectionVftablePtr); + return false; + } - void IDisposable.Dispose() - { - enumeration.Dispose(); - } + return true; + } - public bool MoveNext() - { - return enumeration.MoveNext(); - } + private static global::System.Delegate[] DelegateCache; - object IEnumerator.Current => Current; +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + internal static unsafe void InitFallbackCCWVtable() + { + DelegateCache = new global::System.Delegate[] + { + global::System.Delegate.CreateDelegate(Lookup_0_Type, typeof(IReadOnlyDictionaryMethods).GetMethod(nameof(Do_Abi_Lookup_0), BindingFlags.NonPublic | BindingFlags.Static)), + new _get_PropertyAsUInt32_Abi(Do_Abi_get_Size_1), + global::System.Delegate.CreateDelegate(HasKey_2_Type, typeof(IReadOnlyDictionaryMethods).GetMethod(nameof(Do_Abi_HasKey_2), BindingFlags.NonPublic | BindingFlags.Static)), + new IReadOnlyDictionary_Delegates.Split_3_Abi(Do_Abi_Split_3), + }; + + InitCcw( + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[0]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[1]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[2]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[3]) + ); + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_Lookup_0(IntPtr thisPtr, KAbi key, VAbi* __return_value__) + { + V ____return_value__ = default; - public K Current => enumeration.Current.Key; + *__return_value__ = default; - public void Reset() - { - enumeration = dictionary.GetEnumerator(); - } + try + { + ____return_value__ = IReadOnlyDictionary.FindAdapter(thisPtr).Lookup(Marshaler.FromAbi(key)); + *__return_value__ = (VAbi)Marshaler.FromManaged(____return_value__); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); } + return 0; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_HasKey_2(IntPtr thisPtr, KAbi key, byte* __return_value__) + { + bool ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = IReadOnlyDictionary.FindAdapter(thisPtr).HasKey(Marshaler.FromAbi(key)); + *__return_value__ = (byte)(____return_value__ ? 1 : 0); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; } - private sealed class ReadOnlyDictionaryValueCollection : global::System.Collections.Generic.IEnumerable + private static unsafe int Do_Abi_Split_3(IntPtr thisPtr, IntPtr* first, IntPtr* second) { - private readonly global::System.Collections.Generic.IReadOnlyDictionary dictionary; + *first = default; + *second = default; + global::Windows.Foundation.Collections.IMapView __first = default; + global::Windows.Foundation.Collections.IMapView __second = default; - public ReadOnlyDictionaryValueCollection(global::System.Collections.Generic.IReadOnlyDictionary dictionary) + try { - if (dictionary == null) - throw new ArgumentNullException(nameof(dictionary)); - - this.dictionary = dictionary; + IReadOnlyDictionary.FindAdapter(thisPtr).Split(out __first, out __second); + *first = MarshalInterface>.FromManaged(__first); + *second = MarshalInterface>.FromManaged(__second); } - - public global::System.Collections.Generic.IEnumerator GetEnumerator() + catch (Exception __exception__) { - return new ReadOnlyDictionaryValueEnumerator(dictionary); + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); } - global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + return 0; + } - private sealed class ReadOnlyDictionaryValueEnumerator : global::System.Collections.Generic.IEnumerator + private static unsafe int Do_Abi_get_Size_1(IntPtr thisPtr, uint* __return_value__) + { + uint ____return_value__ = default; + + *__return_value__ = default; + + try { - private readonly global::System.Collections.Generic.IReadOnlyDictionary dictionary; - private global::System.Collections.Generic.IEnumerator> enumeration; + ____return_value__ = IReadOnlyDictionary.FindAdapter(thisPtr).Size; + *__return_value__ = ____return_value__; + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } - public ReadOnlyDictionaryValueEnumerator(global::System.Collections.Generic.IReadOnlyDictionary dictionary) - { - if (dictionary == null) - throw new ArgumentNullException(nameof(dictionary)); + private static Type _lookup_0_type; + private static Type Lookup_0_Type + { +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + get => _lookup_0_type ?? MakeLookupType(); + } - this.dictionary = dictionary; - enumeration = dictionary.GetEnumerator(); - } +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + private static Type MakeLookupType() + { + global::System.Threading.Interlocked.CompareExchange(ref _lookup_0_type, Projections.GetAbiDelegateType(new Type[] { typeof(IntPtr), typeof(KAbi), typeof(VAbi*), typeof(int) }), null); + return _lookup_0_type; + } - void IDisposable.Dispose() - { - enumeration.Dispose(); - } + private static Type _hasKey_2_type; + private static Type HasKey_2_Type + { +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + get => _hasKey_2_type ?? MakeHasKeyType(); + } - public bool MoveNext() - { - return enumeration.MoveNext(); - } +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + private static Type MakeHasKeyType() + { + global::System.Threading.Interlocked.CompareExchange(ref _hasKey_2_type, Projections.GetAbiDelegateType(new Type[] { typeof(IntPtr), typeof(KAbi), typeof(byte*), typeof(int) }), null); + return _hasKey_2_type; + } - object IEnumerator.Current => Current; +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + private sealed class DelegateHelper + { + private readonly IntPtr _ptr; - public V Current => enumeration.Current.Value; + private Delegate _lookupDelegate; + public Delegate Lookup => _lookupDelegate ?? GenericDelegateHelper.CreateDelegate(_ptr, ref _lookupDelegate, Lookup_0_Type, 6); - public void Reset() - { - enumeration = dictionary.GetEnumerator(); - } + private Delegate _hasKeyDelegate; + public Delegate HasKey => _hasKeyDelegate ?? GenericDelegateHelper.CreateDelegate(_ptr, ref _hasKeyDelegate, HasKey_2_Type, 8); + + private DelegateHelper(IntPtr ptr) + { + _ptr = ptr; + } + + public static DelegateHelper Get(IObjectReference obj) + { + return (DelegateHelper)GenericDelegateHelper.DelegateTable.GetValue(obj, static (objRef) => new DelegateHelper(objRef.ThisPtr)); } } + } - public sealed class ToAbiHelper : global::Windows.Foundation.Collections.IMapView + internal sealed class ConstantSplittableMap : global::Windows.Foundation.Collections.IMapView, global::System.Collections.Generic.IReadOnlyDictionary + { + private sealed class KeyValuePairComparator : IComparer> { - private readonly global::System.Collections.Generic.IReadOnlyDictionary _dictionary; + private static readonly IComparer keyComparator = Comparer.Default; - internal ToAbiHelper(global::System.Collections.Generic.IReadOnlyDictionary dictionary) => _dictionary = dictionary; + public int Compare(global::System.Collections.Generic.KeyValuePair x, global::System.Collections.Generic.KeyValuePair y) + { + return keyComparator.Compare(x.Key, y.Key); + } + } - uint global::Windows.Foundation.Collections.IMapView.Size { get => (uint)_dictionary.Count; } + private static readonly KeyValuePairComparator keyValuePairComparator = new KeyValuePairComparator(); - global::System.Collections.Generic.IEnumerator> global::Windows.Foundation.Collections.IIterable>.First() => - new KeyValuePair.Enumerator(_dictionary.GetEnumerator()); + private readonly global::System.Collections.Generic.KeyValuePair[] items; + private readonly int firstItemIndex; + private readonly int lastItemIndex; - public V Lookup(K key) - { - V value; - bool keyFound = _dictionary.TryGetValue(key, out value); + internal ConstantSplittableMap(global::System.Collections.Generic.IReadOnlyDictionary data) + { + if (data == null) + throw new ArgumentNullException(nameof(data)); - if (!keyFound) - { - Exception e = new KeyNotFoundException(String.Format(WinRTRuntimeErrorStrings.Arg_KeyNotFoundWithKey, key.ToString())); - e.SetHResult(ExceptionHelpers.E_BOUNDS); - throw e; - } + firstItemIndex = 0; + lastItemIndex = data.Count - 1; + items = CreateKeyValueArray(data.Count, data.GetEnumerator()); + } - return value; - } + private ConstantSplittableMap(global::System.Collections.Generic.KeyValuePair[] items, int firstItemIndex, int lastItemIndex) + { + this.items = items; + this.firstItemIndex = firstItemIndex; + this.lastItemIndex = lastItemIndex; + } - public uint Size() => (uint)_dictionary.Count; + private global::System.Collections.Generic.KeyValuePair[] CreateKeyValueArray(int count, global::System.Collections.Generic.IEnumerator> data) + { + global::System.Collections.Generic.KeyValuePair[] kvArray = new global::System.Collections.Generic.KeyValuePair[count]; - public bool HasKey(K key) => _dictionary.ContainsKey(key); + int i = 0; + while (data.MoveNext()) + kvArray[i++] = data.Current; - void global::Windows.Foundation.Collections.IMapView.Split(out global::Windows.Foundation.Collections.IMapView first, out global::Windows.Foundation.Collections.IMapView second) - { - if (_dictionary.Count < 2) - { - first = null; - second = null; - return; - } + Array.Sort(kvArray, keyValuePairComparator); - if (!(_dictionary is ConstantSplittableMap splittableMap)) - splittableMap = new ConstantSplittableMap(_dictionary); + return kvArray; + } - splittableMap.Split(out first, out second); - } + public uint Size => (uint)(lastItemIndex - firstItemIndex + 1); - private sealed class ConstantSplittableMap : global::Windows.Foundation.Collections.IMapView, global::System.Collections.Generic.IReadOnlyDictionary - { - private sealed class KeyValuePairComparator : IComparer> - { - private static readonly IComparer keyComparator = Comparer.Default; + public global::System.Collections.Generic.IEnumerable Keys + { + get => new ReadOnlyDictionaryKeyCollection(this); + } - public int Compare(global::System.Collections.Generic.KeyValuePair x, global::System.Collections.Generic.KeyValuePair y) - { - return keyComparator.Compare(x.Key, y.Key); - } - } + public global::System.Collections.Generic.IEnumerable Values + { + get => new ReadOnlyDictionaryValueCollection(this); + } - private static readonly KeyValuePairComparator keyValuePairComparator = new KeyValuePairComparator(); + public int Count => lastItemIndex - firstItemIndex + 1; - private readonly global::System.Collections.Generic.KeyValuePair[] items; - private readonly int firstItemIndex; - private readonly int lastItemIndex; + public V this[K key] => Lookup(key); - internal ConstantSplittableMap(global::System.Collections.Generic.IReadOnlyDictionary data) - { - if (data == null) - throw new ArgumentNullException(nameof(data)); + public V Lookup(K key) + { + V value; + bool found = TryGetValue(key, out value); - firstItemIndex = 0; - lastItemIndex = data.Count - 1; - items = CreateKeyValueArray(data.Count, data.GetEnumerator()); - } + if (!found) + { + Exception e = new KeyNotFoundException(String.Format(WinRTRuntimeErrorStrings.Arg_KeyNotFoundWithKey, key.ToString())); + e.SetHResult(ExceptionHelpers.E_BOUNDS); + throw e; + } - private ConstantSplittableMap(global::System.Collections.Generic.KeyValuePair[] items, int firstItemIndex, int lastItemIndex) - { - this.items = items; - this.firstItemIndex = firstItemIndex; - this.lastItemIndex = lastItemIndex; - } + return value; + } - private global::System.Collections.Generic.KeyValuePair[] CreateKeyValueArray(int count, global::System.Collections.Generic.IEnumerator> data) - { - global::System.Collections.Generic.KeyValuePair[] kvArray = new global::System.Collections.Generic.KeyValuePair[count]; + public bool HasKey(K key) => + TryGetValue(key, out _); - int i = 0; - while (data.MoveNext()) - kvArray[i++] = data.Current; + public global::System.Collections.Generic.IEnumerator> First() => GetEnumerator(); - Array.Sort(kvArray, keyValuePairComparator); + public void Split(out global::Windows.Foundation.Collections.IMapView firstPartition, out global::Windows.Foundation.Collections.IMapView secondPartition) + { + if (Count < 2) + { + firstPartition = null; + secondPartition = null; + return; + } - return kvArray; - } + int pivot = (int)(((long)firstItemIndex + (long)lastItemIndex) / (long)2); - public uint Size => (uint)(lastItemIndex - firstItemIndex + 1); + firstPartition = new ConstantSplittableMap(items, firstItemIndex, pivot); + secondPartition = new ConstantSplittableMap(items, pivot + 1, lastItemIndex); + } - public global::System.Collections.Generic.IEnumerable Keys - { - get => new ReadOnlyDictionaryKeyCollection(this); - } + public bool TryGetValue(K key, out V value) + { + var searchKey = new global::System.Collections.Generic.KeyValuePair(key, default!); + int index = Array.BinarySearch(items, firstItemIndex, Count, searchKey, keyValuePairComparator); - public global::System.Collections.Generic.IEnumerable Values - { - get => new ReadOnlyDictionaryValueCollection(this); - } + if (index < 0) + { + value = default!; + return false; + } - public int Count => lastItemIndex - firstItemIndex + 1; + value = items[index].Value; + return true; + } - public V this[K key] => Lookup(key); + public bool ContainsKey(K key) + { + return HasKey(key); + } - public V Lookup(K key) - { - V value; - bool found = TryGetValue(key, out value); + global::System.Collections.Generic.IEnumerator> global::Windows.Foundation.Collections.IIterable>.First() + { + var itemsAsIKeyValuePairs = new global::Windows.Foundation.Collections.IKeyValuePair[items.Length]; + for (var i = 0; i < items.Length; i++) + { + itemsAsIKeyValuePairs[i] = new KeyValuePair.ToIKeyValuePair(ref items[i]); + } + return new ConstantSplittableMapEnumerator>(itemsAsIKeyValuePairs, firstItemIndex, lastItemIndex); + } - if (!found) - { - Exception e = new KeyNotFoundException(String.Format(WinRTRuntimeErrorStrings.Arg_KeyNotFoundWithKey, key.ToString())); - e.SetHResult(ExceptionHelpers.E_BOUNDS); - throw e; - } + public global::System.Collections.Generic.IEnumerator> GetEnumerator() + { + return new ConstantSplittableMapEnumerator>(items, firstItemIndex, lastItemIndex); + } - return value; - } + IEnumerator global::System.Collections.IEnumerable.GetEnumerator() + { + return new ConstantSplittableMapEnumerator>(items, firstItemIndex, lastItemIndex); + } + } - public bool HasKey(K key) => - TryGetValue(key, out _); + internal struct ConstantSplittableMapEnumerator : global::System.Collections.Generic.IEnumerator + { + private readonly T[] _array; + private readonly int _start; + private readonly int _end; + private int _current; - public global::System.Collections.Generic.IEnumerator> First() => GetEnumerator(); + internal ConstantSplittableMapEnumerator(T[] items, int first, int end) + { + _array = items; + _start = first; + _end = end; + _current = _start - 1; + } - public void Split(out global::Windows.Foundation.Collections.IMapView firstPartition, out global::Windows.Foundation.Collections.IMapView secondPartition) - { - if (Count < 2) - { - firstPartition = null; - secondPartition = null; - return; - } + public bool MoveNext() + { + if (_current < _end) + { + _current++; + return true; + } + return false; + } - int pivot = (int)(((long)firstItemIndex + (long)lastItemIndex) / (long)2); + public T Current + { + get + { + if (_current < _start) throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_EnumNotStarted); + if (_current > _end) throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_EnumEnded); + return _array[_current]; + } + } - firstPartition = new ConstantSplittableMap(items, firstItemIndex, pivot); - secondPartition = new ConstantSplittableMap(items, pivot + 1, lastItemIndex); - } + object IEnumerator.Current => Current; - public bool TryGetValue(K key, out V value) - { - var searchKey = new global::System.Collections.Generic.KeyValuePair(key, default!); - int index = Array.BinarySearch(items, firstItemIndex, Count, searchKey, keyValuePairComparator); + void IEnumerator.Reset() => + _current = _start - 1; - if (index < 0) - { - value = default!; - return false; - } + public void Dispose() + { + } + } - value = items[index].Value; - return true; - } + internal sealed class ReadOnlyDictionaryKeyCollection : global::System.Collections.Generic.IEnumerable + { + private readonly global::System.Collections.Generic.IReadOnlyDictionary dictionary; - public bool ContainsKey(K key) - { - return HasKey(key); - } + public ReadOnlyDictionaryKeyCollection(global::System.Collections.Generic.IReadOnlyDictionary dictionary) + { + this.dictionary = dictionary ?? throw new ArgumentNullException(nameof(dictionary)); + } - global::System.Collections.Generic.IEnumerator> global::Windows.Foundation.Collections.IIterable>.First() - { - var itemsAsIKeyValuePairs = new global::Windows.Foundation.Collections.IKeyValuePair[items.Length]; - for (var i = 0; i < items.Length; i++) - { - itemsAsIKeyValuePairs[i] = new KeyValuePair.ToIKeyValuePair(ref items[i]); - } - return new Enumerator>(itemsAsIKeyValuePairs, firstItemIndex, lastItemIndex); - } + public global::System.Collections.Generic.IEnumerator GetEnumerator() + { + return new ReadOnlyDictionaryKeyEnumerator(dictionary); + } + IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); - public global::System.Collections.Generic.IEnumerator> GetEnumerator() - { - return new Enumerator>(items, firstItemIndex, lastItemIndex); - } + private sealed class ReadOnlyDictionaryKeyEnumerator : global::System.Collections.Generic.IEnumerator + { + private readonly global::System.Collections.Generic.IReadOnlyDictionary dictionary; + private global::System.Collections.Generic.IEnumerator> enumeration; - IEnumerator global::System.Collections.IEnumerable.GetEnumerator() - { - return new Enumerator>(items, firstItemIndex, lastItemIndex); - } + public ReadOnlyDictionaryKeyEnumerator(global::System.Collections.Generic.IReadOnlyDictionary dictionary) + { + this.dictionary = dictionary ?? throw new ArgumentNullException(nameof(dictionary)); + enumeration = dictionary.GetEnumerator(); } - internal struct Enumerator : global::System.Collections.Generic.IEnumerator + void IDisposable.Dispose() { - private readonly T[] _array; - private readonly int _start; - private readonly int _end; - private int _current; + enumeration.Dispose(); + } - internal Enumerator(T[] items, int first, int end) - { - _array = items; - _start = first; - _end = end; - _current = _start - 1; - } + public bool MoveNext() + { + return enumeration.MoveNext(); + } - public bool MoveNext() - { - if (_current < _end) - { - _current++; - return true; - } - return false; - } + object IEnumerator.Current => Current; - public T Current - { - get - { - if (_current < _start) throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_EnumNotStarted); - if (_current > _end) throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_EnumEnded); - return _array[_current]; - } - } + public K Current => enumeration.Current.Key; - object IEnumerator.Current => Current; + public void Reset() + { + enumeration = dictionary.GetEnumerator(); + } + } + } - void IEnumerator.Reset() => - _current = _start - 1; + internal sealed class ReadOnlyDictionaryValueCollection : global::System.Collections.Generic.IEnumerable + { + private readonly global::System.Collections.Generic.IReadOnlyDictionary dictionary; - public void Dispose() - { - } - } + public ReadOnlyDictionaryValueCollection(global::System.Collections.Generic.IReadOnlyDictionary dictionary) + { + this.dictionary = dictionary ?? throw new ArgumentNullException(nameof(dictionary)); } - [Guid("E480CE40-A338-4ADA-ADCF-272272E48CB9")] - public unsafe struct Vftbl + public global::System.Collections.Generic.IEnumerator GetEnumerator() { - internal IInspectable.Vftbl IInspectableVftbl; - public global::System.Delegate Lookup_0; - private void* _get_Size_1; - internal delegate* unmanaged[Stdcall] GetSize_1 { get => (delegate* unmanaged[Stdcall])_get_Size_1; set => _get_Size_1 = (void*)value; } - public global::System.Delegate HasKey_2; - private void* _split_3; - internal delegate* unmanaged[Stdcall] Split_3 { get => (delegate* unmanaged[Stdcall])_split_3; set => _split_3 = (void*)value; } + return new ReadOnlyDictionaryValueEnumerator(dictionary); + } + global::System.Collections.IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); - public static Guid PIID = GuidGenerator.CreateIID(typeof(IReadOnlyDictionary)); - private static readonly Type Lookup_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, Marshaler.AbiType.MakeByRefType(), typeof(int) }); - private static readonly Type HasKey_2_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(byte).MakeByRefType(), typeof(int) }); + private sealed class ReadOnlyDictionaryValueEnumerator : global::System.Collections.Generic.IEnumerator + { + private readonly global::System.Collections.Generic.IReadOnlyDictionary dictionary; + private global::System.Collections.Generic.IEnumerator> enumeration; - internal unsafe Vftbl(IntPtr thisPtr) : this() + public ReadOnlyDictionaryValueEnumerator(global::System.Collections.Generic.IReadOnlyDictionary dictionary) { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); - Lookup_0 = Marshal.GetDelegateForFunctionPointer(vftbl[6], Lookup_0_Type); - GetSize_1 = (delegate* unmanaged[Stdcall])vftbl[7]; - HasKey_2 = Marshal.GetDelegateForFunctionPointer(vftbl[8], HasKey_2_Type); - Split_3 = (delegate* unmanaged[Stdcall])vftbl[9]; + this.dictionary = dictionary ?? throw new ArgumentNullException(nameof(dictionary)); + enumeration = dictionary.GetEnumerator(); } - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - private static readonly Delegate[] DelegateCache = new Delegate[2]; - - static unsafe Vftbl() + void IDisposable.Dispose() { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - Lookup_0 = global::System.Delegate.CreateDelegate(Lookup_0_Type, typeof(Vftbl).GetMethod("Do_Abi_Lookup_0", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType, Marshaler.AbiType)), - _get_Size_1 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[0] = new _get_PropertyAsUInt32(Do_Abi_get_Size_1)), - HasKey_2 = global::System.Delegate.CreateDelegate(HasKey_2_Type, typeof(Vftbl).GetMethod("Do_Abi_HasKey_2", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType)), - _split_3 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[1] = new IReadOnlyDictionary_Delegates.Split_3(Do_Abi_Split_3)), - }; - var nativeVftbl = (IntPtr*)Marshal.AllocCoTaskMem(Marshal.SizeOf() + sizeof(IntPtr) * 4); - Marshal.StructureToPtr(AbiToProjectionVftable.IInspectableVftbl, (IntPtr)nativeVftbl, false); - nativeVftbl[6] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.Lookup_0); - nativeVftbl[7] = (IntPtr)AbiToProjectionVftable.GetSize_1; - nativeVftbl[8] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.HasKey_2); - nativeVftbl[9] = (IntPtr)AbiToProjectionVftable.Split_3; + enumeration.Dispose(); + } - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + public bool MoveNext() + { + return enumeration.MoveNext(); } - private static ConditionalWeakTable, ToAbiHelper> _adapterTable = - new ConditionalWeakTable, ToAbiHelper>(); + object IEnumerator.Current => Current; - private static global::Windows.Foundation.Collections.IMapView FindAdapter(IntPtr thisPtr) + public V Current => enumeration.Current.Value; + + public void Reset() { - var __this = global::WinRT.ComWrappersSupport.FindObject>(thisPtr); - return _adapterTable.GetValue(__this, (dictionary) => new ToAbiHelper(dictionary)); + enumeration = dictionary.GetEnumerator(); } + } + } - private static unsafe int Do_Abi_Lookup_0(void* thisPtr, KAbi key, out VAbi __return_value__) - { - V ____return_value__ = default; + [DynamicInterfaceCastableImplementation] + [Guid("E480CE40-A338-4ADA-ADCF-272272E48CB9")] +#pragma warning disable CA2256 // Not implementing IMapView for [DynamicInterfaceCastableImplementation], as we don't expect to need IDIC for WinRT types + interface IReadOnlyDictionary : global::System.Collections.Generic.IReadOnlyDictionary, global::Windows.Foundation.Collections.IMapView +#pragma warning restore CA2256 + { + public static IObjectReference CreateMarshaler(global::System.Collections.Generic.IReadOnlyDictionary obj) => + obj is null ? null : ComWrappersSupport.CreateCCWForObject(obj, PIID); - __return_value__ = default; + public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.Generic.IReadOnlyDictionary obj) => + ComWrappersSupport.CreateCCWForObjectForMarshaling(obj, PIID); - try - { - ____return_value__ = FindAdapter(new IntPtr(thisPtr)).Lookup(Marshaler.FromAbi(key)); - __return_value__ = (VAbi)Marshaler.FromManaged(____return_value__); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_HasKey_2(void* thisPtr, KAbi key, out byte __return_value__) - { - bool ____return_value__ = default; + public static IntPtr GetAbi(IObjectReference objRef) => + objRef?.ThisPtr ?? IntPtr.Zero; - __return_value__ = default; + public static IntPtr FromManaged(global::System.Collections.Generic.IReadOnlyDictionary value) => + (value is null) ? IntPtr.Zero : CreateMarshaler2(value).Detach(); - try - { - ____return_value__ = FindAdapter(new IntPtr(thisPtr)).HasKey(Marshaler.FromAbi(key)); - __return_value__ = (byte)(____return_value__ ? 1 : 0); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_Split_3(IntPtr thisPtr, out IntPtr first, out IntPtr second) - { + public static void DisposeMarshaler(IObjectReference objRef) => objRef?.Dispose(); - first = default; - second = default; - global::Windows.Foundation.Collections.IMapView __first = default; - global::Windows.Foundation.Collections.IMapView __second = default; + public static void DisposeAbi(IntPtr abi) => + MarshalInterfaceHelper>.DisposeAbi(abi); - try - { - FindAdapter(thisPtr).Split(out __first, out __second); - first = MarshalInterface>.FromManaged(__first); - second = MarshalInterface>.FromManaged(__second); - } - catch (Exception __exception__) + public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IReadOnlyDictionary)); + +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + public sealed class ToAbiHelper : global::Windows.Foundation.Collections.IMapView +#pragma warning restore CA2257 + { + private readonly global::System.Collections.Generic.IReadOnlyDictionary _dictionary; + + internal ToAbiHelper(global::System.Collections.Generic.IReadOnlyDictionary dictionary) => _dictionary = dictionary; + + uint global::Windows.Foundation.Collections.IMapView.Size { get => (uint)_dictionary.Count; } + + global::System.Collections.Generic.IEnumerator> global::Windows.Foundation.Collections.IIterable>.First() => + new KeyValuePair.Enumerator(_dictionary.GetEnumerator()); + + public V Lookup(K key) + { + V value; + bool keyFound = _dictionary.TryGetValue(key, out value); + + if (!keyFound) { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + Exception e = new KeyNotFoundException(String.Format(WinRTRuntimeErrorStrings.Arg_KeyNotFoundWithKey, key.ToString())); + e.SetHResult(ExceptionHelpers.E_BOUNDS); + throw e; } - return 0; + + return value; } - private static unsafe int Do_Abi_get_Size_1(IntPtr thisPtr, out uint __return_value__) - { - uint ____return_value__ = default; - __return_value__ = default; + public uint Size() => (uint)_dictionary.Count; - try + public bool HasKey(K key) => _dictionary.ContainsKey(key); + + void global::Windows.Foundation.Collections.IMapView.Split(out global::Windows.Foundation.Collections.IMapView first, out global::Windows.Foundation.Collections.IMapView second) + { + if (_dictionary.Count < 2) { - ____return_value__ = FindAdapter(thisPtr).Size; - __return_value__ = ____return_value__; + first = null; + second = null; + return; } - catch (Exception __exception__) + + if (!(_dictionary is ConstantSplittableMap splittableMap)) + splittableMap = new ConstantSplittableMap(_dictionary); + + splittableMap.Split(out first, out second); + } + } + + public static readonly IntPtr AbiToProjectionVftablePtr; + static IReadOnlyDictionary() + { + if (RuntimeFeature.IsDynamicCodeCompiled) + { + // Simple invocation guarded by a direct runtime feature check to help the linker. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + InitFallbackCCWVTableIfNeeded(); +#pragma warning restore IL3050 + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2080", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] + [UnconditionalSuppressMessage("Trimming", "IL2081", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] +#endif + [MethodImpl(MethodImplOptions.NoInlining)] + static void InitFallbackCCWVTableIfNeeded() { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + if (IReadOnlyDictionaryMethods.AbiToProjectionVftablePtr == default) + { + // Handle the compat scenario where the source generator wasn't used or IDIC was used. + var initFallbackCCWVtable = (Action)typeof(IReadOnlyDictionaryMethods<,,,>).MakeGenericType(typeof(K), Marshaler.AbiType, typeof(V), Marshaler.AbiType). + GetMethod("InitFallbackCCWVtable", BindingFlags.NonPublic | BindingFlags.Static). + CreateDelegate(typeof(Action)); + initFallbackCCWVtable(); + } } - return 0; } + + AbiToProjectionVftablePtr = IReadOnlyDictionaryMethods.AbiToProjectionVftablePtr; } - public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) + + // This is left here for backwards compat purposes where older generated + // projections can be using FindVftblType and using this to cast. + [Guid("E480CE40-A338-4ADA-ADCF-272272E48CB9")] +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + public unsafe struct Vftbl +#pragma warning restore CA2257 + { + internal IInspectable.Vftbl IInspectableVftbl; + + public static readonly IntPtr AbiToProjectionVftablePtr = ABI.System.Collections.Generic.IReadOnlyDictionary.AbiToProjectionVftablePtr; + + public static readonly Guid PIID = ABI.System.Collections.Generic.IReadOnlyDictionary.PIID; + } + + private static readonly ConditionalWeakTable, ToAbiHelper> _adapterTable = new(); + + internal static global::Windows.Foundation.Collections.IMapView FindAdapter(IntPtr thisPtr) + { + var __this = global::WinRT.ComWrappersSupport.FindObject>(thisPtr); + return _adapterTable.GetValue(__this, (dictionary) => new ToAbiHelper(dictionary)); + } + + public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) { if (thisPtr == IntPtr.Zero) { return null; } - var vftblT = new Vftbl(thisPtr); - return ObjectReference.FromAbi(thisPtr, vftblT); + return ObjectReference.FromAbi(thisPtr, PIID); } - public static Guid PIID = Vftbl.PIID; + + public static readonly Guid PIID = IReadOnlyDictionaryMethods.PIID; global::System.Collections.Generic.IEnumerable global::System.Collections.Generic.IReadOnlyDictionary.Keys { get { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IReadOnlyDictionary).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IReadOnlyDictionary).TypeHandle); return IReadOnlyDictionaryMethods.get_Keys(_obj); } } @@ -900,7 +1193,7 @@ public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) { get { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IReadOnlyDictionary).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IReadOnlyDictionary).TypeHandle); return IReadOnlyDictionaryMethods.get_Values(_obj); } } @@ -908,7 +1201,7 @@ public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) { get { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IReadOnlyDictionary).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IReadOnlyDictionary).TypeHandle); return IReadOnlyDictionaryMethods.get_Count(_obj); } } @@ -916,24 +1209,24 @@ public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) { get { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IReadOnlyDictionary).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IReadOnlyDictionary).TypeHandle); return IReadOnlyDictionaryMethods.Indexer_Get(_obj, key); } } bool global::System.Collections.Generic.IReadOnlyDictionary.ContainsKey(K key) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IReadOnlyDictionary).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IReadOnlyDictionary).TypeHandle); return IReadOnlyDictionaryMethods.ContainsKey(_obj, key); } bool global::System.Collections.Generic.IReadOnlyDictionary.TryGetValue(K key, out V value) { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IReadOnlyDictionary).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IReadOnlyDictionary).TypeHandle); return IReadOnlyDictionaryMethods.TryGetValue(_obj, key, out value); } global::System.Collections.Generic.IEnumerator> global::System.Collections.Generic.IEnumerable>.GetEnumerator() { ((IWinRTObject)this).IsInterfaceImplemented(typeof(global::System.Collections.Generic.IEnumerable>).TypeHandle, true); - var _objEnumerable = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IEnumerable>).TypeHandle)); + var _objEnumerable = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IEnumerable>).TypeHandle); return IEnumerableMethods>.GetEnumerator(_objEnumerable); } @@ -948,5 +1241,7 @@ public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) static class IReadOnlyDictionary_Delegates { public unsafe delegate int Split_3(IntPtr thisPtr, out IntPtr first, out IntPtr second); + + internal unsafe delegate int Split_3_Abi(IntPtr thisPtr, IntPtr* first, IntPtr* second); } } diff --git a/src/WinRT.Runtime/Projections/IReadOnlyDictionary.netstandard2.0.cs b/src/WinRT.Runtime/Projections/IReadOnlyDictionary.netstandard2.0.cs index f36c600f3..2992d13a8 100644 --- a/src/WinRT.Runtime/Projections/IReadOnlyDictionary.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/IReadOnlyDictionary.netstandard2.0.cs @@ -515,15 +515,15 @@ public struct Vftbl internal _get_PropertyAsUInt32 get_Size_1; public global::System.Delegate HasKey_2; public IReadOnlyDictionary_Delegates.Split_3 Split_3; - public static Guid PIID = GuidGenerator.CreateIID(typeof(IReadOnlyDictionary)); - private static readonly Type Lookup_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, Marshaler.AbiType.MakeByRefType(), typeof(int) }); - private static readonly Type HasKey_2_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(byte).MakeByRefType(), typeof(int) }); + public static readonly Guid PIID = GuidGenerator.CreateIIDUnsafe(typeof(IReadOnlyDictionary)); + private static readonly Type Lookup_0_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, Marshaler.AbiType.MakeByRefType(), typeof(int) }); + private static readonly Type HasKey_2_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(byte).MakeByRefType(), typeof(int) }); internal unsafe Vftbl(IntPtr thisPtr) { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); + var vftblPtr = *(void***)thisPtr; + var vftbl = (IntPtr*)vftblPtr; + IInspectableVftbl = *(IInspectable.Vftbl*)vftblPtr; Lookup_0 = Marshal.GetDelegateForFunctionPointer(vftbl[6], Lookup_0_Type); get_Size_1 = Marshal.GetDelegateForFunctionPointer<_get_PropertyAsUInt32>(vftbl[7]); HasKey_2 = Marshal.GetDelegateForFunctionPointer(vftbl[8], HasKey_2_Type); @@ -646,7 +646,7 @@ public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) var vftblT = new Vftbl(thisPtr); return ObjectReference.FromAbi(thisPtr, vftblT); } - public static Guid PIID = Vftbl.PIID; + public static readonly Guid PIID = Vftbl.PIID; public static implicit operator IReadOnlyDictionary(IObjectReference obj) => (obj != null) ? new IReadOnlyDictionary(obj) : null; public static implicit operator IReadOnlyDictionary(ObjectReference obj) => (obj != null) ? new IReadOnlyDictionary(obj) : null; @@ -673,6 +673,7 @@ public unsafe V Lookup(K key) __key = Marshaler.CreateMarshaler2(key); __params[1] = Marshaler.GetAbi(__key); _obj.Vftbl.Lookup_0.DynamicInvokeAbi(__params); + GC.KeepAlive(_obj); return Marshaler.FromAbi(__params[2]); } finally @@ -691,6 +692,7 @@ public unsafe bool HasKey(K key) __key = Marshaler.CreateMarshaler2(key); __params[1] = Marshaler.GetAbi(__key); _obj.Vftbl.HasKey_2.DynamicInvokeAbi(__params); + GC.KeepAlive(_obj); return (byte)__params[2] != 0; } finally @@ -706,6 +708,7 @@ internal unsafe void Split(out global::Windows.Foundation.Collections.IMapView>.FromAbi(__first); second = MarshalInterface>.FromAbi(__second); } @@ -722,6 +725,7 @@ public unsafe uint Size { uint __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Size_1(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } } diff --git a/src/WinRT.Runtime/Projections/IReadOnlyList.net5.cs b/src/WinRT.Runtime/Projections/IReadOnlyList.net5.cs index 5e861255f..7e38a4be0 100644 --- a/src/WinRT.Runtime/Projections/IReadOnlyList.net5.cs +++ b/src/WinRT.Runtime/Projections/IReadOnlyList.net5.cs @@ -1,513 +1,773 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.InteropServices; -using WinRT; -using WinRT.Interop; - -#pragma warning disable 0169 // warning CS0169: The field '...' is never used -#pragma warning disable 0649 // warning CS0169: Field '...' is never assigned to - -namespace Windows.Foundation.Collections -{ - [Guid("BBE1FA4C-B0E3-4583-BAEF-1F1B2E483E56")] - interface IVectorView : IIterable - { - T GetAt(uint index); - bool IndexOf(T value, out uint index); - uint GetMany(uint startIndex, ref T[] items); - uint Size { get; } - } -} - -namespace System.Collections.Generic -{ - internal sealed class IReadOnlyListImpl : IReadOnlyList, IWinRTObject - { - private readonly IObjectReference _inner; - - private volatile IObjectReference __iReadOnlyListObjRef; - private IObjectReference Make_IListObjRef() - { - global::System.Threading.Interlocked.CompareExchange(ref __iReadOnlyListObjRef, _inner.As.Vftbl>(), null); - return __iReadOnlyListObjRef; - } - private IObjectReference iReadOnlyListObjRef => __iReadOnlyListObjRef ?? Make_IListObjRef(); - - private volatile IObjectReference __iEnumerableObjRef; - private IObjectReference Make_IEnumerableObjRef() - { - global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, _inner.As.Vftbl>(), null); - return __iEnumerableObjRef; - } - private IObjectReference iEnumerableObjRef => __iEnumerableObjRef ?? Make_IEnumerableObjRef(); - - internal IReadOnlyListImpl(IObjectReference _inner) - { - this._inner = _inner; - } - - public static IReadOnlyListImpl CreateRcw(IInspectable obj) => new(obj.ObjRef); - - IObjectReference IWinRTObject.NativeObject => _inner; - - bool IWinRTObject.HasUnwrappableNativeObject => true; - - private volatile global::System.Collections.Concurrent.ConcurrentDictionary _queryInterfaceCache; - private global::System.Collections.Concurrent.ConcurrentDictionary MakeQueryInterfaceCache() - { - global::System.Threading.Interlocked.CompareExchange(ref _queryInterfaceCache, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); - return _queryInterfaceCache; - } - global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.QueryInterfaceCache => _queryInterfaceCache ?? MakeQueryInterfaceCache(); - private volatile global::System.Collections.Concurrent.ConcurrentDictionary _additionalTypeData; - private global::System.Collections.Concurrent.ConcurrentDictionary MakeAdditionalTypeData() - { - global::System.Threading.Interlocked.CompareExchange(ref _additionalTypeData, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); - return _additionalTypeData; - } - global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.AdditionalTypeData => _additionalTypeData ?? MakeAdditionalTypeData(); - - public T this[int index] => ABI.System.Collections.Generic.IReadOnlyListMethods.Indexer_Get(iReadOnlyListObjRef, index); - - public int Count => ABI.System.Collections.Generic.IReadOnlyListMethods.get_Count(iReadOnlyListObjRef); - - public IEnumerator GetEnumerator() => ABI.System.Collections.Generic.IEnumerableMethods.GetEnumerator(iEnumerableObjRef); - - IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); - } -} - -namespace ABI.Windows.Foundation.Collections -{ - using global::System; - using global::System.Runtime.CompilerServices; - - internal static class IVectorViewMethods - { - public static unsafe T GetAt(IObjectReference obj, uint index) - { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - - var __params = new object[] { ThisPtr, index, null }; - try - { - _obj.Vftbl.GetAt_0.DynamicInvokeAbi(__params); - return Marshaler.FromAbi(__params[2]); - } - finally - { - Marshaler.DisposeAbi(__params[2]); - } - } - - public static unsafe bool IndexOf(IObjectReference obj, T value, out uint index) - { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - - object __value = default; - var __params = new object[] { ThisPtr, null, null, null }; - try - { - __value = Marshaler.CreateMarshaler2(value); - __params[1] = Marshaler.GetAbi(__value); - _obj.Vftbl.IndexOf_2.DynamicInvokeAbi(__params); - index = (uint)__params[2]; - return (byte)__params[3] != 0; - } - finally - { - Marshaler.DisposeMarshaler(__value); - } - } - - public static unsafe uint GetMany(IObjectReference obj, uint startIndex, ref T[] items) - { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - - object __items = default; - int __items_length = default; - IntPtr __items_data = default; - uint __retval = default; - try - { - __items = Marshaler.CreateMarshalerArray(items); - (__items_length, __items_data) = Marshaler.GetAbiArray(__items); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetMany_3(ThisPtr, startIndex, __items_length, __items_data, out __retval)); - items = Marshaler.FromAbiArray((__items_length, __items_data)); - return __retval; - } - finally - { - Marshaler.DisposeMarshalerArray(__items); - } - } - - public static unsafe uint get_Size(IObjectReference obj) - { - var _obj = (ObjectReference.Vftbl>)obj; - var ThisPtr = _obj.ThisPtr; - - uint __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetSize_1(ThisPtr, out __retval)); - return __retval; - } - } -} - -namespace ABI.System.Collections.Generic -{ - using global::System; - using global::System.Runtime.CompilerServices; - -#if EMBED - internal -#else - public -#endif - static class IReadOnlyListMethods - { - public static int get_Count(IObjectReference obj) - { - uint size = ABI.Windows.Foundation.Collections.IVectorViewMethods.get_Size(obj); - if (((uint)int.MaxValue) < size) - { - throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_CollectionBackingListTooLarge); - } - return (int)size; - } - - public static T Indexer_Get(IObjectReference obj, int index) - { - if (index < 0) - throw new ArgumentOutOfRangeException(nameof(index)); - try - { - return ABI.Windows.Foundation.Collections.IVectorViewMethods.GetAt(obj, (uint)index); - // We delegate bounds checking to the underlying collection and if it detected a fault, - // we translate it to the right exception: - } - catch (Exception ex) - { - if (ExceptionHelpers.E_BOUNDS == ex.HResult) - throw new ArgumentOutOfRangeException(nameof(index)); - - throw; - } - } - } - - [DynamicInterfaceCastableImplementation] - [Guid("BBE1FA4C-B0E3-4583-BAEF-1F1B2E483E56")] - interface IReadOnlyList : global::System.Collections.Generic.IReadOnlyList, global::Windows.Foundation.Collections.IVectorView - { - public static IObjectReference CreateMarshaler(global::System.Collections.Generic.IReadOnlyList obj) => - obj is null ? null : ComWrappersSupport.CreateCCWForObject(obj, PIID); - - public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.Generic.IReadOnlyList obj) => - ComWrappersSupport.CreateCCWForObjectForMarshaling(obj, PIID); - - public static IntPtr GetAbi(IObjectReference objRef) => - objRef?.ThisPtr ?? IntPtr.Zero; - - public static IntPtr FromManaged(global::System.Collections.Generic.IReadOnlyList value) => - (value is null) ? IntPtr.Zero : CreateMarshaler2(value).Detach(); - - public static void DisposeMarshaler(IObjectReference objRef) => objRef?.Dispose(); - - public static void DisposeAbi(IntPtr abi) => - MarshalInterfaceHelper>.DisposeAbi(abi); - - public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IReadOnlyList)); - - public sealed class ToAbiHelper : global::Windows.Foundation.Collections.IVectorView - { - private readonly global::System.Collections.Generic.IReadOnlyList _list; - - internal ToAbiHelper(global::System.Collections.Generic.IReadOnlyList list) => _list = list; - - global::System.Collections.Generic.IEnumerator global::Windows.Foundation.Collections.IIterable.First() => _list.GetEnumerator(); - - private static void EnsureIndexInt32(uint index, int limit = int.MaxValue) - { - // We use '<=' and not '<' because int.MaxValue == index would imply - // that Size > int.MaxValue: - if (((uint)int.MaxValue) <= index || index >= (uint)limit) - { - Exception e = new ArgumentOutOfRangeException(nameof(index), WinRTRuntimeErrorStrings.ArgumentOutOfRange_IndexLargerThanMaxValue); - e.SetHResult(ExceptionHelpers.E_BOUNDS); - throw e; - } - } - - public T GetAt(uint index) - { - EnsureIndexInt32(index, _list.Count); - - try - { - return _list[(int)index]; - } - catch (ArgumentOutOfRangeException ex) - { - ex.SetHResult(ExceptionHelpers.E_BOUNDS); - throw; - } - } - - public uint Size => (uint)_list.Count; - - public bool IndexOf(T value, out uint index) - { - int ind = -1; - int max = _list.Count; - for (int i = 0; i < max; i++) - { - if (EqualityComparer.Default.Equals(value, _list[i])) - { - ind = i; - break; - } - } - - if (-1 == ind) - { - index = 0; - return false; - } - - index = (uint)ind; - return true; - } - - public uint GetMany(uint startIndex, ref T[] items) - { - // Spec says "calling GetMany with startIndex equal to the length of the vector - // (last valid index + 1) and any specified capacity will succeed and return zero actual - // elements". - if (startIndex == _list.Count) - return 0; - - EnsureIndexInt32(startIndex, _list.Count); - - if (items == null) - { - return 0; - } - - uint itemCount = Math.Min((uint)items.Length, (uint)_list.Count - startIndex); - - for (uint i = 0; i < itemCount; ++i) - { - items[i] = _list[(int)(i + startIndex)]; - } - - if (typeof(T) == typeof(string)) - { - string[] stringItems = (items as string[])!; - - // Fill in the rest of the array with string.Empty to avoid marshaling failure - for (uint i = itemCount; i < items.Length; ++i) - stringItems[i] = string.Empty; - } - - return itemCount; - } - } - - [Guid("BBE1FA4C-B0E3-4583-BAEF-1F1B2E483E56")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - public global::System.Delegate GetAt_0; - private void* _get_Size_1; - internal delegate* unmanaged[Stdcall] GetSize_1 { get => (delegate* unmanaged[Stdcall])_get_Size_1; set => _get_Size_1 = (void*)value; } - public global::System.Delegate IndexOf_2; - private void* _getMany_3; - public delegate* unmanaged[Stdcall] GetMany_3 { get => (delegate* unmanaged[Stdcall])_getMany_3; set => _getMany_3 = (void*)value; } - - public static Guid PIID = GuidGenerator.CreateIID(typeof(IReadOnlyList)); - private static readonly Type GetAt_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType.MakeByRefType(), typeof(int) }); - private static readonly Type IndexOf_2_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }); - - internal unsafe Vftbl(IntPtr thisPtr) : this() - { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); - GetAt_0 = Marshal.GetDelegateForFunctionPointer(vftbl[6], GetAt_0_Type); - GetSize_1 = (delegate* unmanaged[Stdcall])vftbl[7]; - IndexOf_2 = Marshal.GetDelegateForFunctionPointer(vftbl[8], IndexOf_2_Type); - GetMany_3 = (delegate* unmanaged[Stdcall])vftbl[9]; - } - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - private static readonly Delegate[] DelegateCache = new Delegate[2]; - - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - GetAt_0 = global::System.Delegate.CreateDelegate(GetAt_0_Type, typeof(Vftbl).GetMethod("Do_Abi_GetAt_0", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType)), - _get_Size_1 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[0] = new _get_PropertyAsUInt32(Do_Abi_get_Size_1)), - IndexOf_2 = global::System.Delegate.CreateDelegate(IndexOf_2_Type, typeof(Vftbl).GetMethod("Do_Abi_IndexOf_2", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType)), - _getMany_3 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[1] = new IReadOnlyList_Delegates.GetMany_3(Do_Abi_GetMany_3)), - }; - var nativeVftbl = (IntPtr*)Marshal.AllocCoTaskMem(Marshal.SizeOf() + sizeof(IntPtr) * 4); - Marshal.StructureToPtr(AbiToProjectionVftable.IInspectableVftbl, (IntPtr)nativeVftbl, false); - nativeVftbl[6] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.GetAt_0); - nativeVftbl[7] = (IntPtr)AbiToProjectionVftable.GetSize_1; - nativeVftbl[8] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.IndexOf_2); - nativeVftbl[9] = (IntPtr)AbiToProjectionVftable.GetMany_3; - - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - private static ConditionalWeakTable, ToAbiHelper> _adapterTable = - new ConditionalWeakTable, ToAbiHelper>(); - - private static ToAbiHelper FindAdapter(IntPtr thisPtr) - { - var __this = global::WinRT.ComWrappersSupport.FindObject>(thisPtr); - return _adapterTable.GetValue(__this, (list) => new ToAbiHelper(list)); - } - - private static unsafe int Do_Abi_GetAt_0(void* thisPtr, uint index, out TAbi __return_value__) - { - T ____return_value__ = default; - __return_value__ = default; - try - { - ____return_value__ = FindAdapter(new IntPtr(thisPtr)).GetAt(index); - __return_value__ = (TAbi)Marshaler.FromManaged(____return_value__); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_IndexOf_2(void* thisPtr, TAbi value, out uint index, out byte __return_value__) - { - bool ____return_value__ = default; - - index = default; - __return_value__ = default; - uint __index = default; - - try - { - ____return_value__ = FindAdapter(new IntPtr(thisPtr)).IndexOf(Marshaler.FromAbi(value), out __index); - index = __index; - __return_value__ = (byte)(____return_value__ ? 1 : 0); - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_GetMany_3(IntPtr thisPtr, uint startIndex, int __itemsSize, IntPtr items, out uint __return_value__) - { - uint ____return_value__ = default; - - __return_value__ = default; - T[] __items = Marshaler.FromAbiArray((__itemsSize, items)); - - try - { - ____return_value__ = FindAdapter(thisPtr).GetMany(startIndex, ref __items); - Marshaler.CopyManagedArray(__items, items); - __return_value__ = ____return_value__; - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_get_Size_1(IntPtr thisPtr, out uint __return_value__) - { - uint ____return_value__ = default; - - __return_value__ = default; - - try - { - ____return_value__ = FindAdapter(thisPtr).Size; - __return_value__ = ____return_value__; - - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) - { - if (thisPtr == IntPtr.Zero) - { - return null; - } - var vftblT = new Vftbl(thisPtr); - return ObjectReference.FromAbi(thisPtr, vftblT); - } - public static Guid PIID = Vftbl.PIID; - - int global::System.Collections.Generic.IReadOnlyCollection.Count - { - get - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IReadOnlyList).TypeHandle)); - return IReadOnlyListMethods.get_Count(_obj); - } - } - - T global::System.Collections.Generic.IReadOnlyList.this[int index] - { - get - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IReadOnlyList).TypeHandle)); - return IReadOnlyListMethods.Indexer_Get(_obj, index); - } - } - - global::System.Collections.Generic.IEnumerator global::System.Collections.Generic.IEnumerable.GetEnumerator() - { - ((IWinRTObject)this).IsInterfaceImplemented(typeof(global::System.Collections.Generic.IEnumerable).TypeHandle, true); - var _objEnumerable = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IEnumerable).TypeHandle)); - return IEnumerableMethods.GetEnumerator(_objEnumerable); - } - - IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); - } - -#if EMBED - internal -#else - public -#endif - static class IReadOnlyList_Delegates - { - public unsafe delegate int GetMany_3(IntPtr thisPtr, uint startIndex, int __itemsSize, IntPtr items, out uint __return_value__); +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.InteropServices; +using WinRT; +using WinRT.Interop; + +#pragma warning disable 0169 // warning CS0169: The field '...' is never used +#pragma warning disable 0649 // warning CS0169: Field '...' is never assigned to + +namespace Windows.Foundation.Collections +{ + [Guid("BBE1FA4C-B0E3-4583-BAEF-1F1B2E483E56")] + interface IVectorView : IIterable + { + T GetAt(uint index); + bool IndexOf(T value, out uint index); + uint GetMany(uint startIndex, ref T[] items); + uint Size { get; } + } +} + +namespace System.Collections.Generic +{ + internal sealed class IReadOnlyListImpl : IReadOnlyList, IWinRTObject + { + private readonly IObjectReference _inner; + + private volatile IObjectReference __iReadOnlyListObjRef; + private IObjectReference Make_IListObjRef() + { + global::System.Threading.Interlocked.CompareExchange(ref __iReadOnlyListObjRef, _inner.As(ABI.System.Collections.Generic.IReadOnlyListMethods.PIID), null); + return __iReadOnlyListObjRef; + } + private IObjectReference iReadOnlyListObjRef => __iReadOnlyListObjRef ?? Make_IListObjRef(); + + private volatile IObjectReference __iEnumerableObjRef; + private IObjectReference Make_IEnumerableObjRef() + { + global::System.Threading.Interlocked.CompareExchange(ref __iEnumerableObjRef, _inner.As(ABI.System.Collections.Generic.IEnumerableMethods.PIID), null); + return __iEnumerableObjRef; + } + private IObjectReference iEnumerableObjRef => __iEnumerableObjRef ?? Make_IEnumerableObjRef(); + + internal IReadOnlyListImpl(IObjectReference _inner) + { + this._inner = _inner; + } + + public static IReadOnlyListImpl CreateRcw(IInspectable obj) => new(obj.ObjRef); + + IObjectReference IWinRTObject.NativeObject => _inner; + + bool IWinRTObject.HasUnwrappableNativeObject => true; + + private volatile global::System.Collections.Concurrent.ConcurrentDictionary _queryInterfaceCache; + private global::System.Collections.Concurrent.ConcurrentDictionary MakeQueryInterfaceCache() + { + global::System.Threading.Interlocked.CompareExchange(ref _queryInterfaceCache, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); + return _queryInterfaceCache; + } + global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.QueryInterfaceCache => _queryInterfaceCache ?? MakeQueryInterfaceCache(); + private volatile global::System.Collections.Concurrent.ConcurrentDictionary _additionalTypeData; + private global::System.Collections.Concurrent.ConcurrentDictionary MakeAdditionalTypeData() + { + global::System.Threading.Interlocked.CompareExchange(ref _additionalTypeData, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); + return _additionalTypeData; + } + global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.AdditionalTypeData => _additionalTypeData ?? MakeAdditionalTypeData(); + + public T this[int index] => ABI.System.Collections.Generic.IReadOnlyListMethods.Indexer_Get(iReadOnlyListObjRef, index); + + public int Count => ABI.System.Collections.Generic.IReadOnlyListMethods.get_Count(iReadOnlyListObjRef); + + public IEnumerator GetEnumerator() => ABI.System.Collections.Generic.IEnumerableMethods.GetEnumerator(iEnumerableObjRef); + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +} + +namespace ABI.Windows.Foundation.Collections +{ + using global::System; + using global::System.Diagnostics.CodeAnalysis; + using global::System.Runtime.CompilerServices; + + internal static class IVectorViewMethods + { + // These function pointers will be set by IReadOnlyListMethods + // when it is called by the source generated type or by the fallback + // mechanism if the source generated type wasn't used. + internal volatile unsafe static delegate* _GetAt; + internal volatile unsafe static delegate* _IndexOf; + internal volatile unsafe static delegate* _GetMany; + internal volatile static bool _RcwHelperInitialized; + + internal static unsafe void EnsureInitialized() + { + if (RuntimeFeature.IsDynamicCodeCompiled) + { + // Simple invocation guarded by a direct runtime feature check to help the linker. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + InitRcwHelperFallbackIfNeeded(); +#pragma warning restore IL3050 + + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2080", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] + [UnconditionalSuppressMessage("Trimming", "IL2081", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] +#endif + [MethodImpl(MethodImplOptions.NoInlining)] + static void InitRcwHelperFallbackIfNeeded() + { + // Handle the compat scenario where the source generator wasn't used and IDIC hasn't been used yet + // and due to that the function pointers haven't been initialized. + if (!_RcwHelperInitialized) + { + var initRcwHelperFallback = (Func)typeof(ABI.System.Collections.Generic.IReadOnlyListMethods<,>).MakeGenericType(typeof(T), Marshaler.AbiType). + GetMethod("InitRcwHelperFallback", BindingFlags.NonPublic | BindingFlags.Static). + CreateDelegate(typeof(Func)); + initRcwHelperFallback(); + } + } + + return; + } + + if (!_RcwHelperInitialized) + { + [DoesNotReturn] + static void ThrowNotInitialized() + { + throw new NotImplementedException( + $"Type '{typeof(global::System.Collections.Generic.IReadOnlyList)}' was called without initializing the RCW methods using 'IReadOnlyListMethods.InitRcwHelper'. " + + $"If using 'IDynamicInterfaceCastable' support to do a dynamic cast to this interface, ensure the 'InitRcwHelper' method is called."); + } + + ThrowNotInitialized(); + } + } + + public static unsafe T GetAt(IObjectReference obj, uint index) + { + EnsureInitialized(); + return _GetAt(obj, index); + } + + public static unsafe uint get_Size(IObjectReference obj) + { + var ThisPtr = obj.ThisPtr; + + uint __retval = default; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[7](ThisPtr, &__retval)); + GC.KeepAlive(obj); + return __retval; + } + + public static unsafe bool IndexOf(IObjectReference obj, T value, out uint index) + { + EnsureInitialized(); + return _IndexOf(obj, value, out index); + } + + public static unsafe uint GetMany(IObjectReference obj, uint startIndex, ref T[] items) + { + // Early return to ensure things are trimmed correctly on NAOT. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + EnsureInitialized(); + return _GetMany(obj, startIndex, items); + } + + if (_GetMany != null) + { + return _GetMany(obj, startIndex, items); + } + else + { + var ThisPtr = obj.ThisPtr; + + object __items = default; + int __items_length = default; + IntPtr __items_data = default; + uint __retval = default; +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + try + { + __items = Marshaler.CreateMarshalerArray(items); + (__items_length, __items_data) = Marshaler.GetAbiArray(__items); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[9](ThisPtr, startIndex, __items_length, __items_data, &__retval)); + GC.KeepAlive(obj); + items = Marshaler.FromAbiArray((__items_length, __items_data)); + return __retval; + } + finally + { + Marshaler.DisposeMarshalerArray(__items); + } +#pragma warning restore IL3050 + } + } + } +} + +namespace ABI.System.Collections.Generic +{ + using global::System; + using global::System.Diagnostics.CodeAnalysis; + using global::System.Runtime.CompilerServices; + +#if EMBED + internal +#else + public +#endif + static class IReadOnlyListMethods + { + static IReadOnlyListMethods() + { + ComWrappersSupport.RegisterHelperType(typeof(global::System.Collections.Generic.IReadOnlyList), typeof(global::ABI.System.Collections.Generic.IReadOnlyList)); + } + + public static int get_Count(IObjectReference obj) + { + uint size = ABI.Windows.Foundation.Collections.IVectorViewMethods.get_Size(obj); + if (((uint)int.MaxValue) < size) + { + throw new InvalidOperationException(WinRTRuntimeErrorStrings.InvalidOperation_CollectionBackingListTooLarge); + } + return (int)size; + } + + public static T Indexer_Get(IObjectReference obj, int index) + { + if (index < 0) + throw new ArgumentOutOfRangeException(nameof(index)); + try + { + return ABI.Windows.Foundation.Collections.IVectorViewMethods.GetAt(obj, (uint)index); + // We delegate bounds checking to the underlying collection and if it detected a fault, + // we translate it to the right exception: + } + catch (Exception ex) + { + if (ExceptionHelpers.E_BOUNDS == ex.HResult) + throw new ArgumentOutOfRangeException(nameof(index)); + + throw; + } + } + + private static IntPtr abiToProjectionVftablePtr; + public static IntPtr AbiToProjectionVftablePtr => abiToProjectionVftablePtr; + + internal static bool TryInitCCWVtable(IntPtr ptr) + { + return global::System.Threading.Interlocked.CompareExchange(ref abiToProjectionVftablePtr, ptr, IntPtr.Zero) == IntPtr.Zero; + } + + public static T Abi_GetAt_0(IntPtr thisPtr, uint index) + { + return IReadOnlyList.FindAdapter(thisPtr).GetAt(index); + } + + public static bool Abi_IndexOf_2(IntPtr thisPtr, T value, out uint index) + { + return IReadOnlyList.FindAdapter(thisPtr).IndexOf(value, out index); + } + + public static uint Abi_GetMany_3(IntPtr thisPtr, uint startIndex, ref T[] items) + { + return IReadOnlyList.FindAdapter(thisPtr).GetMany(startIndex, ref items); + } + + public static uint Abi_get_Size_1(IntPtr thisPtr) + { + return IReadOnlyList.FindAdapter(thisPtr).Size; + } + + internal static readonly Guid PIID = GuidGenerator.CreateIIDUnsafe(typeof(IReadOnlyList)); + public static Guid IID => PIID; + } + +#if EMBED + internal +#else + public +#endif + static class IReadOnlyListMethods where TAbi: unmanaged + { + public unsafe static bool InitRcwHelper( + delegate* getAt, + delegate* indexOf, + delegate* getMany) + { + if (ABI.Windows.Foundation.Collections.IVectorViewMethods._RcwHelperInitialized) + { + return true; + } + + ABI.Windows.Foundation.Collections.IVectorViewMethods._GetAt = getAt; + ABI.Windows.Foundation.Collections.IVectorViewMethods._IndexOf = indexOf; + ABI.Windows.Foundation.Collections.IVectorViewMethods._GetMany = getMany; + + ComWrappersSupport.RegisterTypedRcwFactory( + typeof(global::System.Collections.Generic.IReadOnlyList), + IReadOnlyListImpl.CreateRcw); + ComWrappersSupport.RegisterHelperType(typeof(global::System.Collections.Generic.IReadOnlyList), typeof(global::ABI.System.Collections.Generic.IReadOnlyList)); + + ABI.Windows.Foundation.Collections.IVectorViewMethods._RcwHelperInitialized = true; + return true; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private unsafe static bool InitRcwHelperFallback() + { + return InitRcwHelper(&GetAtDynamic, &IndexOfDynamic, null); + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private unsafe static T GetAtDynamic(IObjectReference obj, uint index) + { + var ThisPtr = obj.ThisPtr; + TAbi result = default; + try + { + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6](ThisPtr, index, &result)); + GC.KeepAlive(obj); + return Marshaler.FromAbi(result); + } + finally + { + Marshaler.DisposeAbi(result); + } + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe bool IndexOfDynamic(IObjectReference obj, T value, out uint index) + { + var ThisPtr = obj.ThisPtr; + byte found; + uint _index; + object __value = default; + var __params = new object[] { ThisPtr, null, (IntPtr)(void*)&_index, (IntPtr)(void*)&found }; + try + { + __value = Marshaler.CreateMarshaler2(value); + __params[1] = Marshaler.GetAbi(__value); + DelegateHelper.Get(obj).IndexOf.DynamicInvokeAbi(__params); + GC.KeepAlive(obj); + GC.KeepAlive(__params); + index = _index; + return found != 0; + } + finally + { + Marshaler.DisposeMarshaler(__value); + } + } + + public static unsafe bool InitCcw( + delegate* unmanaged[Stdcall] getAt, + delegate* unmanaged[Stdcall] getSize, + delegate* unmanaged[Stdcall] indexOf, + delegate* unmanaged[Stdcall] getMany) + { + if (IReadOnlyListMethods.AbiToProjectionVftablePtr != default) + { + return false; + } + + var abiToProjectionVftablePtr = (IntPtr)NativeMemory.AllocZeroed((nuint)(sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * 4)); + *(IInspectable.Vftbl*)abiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[6] = getAt; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[7] = getSize; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[8] = indexOf; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[9] = getMany; + + if (!IReadOnlyListMethods.TryInitCCWVtable(abiToProjectionVftablePtr)) + { + NativeMemory.Free((void*)abiToProjectionVftablePtr); + return false; + } + + return true; + } + + private static global::System.Delegate[] DelegateCache; + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + internal static unsafe void InitFallbackCCWVtable() + { + Type getAt_0_type = Projections.GetAbiDelegateType(new Type[] { typeof(IntPtr), typeof(uint), typeof(TAbi*), typeof(int) }); + + DelegateCache = new global::System.Delegate[] + { + global::System.Delegate.CreateDelegate(getAt_0_type, typeof(IReadOnlyListMethods).GetMethod(nameof(Do_Abi_GetAt_0), BindingFlags.NonPublic | BindingFlags.Static)), + new _get_PropertyAsUInt32_Abi(Do_Abi_get_Size_1), + global::System.Delegate.CreateDelegate(DelegateHelper.IndexOf_2_Type, typeof(IReadOnlyListMethods).GetMethod(nameof(Do_Abi_IndexOf_2), BindingFlags.NonPublic | BindingFlags.Static)), + new IReadOnlyList_Delegates.GetMany_3_Abi(Do_Abi_GetMany_3) + }; + + InitCcw( + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[0]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[1]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[2]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[3]) + ); + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_GetAt_0(IntPtr thisPtr, uint index, TAbi* __return_value__) + { + T ____return_value__ = default; + *__return_value__ = default; + try + { + ____return_value__ = IReadOnlyList.FindAdapter(thisPtr).GetAt(index); + *__return_value__ = (TAbi)Marshaler.FromManaged(____return_value__); + + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_IndexOf_2(IntPtr thisPtr, TAbi value, uint* index, byte* __return_value__) + { + bool ____return_value__ = default; + + *index = default; + *__return_value__ = default; + uint __index = default; + + try + { + ____return_value__ = IReadOnlyList.FindAdapter(thisPtr).IndexOf(Marshaler.FromAbi(value), out __index); + *index = __index; + *__return_value__ = (byte)(____return_value__ ? 1 : 0); + + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + 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; + T[] __items = Marshaler.FromAbiArray((__itemsSize, items)); + + try + { + ____return_value__ = IReadOnlyList.FindAdapter(thisPtr).GetMany(startIndex, ref __items); + Marshaler.CopyManagedArray(__items, items); + *__return_value__ = ____return_value__; + + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe int Do_Abi_get_Size_1(IntPtr thisPtr, uint* __return_value__) + { + uint ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = IReadOnlyList.FindAdapter(thisPtr).Size; + *__return_value__ = ____return_value__; + + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif + private sealed class DelegateHelper + { + internal static Type IndexOf_2_Type = Projections.GetAbiDelegateType(new Type[] { typeof(IntPtr), typeof(TAbi), typeof(uint*), typeof(byte*), typeof(int) }); + + private readonly IntPtr _ptr; + + private Delegate _indexOfDelegate; + public Delegate IndexOf => _indexOfDelegate ?? GenericDelegateHelper.CreateDelegate(_ptr, ref _indexOfDelegate, IndexOf_2_Type, 8); + + private DelegateHelper(IntPtr ptr) + { + _ptr = ptr; + } + + public static DelegateHelper Get(IObjectReference obj) + { + return (DelegateHelper)GenericDelegateHelper.DelegateTable.GetValue(obj, static (objRef) => new DelegateHelper(objRef.ThisPtr)); + } + } + } + + [DynamicInterfaceCastableImplementation] + [Guid("BBE1FA4C-B0E3-4583-BAEF-1F1B2E483E56")] +#pragma warning disable CA2256 // Not implementing IVectorView for [DynamicInterfaceCastableImplementation], as we don't expect to need IDIC for WinRT types + interface IReadOnlyList : global::System.Collections.Generic.IReadOnlyList, global::Windows.Foundation.Collections.IVectorView +#pragma warning restore CA2256 + { + public static IObjectReference CreateMarshaler(global::System.Collections.Generic.IReadOnlyList obj) => + obj is null ? null : ComWrappersSupport.CreateCCWForObject(obj, PIID); + + public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.Generic.IReadOnlyList obj) => + ComWrappersSupport.CreateCCWForObjectForMarshaling(obj, PIID); + + public static IntPtr GetAbi(IObjectReference objRef) => + objRef?.ThisPtr ?? IntPtr.Zero; + + public static IntPtr FromManaged(global::System.Collections.Generic.IReadOnlyList value) => + (value is null) ? IntPtr.Zero : CreateMarshaler2(value).Detach(); + + public static void DisposeMarshaler(IObjectReference objRef) => objRef?.Dispose(); + + public static void DisposeAbi(IntPtr abi) => + MarshalInterfaceHelper>.DisposeAbi(abi); + + public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IReadOnlyList)); + +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + public sealed class ToAbiHelper : global::Windows.Foundation.Collections.IVectorView +#pragma warning restore CA2257 + { + private readonly global::System.Collections.Generic.IReadOnlyList _list; + + internal ToAbiHelper(global::System.Collections.Generic.IReadOnlyList list) => _list = list; + + global::System.Collections.Generic.IEnumerator global::Windows.Foundation.Collections.IIterable.First() => _list.GetEnumerator(); + + private static void EnsureIndexInt32(uint index, int limit = int.MaxValue) + { + // We use '<=' and not '<' because int.MaxValue == index would imply + // that Size > int.MaxValue: + if (((uint)int.MaxValue) <= index || index >= (uint)limit) + { + Exception e = new ArgumentOutOfRangeException(nameof(index), WinRTRuntimeErrorStrings.ArgumentOutOfRange_IndexLargerThanMaxValue); + e.SetHResult(ExceptionHelpers.E_BOUNDS); + throw e; + } + } + + public T GetAt(uint index) + { + EnsureIndexInt32(index, _list.Count); + + try + { + return _list[(int)index]; + } + catch (ArgumentOutOfRangeException ex) + { + ex.SetHResult(ExceptionHelpers.E_BOUNDS); + throw; + } + } + + public uint Size => (uint)_list.Count; + + public bool IndexOf(T value, out uint index) + { + int ind = -1; + int max = _list.Count; + for (int i = 0; i < max; i++) + { + if (EqualityComparer.Default.Equals(value, _list[i])) + { + ind = i; + break; + } + } + + if (-1 == ind) + { + index = 0; + return false; + } + + index = (uint)ind; + return true; + } + + public uint GetMany(uint startIndex, ref T[] items) + { + // Spec says "calling GetMany with startIndex equal to the length of the vector + // (last valid index + 1) and any specified capacity will succeed and return zero actual + // elements". + if (startIndex == _list.Count) + return 0; + + EnsureIndexInt32(startIndex, _list.Count); + + if (items == null) + { + return 0; + } + + uint itemCount = Math.Min((uint)items.Length, (uint)_list.Count - startIndex); + + for (uint i = 0; i < itemCount; ++i) + { + items[i] = _list[(int)(i + startIndex)]; + } + + if (typeof(T) == typeof(string)) + { + string[] stringItems = (items as string[])!; + + // Fill in the rest of the array with string.Empty to avoid marshaling failure + for (uint i = itemCount; i < items.Length; ++i) + stringItems[i] = string.Empty; + } + + return itemCount; + } + } + + public static readonly IntPtr AbiToProjectionVftablePtr; + static IReadOnlyList() + { + if (RuntimeFeature.IsDynamicCodeCompiled) + { + // Simple invocation guarded by a direct runtime feature check to help the linker. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + InitFallbackCCWVTableIfNeeded(); +#pragma warning restore IL3050 + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2080", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] + [UnconditionalSuppressMessage("Trimming", "IL2081", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] +#endif + [MethodImpl(MethodImplOptions.NoInlining)] + static void InitFallbackCCWVTableIfNeeded() + { + if (IReadOnlyListMethods.AbiToProjectionVftablePtr == default) + { + // Handle the compat scenario where the source generator wasn't used or IDIC was used. + var initFallbackCCWVtable = (Action)typeof(IReadOnlyListMethods<,>).MakeGenericType(typeof(T), Marshaler.AbiType). + GetMethod("InitFallbackCCWVtable", BindingFlags.NonPublic | BindingFlags.Static). + CreateDelegate(typeof(Action)); + initFallbackCCWVtable(); + } + } + } + + AbiToProjectionVftablePtr = IReadOnlyListMethods.AbiToProjectionVftablePtr; + } + + // This is left here for backwards compat purposes where older generated + // projections can be using FindVftblType and using this to cast. + [Guid("BBE1FA4C-B0E3-4583-BAEF-1F1B2E483E56")] +#pragma warning disable CA2257 // This member is a type (so it cannot be invoked) + public unsafe struct Vftbl +#pragma warning restore CA2257 + { + internal IInspectable.Vftbl IInspectableVftbl; + + public static readonly IntPtr AbiToProjectionVftablePtr = ABI.System.Collections.Generic.IReadOnlyList.AbiToProjectionVftablePtr; + + public static readonly Guid PIID = ABI.System.Collections.Generic.IReadOnlyList.PIID; + } + + private static readonly ConditionalWeakTable, ToAbiHelper> _adapterTable = new(); + + internal static ToAbiHelper FindAdapter(IntPtr thisPtr) + { + var __this = global::WinRT.ComWrappersSupport.FindObject>(thisPtr); + return _adapterTable.GetValue(__this, (list) => new ToAbiHelper(list)); + } + + public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) + { + if (thisPtr == IntPtr.Zero) + { + return null; + } + return ObjectReference.FromAbi(thisPtr, PIID); + } + + public static readonly Guid PIID = IReadOnlyListMethods.PIID; + + int global::System.Collections.Generic.IReadOnlyCollection.Count + { + get + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IReadOnlyList).TypeHandle); + return IReadOnlyListMethods.get_Count(_obj); + } + } + + T global::System.Collections.Generic.IReadOnlyList.this[int index] + { + get + { + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IReadOnlyList).TypeHandle); + return IReadOnlyListMethods.Indexer_Get(_obj, index); + } + } + + global::System.Collections.Generic.IEnumerator global::System.Collections.Generic.IEnumerable.GetEnumerator() + { + ((IWinRTObject)this).IsInterfaceImplemented(typeof(global::System.Collections.Generic.IEnumerable).TypeHandle, true); + var _objEnumerable = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.Collections.Generic.IEnumerable).TypeHandle); + return IEnumerableMethods.GetEnumerator(_objEnumerable); + } + + IEnumerator global::System.Collections.IEnumerable.GetEnumerator() => GetEnumerator(); + } + +#if EMBED + internal +#else + public +#endif + static class IReadOnlyList_Delegates + { + public unsafe delegate int GetMany_3(IntPtr thisPtr, uint startIndex, int __itemsSize, IntPtr items, out uint __return_value__); + + internal unsafe delegate int GetMany_3_Abi(IntPtr thisPtr, uint startIndex, int __itemsSize, IntPtr items, uint* __return_value__); } -} +} diff --git a/src/WinRT.Runtime/Projections/IReadOnlyList.netstandard2.0.cs b/src/WinRT.Runtime/Projections/IReadOnlyList.netstandard2.0.cs index 264deaa2b..77a756e5f 100644 --- a/src/WinRT.Runtime/Projections/IReadOnlyList.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/IReadOnlyList.netstandard2.0.cs @@ -221,15 +221,15 @@ public struct Vftbl internal _get_PropertyAsUInt32 get_Size_1; public global::System.Delegate IndexOf_2; public IReadOnlyList_Delegates.GetMany_3 GetMany_3; - public static Guid PIID = GuidGenerator.CreateIID(typeof(IReadOnlyList)); - private static readonly Type GetAt_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType.MakeByRefType(), typeof(int) }); - private static readonly Type IndexOf_2_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }); + public static readonly Guid PIID = GuidGenerator.CreateIIDUnsafe(typeof(IReadOnlyList)); + private static readonly Type GetAt_0_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), typeof(uint), Marshaler.AbiType.MakeByRefType(), typeof(int) }); + private static readonly Type IndexOf_2_Type = Projections.GetAbiDelegateType(new Type[] { typeof(void*), Marshaler.AbiType, typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }); internal unsafe Vftbl(IntPtr thisPtr) { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); + var vftblPtr = *(void***)thisPtr; + var vftbl = (IntPtr*)vftblPtr; + IInspectableVftbl = *(IInspectable.Vftbl*)vftblPtr; GetAt_0 = Marshal.GetDelegateForFunctionPointer(vftbl[6], GetAt_0_Type); get_Size_1 = Marshal.GetDelegateForFunctionPointer<_get_PropertyAsUInt32>(vftbl[7]); IndexOf_2 = Marshal.GetDelegateForFunctionPointer(vftbl[8], IndexOf_2_Type); @@ -356,7 +356,7 @@ public static ObjectReference ObjRefFromAbi(IntPtr thisPtr) var vftblT = new Vftbl(thisPtr); return ObjectReference.FromAbi(thisPtr, vftblT); } - public static Guid PIID = Vftbl.PIID; + public static readonly Guid PIID = Vftbl.PIID; public static implicit operator IReadOnlyList(IObjectReference obj) => (obj != null) ? new IReadOnlyList(obj) : null; public static implicit operator IReadOnlyList(ObjectReference obj) => (obj != null) ? new IReadOnlyList(obj) : null; @@ -380,6 +380,7 @@ public unsafe T GetAt(uint index) try { _obj.Vftbl.GetAt_0.DynamicInvokeAbi(__params); + GC.KeepAlive(_obj); return Marshaler.FromAbi(__params[2]); } finally @@ -397,6 +398,7 @@ public unsafe bool IndexOf(T value, out uint index) __value = Marshaler.CreateMarshaler2(value); __params[1] = Marshaler.GetAbi(__value); _obj.Vftbl.IndexOf_2.DynamicInvokeAbi(__params); + GC.KeepAlive(_obj); index = (uint)__params[2]; return (byte)__params[3] != 0; } @@ -417,6 +419,7 @@ public unsafe uint GetMany(uint startIndex, ref T[] items) __items = Marshaler.CreateMarshalerArray(items); (__items_length, __items_data) = Marshaler.GetAbiArray(__items); global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetMany_3(ThisPtr, startIndex, __items_length, __items_data, out __retval)); + GC.KeepAlive(_obj); items = Marshaler.FromAbiArray((__items_length, __items_data)); return __retval; } @@ -432,6 +435,7 @@ public unsafe uint Size { uint __retval = default; global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Size_1(ThisPtr, out __retval)); + GC.KeepAlive(_obj); return __retval; } } diff --git a/src/WinRT.Runtime/Projections/IReferenceArray.net5.cs b/src/WinRT.Runtime/Projections/IReferenceArray.net5.cs index 08d3fd949..08ce73e32 100644 --- a/src/WinRT.Runtime/Projections/IReferenceArray.net5.cs +++ b/src/WinRT.Runtime/Projections/IReferenceArray.net5.cs @@ -1,183 +1,503 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Runtime.InteropServices; -using WinRT; -using WinRT.Interop; - -namespace Windows.Foundation -{ - // Provide a stub definition of IReferenceArray so we have - // a "public" type for the type mapping definition. - // IReferenceArray cannot appear in signatures, so it doesn't need to actually be public. - [Guid("61C17707-2D65-11E0-9AE8-D48564015472")] - [WindowsRuntimeHelperType(typeof(global::ABI.Windows.Foundation.IReferenceArray<>))] - internal interface IReferenceArray - { - T[] Value { get; } - } -} - -namespace ABI.Windows.Foundation -{ - internal static class BoxedArrayIReferenceArrayImpl - { - private static readonly IReferenceArray.Vftbl AbiToProjectionVftable; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using WinRT; +using WinRT.Interop; + +namespace Windows.Foundation +{ + // Provide a stub definition of IReferenceArray so we have + // a "public" type for the type mapping definition. + // IReferenceArray cannot appear in signatures, so it doesn't need to actually be public. + [Guid("61C17707-2D65-11E0-9AE8-D48564015472")] + [WindowsRuntimeHelperType(typeof(global::ABI.Windows.Foundation.IReferenceArray<>))] + internal interface IReferenceArray + { + T[] Value { get; } + } +} + +namespace ABI.Windows.Foundation +{ + internal static class BoxedArrayIReferenceArrayImpl + { public static readonly IntPtr AbiToProjectionVftablePtr; - private static readonly Delegate DelegateCache; - - static unsafe BoxedArrayIReferenceArrayImpl() - { - AbiToProjectionVftable = new IReferenceArray.Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - _get_Value_0 = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache = new IReferenceArray_Delegates.get_Value_0(Do_Abi_get_Value_0)) - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(BoxedArrayIReferenceArrayImpl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable.IInspectableVftbl, (IntPtr)nativeVftbl, false); - nativeVftbl[6] = (IntPtr)AbiToProjectionVftable.GetValue_0; - - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, out int ____return_value__Size, out IntPtr __return_value__) - { - T[] ____return_value__ = default; - - __return_value__ = default; - ____return_value__Size = default; - - try - { - ____return_value__ = (T[])global::WinRT.ComWrappersSupport.FindObject(thisPtr); - (____return_value__Size, __return_value__) = Marshaler.FromManagedArray(____return_value__); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] - [Guid("61C17707-2D65-11E0-9AE8-D48564015472")] - internal sealed class IReferenceArray : global::Windows.Foundation.IReferenceArray - { - public static IObjectReference CreateMarshaler(object value) - { - return value is null ? null : ComWrappersSupport.CreateCCWForObject(value, PIID); - } - - public static ObjectReferenceValue CreateMarshaler2(object value) => - ComWrappersSupport.CreateCCWForObjectForMarshaling(value, PIID); - - public static IntPtr GetAbi(IObjectReference m) => m?.ThisPtr ?? IntPtr.Zero; - - public static object FromAbi(IntPtr ptr) - { - if (ptr == IntPtr.Zero) - { - return null; - } - var vftblT = new Vftbl(ptr); - var wrapper = new IReferenceArray(ObjectReference.FromAbi(ptr, vftblT)); - return wrapper.Value; - } - - internal static unsafe object GetValue(IInspectable inspectable) + private static readonly IReferenceArray_Delegates.get_Value_0 DelegateCache; + + static unsafe BoxedArrayIReferenceArrayImpl() + { + DelegateCache = new IReferenceArray_Delegates.get_Value_0(Do_Abi_get_Value_0); + + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(BoxedArrayIReferenceArrayImpl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr)); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((IntPtr*)AbiToProjectionVftablePtr)[6] = Marshal.GetFunctionPointerForDelegate(DelegateCache); + } + + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, int* ____return_value__Size, IntPtr* __return_value__) + { + T[] ____return_value__ = default; + + *__return_value__ = default; + *____return_value__Size = default; + + try + { + ____return_value__ = (T[])global::WinRT.ComWrappersSupport.FindObject(thisPtr); + (*____return_value__Size, *__return_value__) = FromManagedArray(____return_value__); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static (int, IntPtr) FromManagedArray(T[] items) { + // Blittable value types + if (typeof(T) == typeof(byte) || + typeof(T) == typeof(short) || + typeof(T) == typeof(ushort) || + typeof(T) == typeof(long) || + typeof(T) == typeof(ulong) || + typeof(T) == typeof(int) || + typeof(T) == typeof(uint) || + typeof(T) == typeof(float) || + typeof(T) == typeof(double) || + typeof(T) == typeof(Guid) || + typeof(T) == typeof(global::System.Numerics.Vector2) || + typeof(T) == typeof(global::System.Numerics.Vector3) || + typeof(T) == typeof(global::System.Numerics.Vector4) || + typeof(T) == typeof(global::System.Numerics.Quaternion) || + typeof(T) == typeof(global::System.Numerics.Plane) || + typeof(T) == typeof(global::System.Numerics.Matrix3x2) || + typeof(T) == typeof(global::System.Numerics.Matrix4x4)) + { + return MarshalBlittable.FromManagedArray(items); + } + + // Non-blittable value types + if (typeof(T) == typeof(bool) || + typeof(T) == typeof(char) || + typeof(T) == typeof(TimeSpan) || + typeof(T) == typeof(DateTimeOffset)) + { + return MarshalNonBlittable.FromManagedArray(items); + } + + // Other well-known reference types + if (typeof(T) == typeof(string)) return MarshalString.FromManagedArray(Unsafe.As(items)); + if (typeof(T) == typeof(Type)) return ABI.System.Type.FromManagedArray(Unsafe.As(items)); + if (typeof(T) == typeof(Exception)) return MarshalNonBlittable.FromManagedArray(Unsafe.As(items)); + if (typeof(T) == typeof(object)) return MarshalInspectable.FromManagedArray(Unsafe.As(items)); + +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"Support for 'IReferenceArray' for type '{typeof(T)}' is not available in AOT environments."); + } +#endif +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + return Marshaler.FromManagedArray(items); +#pragma warning restore IL3050 + } + } + + // This type is only used in JIT scenarios, as all well known types supported for AOT use specialized 'IReferenceArray' implementations. + // We can't add '[RequiresDynamicCode]' on this type, as it's indirectly rooted by the interface type. Because it's never actually used + // on AOT, we can just guard each AOT-unsafe code path with a feature check, and throw if on AOT. That avoids ILC warnings. + [Guid("61C17707-2D65-11E0-9AE8-D48564015472")] + internal sealed class IReferenceArray : global::Windows.Foundation.IReferenceArray + { + public static IObjectReference CreateMarshaler(object value) + { + return value is null ? null : ComWrappersSupport.CreateCCWForObject(value, PIID); + } + + public static ObjectReferenceValue CreateMarshaler2(object value) => + ComWrappersSupport.CreateCCWForObjectForMarshaling(value, PIID); + + public static IntPtr GetAbi(IObjectReference m) => m?.ThisPtr ?? IntPtr.Zero; + + public static object FromAbi(IntPtr ptr) + { +#if NET + // This path is unreachable on AOT, but this helps ILC + if (!RuntimeFeature.IsDynamicCodeSupported) + { + throw new NotSupportedException("'IReferenceArray' is not supported in AOT environments."); + } +#endif + + if (ptr == IntPtr.Zero) + { + return null; + } + + var wrapper = new IReferenceArray(ObjectReference.FromAbi(ptr, PIID)); + return wrapper.Value; + } + + public static unsafe object GetValue(IInspectable inspectable) + { +#if NET + // This path is unreachable on AOT, but this fixes some ILC warnings + if (!RuntimeFeature.IsDynamicCodeSupported) + { + throw new NotSupportedException("'IReferenceArray' is not supported in AOT environments."); + } +#endif + IntPtr referenceArrayPtr = IntPtr.Zero; int __retval_length = default; IntPtr __retval_data = default; +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 try { - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref PIID, out referenceArrayPtr)); + ExceptionHelpers.ThrowExceptionForHR( +#if NET8_0_OR_GREATER + Marshal.QueryInterface(inspectable.ThisPtr, in PIID, out referenceArrayPtr) +#else + Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in PIID), out referenceArrayPtr) +#endif + ); + GC.KeepAlive(inspectable); ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)referenceArrayPtr)[6](referenceArrayPtr, &__retval_length, &__retval_data)); return Marshaler.FromAbiArray((__retval_length, __retval_data)); } finally { Marshaler.DisposeAbiArray((__retval_length, __retval_data)); - Marshal.Release(referenceArrayPtr); + MarshalExtensions.ReleaseIfNotNull(referenceArrayPtr); } - } - - public static unsafe void CopyManaged(object o, IntPtr dest) - { - *(IntPtr*)dest.ToPointer() = CreateMarshaler2(o).Detach(); - } - - public static IntPtr FromManaged(object value) - { - if (value is null) - { - return IntPtr.Zero; - } - return CreateMarshaler2(value).Detach(); - } - - public static void DisposeMarshaler(IObjectReference m) { m?.Dispose(); } - public static void DisposeAbi(IntPtr abi) { MarshalInspectable.DisposeAbi(abi); } - - public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IReferenceArray)); - - [Guid("61C17707-2D65-11E0-9AE8-D48564015472")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - internal void* _get_Value_0; - internal delegate* unmanaged[Stdcall] GetValue_0 { get => (delegate* unmanaged[Stdcall])_get_Value_0; set => _get_Value_0 = (void*)value; } - - public static Guid PIID = GuidGenerator.CreateIID(typeof(IReferenceArray)); - - internal unsafe Vftbl(IntPtr thisPtr) : this() - { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); - GetValue_0 = (delegate* unmanaged[Stdcall])vftbl[6]; - } - } - - public static Guid PIID = Vftbl.PIID; - - public static implicit operator IReferenceArray(IObjectReference obj) => (obj != null) ? new IReferenceArray(obj) : null; - public static implicit operator IReferenceArray(ObjectReference obj) => (obj != null) ? new IReferenceArray(obj) : null; - private readonly ObjectReference _obj; - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public IReferenceArray(IObjectReference obj) : this(obj.As()) { } - public IReferenceArray(ObjectReference obj) - { - _obj = obj; - } - - - public unsafe T[] Value - { - get - { - int __retval_length = default; - IntPtr __retval_data = default; - try +#pragma warning disable IL3050 + } + + public static unsafe void CopyManaged(object o, IntPtr dest) + { + *(IntPtr*)dest.ToPointer() = CreateMarshaler2(o).Detach(); + } + + public static IntPtr FromManaged(object value) + { + if (value is null) + { + return IntPtr.Zero; + } + return CreateMarshaler2(value).Detach(); + } + + public static void DisposeMarshaler(IObjectReference m) { m?.Dispose(); } + public static void DisposeAbi(IntPtr abi) { MarshalInspectable.DisposeAbi(abi); } + + public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IReferenceArray)); + + public static readonly Guid PIID = GuidGenerator.CreateIIDUnsafe(typeof(IReferenceArray)); + + private readonly ObjectReference _obj; + public IntPtr ThisPtr => _obj.ThisPtr; + + public IReferenceArray(ObjectReference obj) + { + _obj = obj; + } + + public unsafe T[] Value + { + get + { +#if NET + // This path is unreachable on AOT, but this fixes some ILC warnings + if (!RuntimeFeature.IsDynamicCodeSupported) + { + throw new NotSupportedException("'IReferenceArray' is not supported in AOT environments."); + } +#endif + + int __retval_length = default; + IntPtr __retval_data = default; +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + try + { + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6](ThisPtr, &__retval_length, &__retval_data)); + GC.KeepAlive(_obj); + return Marshaler.FromAbiArray((__retval_length, __retval_data)); + } + finally { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetValue_0(ThisPtr, out __retval_length, out __retval_data)); - return Marshaler.FromAbiArray((__retval_length, __retval_data)); - } - finally - { - Marshaler.DisposeAbiArray((__retval_length, __retval_data)); - } - } - } - } - - internal static class IReferenceArray_Delegates - { - public unsafe delegate int get_Value_0(IntPtr thisPtr, out int ____return_value__Size, out IntPtr __return_value__); - } + Marshaler.DisposeAbiArray((__retval_length, __retval_data)); + } +#pragma warning restore IL3050 + } + } + } + + internal static class IReferenceArray_Delegates + { + public unsafe delegate int get_Value_0(IntPtr thisPtr, int* ____return_value__Size, IntPtr* __return_value__); + } + + internal static class IReferenceArrayType + { + // Gets the IReferenceArray type representation for some built-in known types. + // Note we return array types rather than IReferenceArray to differentiate + // from the reflection support for this and also to save size. + public static global::System.Type GetTypeAsArrayType(global::System.Type type) + { + if (!FeatureSwitches.EnableIReferenceSupport) + { + throw new NotSupportedException("Support for 'IReferenceArray' is not enabled."); + } + + if (type == typeof(string)) return typeof(string[]); + if (type == typeof(int)) return typeof(int[]); + if (type == typeof(byte)) return typeof(byte[]); + if (type == typeof(bool)) return typeof(bool[]); + if (type == typeof(sbyte)) return typeof(sbyte[]); + if (type == typeof(short)) return typeof(short[]); + if (type == typeof(ushort)) return typeof(ushort[]); + if (type == typeof(char)) return typeof(char[]); + if (type == typeof(uint)) return typeof(uint[]); + if (type == typeof(long)) return typeof(long[]); + if (type == typeof(ulong)) return typeof(ulong[]); + if (type == typeof(float)) return typeof(float[]); + if (type == typeof(double)) return typeof(double[]); + if (type == typeof(Guid)) return typeof(Guid[]); + if (type == typeof(global::System.Type)) return typeof(global::System.Type[]); + if (type == typeof(global::System.TimeSpan)) return typeof(global::System.TimeSpan[]); + if (type == typeof(global::System.DateTimeOffset)) return typeof(global::System.DateTimeOffset[]); + if (type == typeof(global::Windows.Foundation.Point)) return typeof(global::Windows.Foundation.Point[]); + if (type == typeof(global::Windows.Foundation.Size)) return typeof(global::Windows.Foundation.Size[]); + if (type == typeof(global::Windows.Foundation.Rect)) return typeof(global::Windows.Foundation.Rect[]); + if (type == typeof(global::System.Numerics.Matrix3x2)) return typeof(global::System.Numerics.Matrix3x2[]); + if (type == typeof(global::System.Numerics.Matrix4x4)) return typeof(global::System.Numerics.Matrix4x4[]); + if (type == typeof(global::System.Numerics.Plane)) return typeof(global::System.Numerics.Plane[]); + if (type == typeof(global::System.Numerics.Quaternion)) return typeof(global::System.Numerics.Quaternion[]); + if (type == typeof(global::System.Numerics.Vector2)) return typeof(global::System.Numerics.Vector2[]); + if (type == typeof(global::System.Numerics.Vector3)) return typeof(global::System.Numerics.Vector3[]); + if (type == typeof(global::System.Numerics.Vector4)) return typeof(global::System.Numerics.Vector4[]); + if (type == typeof(object)) return typeof(object[]); + + return null; + } + + private static Guid GetIID() + { + if (typeof(T) == typeof(int)) return IID.IID_IReferenceArrayOfInt32; + if (typeof(T) == typeof(byte)) return IID.IID_IReferenceArrayOfByte; + if (typeof(T) == typeof(bool)) return IID.IID_IReferenceArrayOfBoolean; + if (typeof(T) == typeof(sbyte)) return IID.IID_IReferenceArrayOfSByte; + if (typeof(T) == typeof(short)) return IID.IID_IReferenceArrayOfInt16; + if (typeof(T) == typeof(ushort)) return IID.IID_IReferenceArrayOfUInt16; + if (typeof(T) == typeof(char)) return IID.IID_IReferenceArrayOfChar; + if (typeof(T) == typeof(uint)) return IID.IID_IReferenceArrayOfUInt32; + if (typeof(T) == typeof(long)) return IID.IID_IReferenceArrayOfInt64; + if (typeof(T) == typeof(ulong)) return IID.IID_IReferenceArrayOfUInt64; + if (typeof(T) == typeof(float)) return IID.IID_IReferenceArrayOfSingle; + if (typeof(T) == typeof(double)) return IID.IID_IReferenceArrayOfDouble; + if (typeof(T) == typeof(Guid)) return IID.IID_IReferenceArrayOfGuid; + if (typeof(T) == typeof(global::System.TimeSpan)) return IID.IID_IReferenceArrayOfTimeSpan; + if (typeof(T) == typeof(global::System.DateTimeOffset)) return IID.IID_IReferenceArrayOfDateTimeOffset; + if (typeof(T) == typeof(global::Windows.Foundation.Point)) return IID.IID_IReferenceArrayOfPoint; + if (typeof(T) == typeof(global::Windows.Foundation.Size)) return IID.IID_IReferenceArrayOfSize; + if (typeof(T) == typeof(global::Windows.Foundation.Rect)) return IID.IID_IReferenceArrayOfRect; + if (typeof(T) == typeof(global::System.Numerics.Matrix3x2)) return IID.IID_IReferenceArrayOfMatrix3x2; + if (typeof(T) == typeof(global::System.Numerics.Matrix4x4)) return IID.IID_IReferenceArrayOfMatrix4x4; + if (typeof(T) == typeof(global::System.Numerics.Plane)) return IID.IID_IReferenceArrayOfPlane; + if (typeof(T) == typeof(global::System.Numerics.Quaternion)) return IID.IID_IReferenceArrayOfQuaternion; + if (typeof(T) == typeof(global::System.Numerics.Vector2)) return IID.IID_IReferenceArrayOfVector2; + if (typeof(T) == typeof(global::System.Numerics.Vector3)) return IID.IID_IReferenceArrayOfVector3; + if (typeof(T) == typeof(global::System.Numerics.Vector4)) return IID.IID_IReferenceArrayOfVector4; + + // If this is an old projection or another type, we shoudn't come here as we pivot based on whether the + // RCW type is an array type or not. + throw new NotSupportedException($"Failed to get the IID of IReferenceArray with type '{typeof(T)}'."); + } + + public static Func GetValueFactory(global::System.Type type) + { + if (!FeatureSwitches.EnableIReferenceSupport) + { + throw new NotSupportedException("Support for 'IReferenceArray' is not enabled."); + } + + return ComWrappersSupport.CreateReferenceCachingFactory(GetValueFactoryInternal(type)); + } + + private static Func GetValueFactoryInternal(global::System.Type type) + { + if (type == typeof(string)) return GetStringValue; + if (type == typeof(int)) return GetBlittableValue; + if (type == typeof(byte)) return GetBlittableValue; + if (type == typeof(bool)) return GetBlittableValue; + if (type == typeof(sbyte)) return GetBlittableValue; + if (type == typeof(short)) return GetBlittableValue; + if (type == typeof(ushort)) return GetBlittableValue; + if (type == typeof(char)) return GetBlittableValue; + if (type == typeof(uint)) return GetBlittableValue; + if (type == typeof(long)) return GetBlittableValue; + if (type == typeof(ulong)) return GetBlittableValue; + if (type == typeof(float)) return GetBlittableValue; + if (type == typeof(double)) return GetBlittableValue; + if (type == typeof(Guid)) return GetBlittableValue; + if (type == typeof(global::System.Type)) return GetTypeValue; + if (type == typeof(global::System.TimeSpan)) return GetNonBlittableValue; + if (type == typeof(global::System.DateTimeOffset)) return GetNonBlittableValue; + if (type == typeof(global::Windows.Foundation.Point)) return GetBlittableValue; + if (type == typeof(global::Windows.Foundation.Size)) return GetBlittableValue; + if (type == typeof(global::Windows.Foundation.Rect)) return GetBlittableValue; + if (type == typeof(global::System.Numerics.Matrix3x2)) return GetBlittableValue; + if (type == typeof(global::System.Numerics.Matrix4x4)) return GetBlittableValue; + if (type == typeof(global::System.Numerics.Plane)) return GetBlittableValue; + if (type == typeof(global::System.Numerics.Quaternion)) return GetBlittableValue; + if (type == typeof(global::System.Numerics.Vector2)) return GetBlittableValue; + if (type == typeof(global::System.Numerics.Vector3)) return GetBlittableValue; + if (type == typeof(global::System.Numerics.Vector4)) return GetBlittableValue; + if (type == typeof(object)) return GetObjectValue; + + // If this is an old projection or another type, we shoudn't come here as we pivot based on whether the + // RCW type is an array type or not. + throw new NotSupportedException($"Failed to get the value of IReferenceArray with type '{type}'."); + } + + private static unsafe object GetBlittableValue(IInspectable inspectable) where T : unmanaged + { + Guid IID = GetIID(); + + IntPtr referenceArrayPtr = IntPtr.Zero; + int __retval_length = default; + IntPtr __retval_data = default; + try + { + ExceptionHelpers.ThrowExceptionForHR( +#if NET8_0_OR_GREATER + Marshal.QueryInterface(inspectable.ThisPtr, in IID, out referenceArrayPtr) +#else + Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in IID), out referenceArrayPtr) +#endif + ); + GC.KeepAlive(inspectable); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)referenceArrayPtr)[6](referenceArrayPtr, &__retval_length, &__retval_data)); + return MarshalBlittable.FromAbiArray((__retval_length, __retval_data)); + } + finally + { + MarshalBlittable.DisposeAbiArray((__retval_length, __retval_data)); + Marshal.Release(referenceArrayPtr); + } + } + + private static unsafe object GetStringValue(IInspectable inspectable) + { + IntPtr referenceArrayPtr = IntPtr.Zero; + int __retval_length = default; + IntPtr __retval_data = default; + try + { + ExceptionHelpers.ThrowExceptionForHR( +#if NET8_0_OR_GREATER + Marshal.QueryInterface(inspectable.ThisPtr, in IID.IID_IReferenceArrayOfString, out referenceArrayPtr) +#else + Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in IID.IID_IReferenceArrayOfString), out referenceArrayPtr) +#endif + ); + GC.KeepAlive(inspectable); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)referenceArrayPtr)[6](referenceArrayPtr, &__retval_length, &__retval_data)); + return MarshalString.FromAbiArray((__retval_length, __retval_data)); + } + finally + { + MarshalString.DisposeAbiArray((__retval_length, __retval_data)); + Marshal.Release(referenceArrayPtr); + } + } + + private static unsafe object GetTypeValue(IInspectable inspectable) + { + IntPtr referenceArrayPtr = IntPtr.Zero; + int __retval_length = default; + IntPtr __retval_data = default; + try + { + ExceptionHelpers.ThrowExceptionForHR( +#if NET8_0_OR_GREATER + Marshal.QueryInterface(inspectable.ThisPtr, in IID.IID_IReferenceArrayOfType, out referenceArrayPtr) +#else + Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in IID.IID_IReferenceArrayOfType), out referenceArrayPtr) +#endif + ); + GC.KeepAlive(inspectable); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)referenceArrayPtr)[6](referenceArrayPtr, &__retval_length, &__retval_data)); + return ABI.System.Type.FromAbiArray((__retval_length, __retval_data)); + } + finally + { + ABI.System.Type.DisposeAbiArray((__retval_length, __retval_data)); + Marshal.Release(referenceArrayPtr); + } + } + + private static unsafe object GetNonBlittableValue(IInspectable inspectable) + { +#if NET && !NET9_0_OR_GREATER + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"Cannot handle array marshalling for non blittable type '{typeof(T)}'."); + } +#endif + + Guid IID = GetIID(); + + IntPtr referenceArrayPtr = IntPtr.Zero; + int __retval_length = default; + IntPtr __retval_data = default; + try + { + ExceptionHelpers.ThrowExceptionForHR( +#if NET8_0_OR_GREATER + Marshal.QueryInterface(inspectable.ThisPtr, in IID, out referenceArrayPtr) +#else + Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in IID), out referenceArrayPtr) +#endif + ); + GC.KeepAlive(inspectable); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)referenceArrayPtr)[6](referenceArrayPtr, &__retval_length, &__retval_data)); + return MarshalNonBlittable.FromAbiArray((__retval_length, __retval_data)); + } + finally + { + MarshalNonBlittable.DisposeAbiArray((__retval_length, __retval_data)); + Marshal.Release(referenceArrayPtr); + } + } + + private static unsafe object GetObjectValue(IInspectable inspectable) + { + IntPtr referenceArrayPtr = IntPtr.Zero; + int __retval_length = default; + IntPtr __retval_data = default; + try + { + ExceptionHelpers.ThrowExceptionForHR( +#if NET8_0_OR_GREATER + Marshal.QueryInterface(inspectable.ThisPtr, in IID.IID_IReferenceArrayOfObject, out referenceArrayPtr) +#else + Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in IID.IID_IReferenceArrayOfObject), out referenceArrayPtr) +#endif + ); + GC.KeepAlive(inspectable); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)referenceArrayPtr)[6](referenceArrayPtr, &__retval_length, &__retval_data)); + return MarshalInspectable.FromAbiArray((__retval_length, __retval_data)); + } + finally + { + MarshalInspectable.DisposeAbiArray((__retval_length, __retval_data)); + Marshal.Release(referenceArrayPtr); + } + } + } } \ No newline at end of file diff --git a/src/WinRT.Runtime/Projections/IReferenceArray.netstandard2.0.cs b/src/WinRT.Runtime/Projections/IReferenceArray.netstandard2.0.cs index 89047f7e6..c7dcdcd02 100644 --- a/src/WinRT.Runtime/Projections/IReferenceArray.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/IReferenceArray.netstandard2.0.cs @@ -1,172 +1,173 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Runtime.InteropServices; -using WinRT; -using WinRT.Interop; - -namespace Windows.Foundation -{ - // Provide a stub definition of IReferenceArray so we have - // a "public" type for the type mapping definition. - // IReferenceArray cannot appear in signatures, so it doesn't need to actually be public. +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.InteropServices; +using WinRT; +using WinRT.Interop; + +namespace Windows.Foundation +{ + // Provide a stub definition of IReferenceArray so we have + // a "public" type for the type mapping definition. + // IReferenceArray cannot appear in signatures, so it doesn't need to actually be public. + [Guid("61C17707-2D65-11E0-9AE8-D48564015472")] + [WindowsRuntimeHelperType(typeof(global::ABI.Windows.Foundation.IReferenceArray<>))] + internal interface IReferenceArray + { + T[] Value { get; } + } +} + +namespace ABI.Windows.Foundation +{ + internal static class BoxedArrayIReferenceArrayImpl + { + private static readonly IReferenceArray.Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + static unsafe BoxedArrayIReferenceArrayImpl() + { + AbiToProjectionVftable = new IReferenceArray.Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, + get_Value_0 = Do_Abi_get_Value_0 + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(BoxedArrayIReferenceArrayImpl), Marshal.SizeOf() + sizeof(IntPtr) * 1); + Marshal.StructureToPtr(AbiToProjectionVftable.IInspectableVftbl, (IntPtr)nativeVftbl, false); + nativeVftbl[6] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.get_Value_0); + + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, out int ____return_value__Size, out IntPtr __return_value__) + { + T[] ____return_value__ = default; + + __return_value__ = default; + ____return_value__Size = default; + + try + { + ____return_value__ = (T[])global::WinRT.ComWrappersSupport.FindObject(thisPtr); + (____return_value__Size, __return_value__) = Marshaler.FromManagedArray(____return_value__); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + + [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] [Guid("61C17707-2D65-11E0-9AE8-D48564015472")] - [WindowsRuntimeHelperType(typeof(global::ABI.Windows.Foundation.IReferenceArray<>))] - internal interface IReferenceArray - { - T[] Value { get; } - } -} - -namespace ABI.Windows.Foundation -{ - internal static class BoxedArrayIReferenceArrayImpl - { - private static readonly IReferenceArray.Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - static unsafe BoxedArrayIReferenceArrayImpl() - { - AbiToProjectionVftable = new IReferenceArray.Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - get_Value_0 = Do_Abi_get_Value_0 - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(BoxedArrayIReferenceArrayImpl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable.IInspectableVftbl, (IntPtr)nativeVftbl, false); - nativeVftbl[6] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.get_Value_0); - - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, out int ____return_value__Size, out IntPtr __return_value__) - { - T[] ____return_value__ = default; - - __return_value__ = default; - ____return_value__Size = default; - - try - { - ____return_value__ = (T[])global::WinRT.ComWrappersSupport.FindObject(thisPtr); - (____return_value__Size, __return_value__) = Marshaler.FromManagedArray(____return_value__); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] - [Guid("61C17707-2D65-11E0-9AE8-D48564015472")] - internal sealed class IReferenceArray : global::Windows.Foundation.IReferenceArray - { - public static IObjectReference CreateMarshaler(object value) - { - if (value is null) - { - return null; - } - return ComWrappersSupport.CreateCCWForObject(value, PIID); + internal sealed class IReferenceArray : global::Windows.Foundation.IReferenceArray + { + public static IObjectReference CreateMarshaler(object value) + { + if (value is null) + { + return null; + } + return ComWrappersSupport.CreateCCWForObject(value, PIID); + } + + public static ObjectReferenceValue CreateMarshaler2(object value) => + ComWrappersSupport.CreateCCWForObjectForMarshaling(value, PIID); + + public static IntPtr GetAbi(IObjectReference m) => m?.ThisPtr ?? IntPtr.Zero; + + public static object FromAbi(IntPtr ptr) + { + if (ptr == IntPtr.Zero) + { + return null; + } + var vftblT = new Vftbl(ptr); + var wrapper = new IReferenceArray(ObjectReference.FromAbi(ptr, vftblT)); + return wrapper.Value; } - public static ObjectReferenceValue CreateMarshaler2(object value) => - ComWrappersSupport.CreateCCWForObjectForMarshaling(value, PIID); - - public static IntPtr GetAbi(IObjectReference m) => m?.ThisPtr ?? IntPtr.Zero; - - public static object FromAbi(IntPtr ptr) - { - if (ptr == IntPtr.Zero) - { - return null; - } - var vftblT = new Vftbl(ptr); - var wrapper = new IReferenceArray(ObjectReference.FromAbi(ptr, vftblT)); - return wrapper.Value; + public static object GetValue(IInspectable inspectable) + { + var array = new IReferenceArray(inspectable.ObjRef); + return array.Value; } - internal static object GetValue(IInspectable inspectable) - { - var array = new IReferenceArray(inspectable.ObjRef); - return array.Value; - } - - public static unsafe void CopyManaged(object o, IntPtr dest) - { - *(IntPtr*)dest.ToPointer() = CreateMarshaler2(o).Detach(); - } - - public static IntPtr FromManaged(object value) - { - if (value is null) - { - return IntPtr.Zero; - } - return CreateMarshaler2(value).Detach(); - } - - public static void DisposeMarshaler(IObjectReference m) { m?.Dispose(); } - public static void DisposeAbi(IntPtr abi) { MarshalInspectable.DisposeAbi(abi); } - - public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IReferenceArray)); - - [Guid("61C17707-2D65-11E0-9AE8-D48564015472")] - public struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - public IReferenceArray_Delegates.get_Value_0 get_Value_0; - public static Guid PIID = GuidGenerator.CreateIID(typeof(IReferenceArray)); - - internal unsafe Vftbl(IntPtr thisPtr) - { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); - get_Value_0 = Marshal.GetDelegateForFunctionPointer(vftbl[6]); - } - } - - public static Guid PIID = Vftbl.PIID; - - public static implicit operator IReferenceArray(IObjectReference obj) => (obj != null) ? new IReferenceArray(obj) : null; - public static implicit operator IReferenceArray(ObjectReference obj) => (obj != null) ? new IReferenceArray(obj) : null; - private readonly ObjectReference _obj; - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public IReferenceArray(IObjectReference obj) : this(obj.As()) { } - public IReferenceArray(ObjectReference obj) - { - _obj = obj; - } - - - public unsafe T[] Value - { - get - { - int __retval_length = default; - IntPtr __retval_data = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Value_0(ThisPtr, out __retval_length, out __retval_data)); - return Marshaler.FromAbiArray((__retval_length, __retval_data)); - } - finally - { - Marshaler.DisposeAbiArray((__retval_length, __retval_data)); - } - } - } - } - - internal static class IReferenceArray_Delegates - { - public unsafe delegate int get_Value_0(IntPtr thisPtr, out int ____return_value__Size, out IntPtr __return_value__); - } -} - + public static unsafe void CopyManaged(object o, IntPtr dest) + { + *(IntPtr*)dest.ToPointer() = CreateMarshaler2(o).Detach(); + } + + public static IntPtr FromManaged(object value) + { + if (value is null) + { + return IntPtr.Zero; + } + return CreateMarshaler2(value).Detach(); + } + + public static void DisposeMarshaler(IObjectReference m) { m?.Dispose(); } + public static void DisposeAbi(IntPtr abi) { MarshalInspectable.DisposeAbi(abi); } + + public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(IReferenceArray)); + + [Guid("61C17707-2D65-11E0-9AE8-D48564015472")] + public struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + public IReferenceArray_Delegates.get_Value_0 get_Value_0; + public static readonly Guid PIID = GuidGenerator.CreateIIDUnsafe(typeof(IReferenceArray)); + + internal unsafe Vftbl(IntPtr thisPtr) + { + var vftblPtr = *(void***)thisPtr; + var vftbl = (IntPtr*)vftblPtr; + IInspectableVftbl = *(IInspectable.Vftbl*)vftblPtr; + get_Value_0 = Marshal.GetDelegateForFunctionPointer(vftbl[6]); + } + } + + public static readonly Guid PIID = Vftbl.PIID; + + public static implicit operator IReferenceArray(IObjectReference obj) => (obj != null) ? new IReferenceArray(obj) : null; + public static implicit operator IReferenceArray(ObjectReference obj) => (obj != null) ? new IReferenceArray(obj) : null; + private readonly ObjectReference _obj; + public IntPtr ThisPtr => _obj.ThisPtr; + public ObjectReference AsInterface() => _obj.As(); + public A As() => _obj.AsType(); + public IReferenceArray(IObjectReference obj) : this(obj.As()) { } + public IReferenceArray(ObjectReference obj) + { + _obj = obj; + } + + + public unsafe T[] Value + { + get + { + int __retval_length = default; + IntPtr __retval_data = default; + try + { + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Value_0(ThisPtr, out __retval_length, out __retval_data)); + GC.KeepAlive(_obj); + return Marshaler.FromAbiArray((__retval_length, __retval_data)); + } + finally + { + Marshaler.DisposeAbiArray((__retval_length, __retval_data)); + } + } + } + } + + internal static class IReferenceArray_Delegates + { + public unsafe delegate int get_Value_0(IntPtr thisPtr, out int ____return_value__Size, out IntPtr __return_value__); + } +} + diff --git a/src/WinRT.Runtime/Projections/IServiceProvider.net5.cs b/src/WinRT.Runtime/Projections/IServiceProvider.net5.cs index 683d5303e..1e7d365a8 100644 --- a/src/WinRT.Runtime/Projections/IServiceProvider.net5.cs +++ b/src/WinRT.Runtime/Projections/IServiceProvider.net5.cs @@ -2,74 +2,84 @@ // Licensed under the MIT License. using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using WinRT; namespace ABI.System -{ - [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] - [Guid("68B3A2DF-8173-539F-B524-C8A2348F5AFB")] - [DynamicInterfaceCastableImplementation] - internal unsafe interface IServiceProvider : global::System.IServiceProvider - { - [Guid("68B3A2DF-8173-539F-B524-C8A2348F5AFB")] - public struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - - public delegate* unmanaged GetService_0; - - public static readonly IntPtr AbiToProjectionVftablePtr; - - static unsafe Vftbl() - { - AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 4); - (*(Vftbl*)AbiToProjectionVftablePtr) = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - GetService_0 = (delegate* unmanaged)&Do_Abi_GetService_0 - }; - } - - [UnmanagedCallersOnly] - private static unsafe int Do_Abi_GetService_0(IntPtr thisPtr, global::ABI.System.Type type, IntPtr* result) - { - object __result = default; - - *result = default; - - try - { - __result = global::WinRT.ComWrappersSupport.FindObject(thisPtr).GetService(global::ABI.System.Type.FromAbi(type)); - *result = MarshalInspectable.FromManaged(__result); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - unsafe object global::System.IServiceProvider.GetService(global::System.Type type) - { +{ +#if EMBED + internal +#else + public +#endif + static class IServiceProviderMethods + { + public static global::System.Guid IID => global::WinRT.Interop.IID.IID_IServiceProvider; + + public static IntPtr AbiToProjectionVftablePtr => IServiceProvider.AbiToProjectionVftablePtr; + + public static unsafe object GetService(IObjectReference obj, global::System.Type type) + { global::ABI.System.Type.Marshaler __type = default; IntPtr __retval = default; try - { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.IServiceProvider).TypeHandle)); - var ThisPtr = _obj.ThisPtr; - __type = global::ABI.System.Type.CreateMarshaler(type); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetService_0(ThisPtr, global::ABI.System.Type.GetAbi(__type), &__retval)); + { + var ThisPtr = obj.ThisPtr; + __type = global::ABI.System.Type.CreateMarshaler(type); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6]( + ThisPtr, + global::ABI.System.Type.GetAbi(__type), + &__retval)); + GC.KeepAlive(obj); return MarshalInspectable.FromAbi(__retval); } finally { global::ABI.System.Type.DisposeMarshaler(__type); MarshalInspectable.DisposeAbi(__retval); - } + } + } + } + + [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] + [Guid("68B3A2DF-8173-539F-B524-C8A2348F5AFB")] + [DynamicInterfaceCastableImplementation] + internal unsafe interface IServiceProvider : global::System.IServiceProvider + { + public static readonly IntPtr AbiToProjectionVftablePtr; + + static unsafe IServiceProvider() + { + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(IServiceProvider), sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[6] = &Do_Abi_GetService_0; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_GetService_0(IntPtr thisPtr, global::ABI.System.Type type, IntPtr* result) + { + object __result = default; + + *result = default; + + try + { + __result = global::WinRT.ComWrappersSupport.FindObject(thisPtr).GetService(global::ABI.System.Type.FromAbi(type)); + *result = MarshalInspectable.FromManaged(__result); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + unsafe object global::System.IServiceProvider.GetService(global::System.Type type) + { + var obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::System.IServiceProvider).TypeHandle); + return IServiceProviderMethods.GetService(obj, type); } } diff --git a/src/WinRT.Runtime/Projections/IServiceProvider.netstandard2.0.cs b/src/WinRT.Runtime/Projections/IServiceProvider.netstandard2.0.cs index b6b4ffc3d..7d05f58d0 100644 --- a/src/WinRT.Runtime/Projections/IServiceProvider.netstandard2.0.cs +++ b/src/WinRT.Runtime/Projections/IServiceProvider.netstandard2.0.cs @@ -30,7 +30,7 @@ public struct Vftbl static unsafe Vftbl() { - AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 4); + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); (*(Vftbl*)AbiToProjectionVftablePtr) = new Vftbl { IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, @@ -79,6 +79,7 @@ public unsafe object GetService(global::System.Type type) { __type = global::ABI.System.Type.CreateMarshaler(type); global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.GetService_0(ThisPtr, global::ABI.System.Type.GetAbi(__type), &__retval)); + GC.KeepAlive(_obj); return MarshalInspectable.FromAbi(__retval); } finally diff --git a/src/WinRT.Runtime/Projections/IStringable.cs b/src/WinRT.Runtime/Projections/IStringable.cs index 341a721d9..53bc3e348 100644 --- a/src/WinRT.Runtime/Projections/IStringable.cs +++ b/src/WinRT.Runtime/Projections/IStringable.cs @@ -17,9 +17,7 @@ internal unsafe struct ManagedIStringableVftbl private delegate* unmanaged[Stdcall] ToString_0 { get => (delegate* unmanaged[Stdcall])_ToString_0; set => _ToString_0 = value; } private static readonly ManagedIStringableVftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - - internal static readonly Guid IID = new(0x96369F54, 0x8EB6, 0x48F0, 0xAB, 0xCE, 0xC1, 0xB2, 0x11, 0xE6, 0x27, 0xC3); + public static readonly IntPtr AbiToProjectionVftablePtr; #if !NET private unsafe delegate int ToStringDelegate(IntPtr thisPtr, IntPtr* value); @@ -36,8 +34,8 @@ static unsafe ManagedIStringableVftbl() _ToString_0 = (delegate* unmanaged)&Do_Abi_ToString_0 #endif }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(ManagedIStringableVftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(ManagedIStringableVftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(ManagedIStringableVftbl*)nativeVftbl = AbiToProjectionVftable; AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; } diff --git a/src/WinRT.Runtime/Projections/KeyValuePair.cs b/src/WinRT.Runtime/Projections/KeyValuePair.cs index b3831507b..cfd623647 100644 --- a/src/WinRT.Runtime/Projections/KeyValuePair.cs +++ b/src/WinRT.Runtime/Projections/KeyValuePair.cs @@ -1,266 +1,509 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using WinRT; - -#pragma warning disable 0169 // warning CS0169: The field '...' is never used -#pragma warning disable 0649 // warning CS0169: Field '...' is never assigned to - -namespace Windows.Foundation.Collections -{ - [Guid("02B51929-C1C4-4A7E-8940-0312B5C18500")] - interface IKeyValuePair - { - K Key { get; } - V Value { get; } - } -} - -namespace ABI.System.Collections.Generic -{ - using global::System; - - [Guid("02B51929-C1C4-4A7E-8940-0312B5C18500")] -#if EMBED - internal -#else - public -#endif - class KeyValuePair : global::Windows.Foundation.Collections.IKeyValuePair - { - public static IObjectReference CreateMarshaler(global::System.Collections.Generic.KeyValuePair obj) => - MarshalInterface>.CreateMarshaler(obj); - - public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.Generic.KeyValuePair obj) => - MarshalInterface>.CreateMarshaler2(obj); - - public static IntPtr GetAbi(IObjectReference objRef) => - objRef?.ThisPtr ?? IntPtr.Zero; - - public static object CreateRcw(IInspectable obj) - { - var pair = new KeyValuePair(obj.As()); - return (object)new global::System.Collections.Generic.KeyValuePair(pair.Key, pair.Value); - } - - public static global::System.Collections.Generic.KeyValuePair FromAbi(IntPtr thisPtr) - { - if (thisPtr == IntPtr.Zero) - { - return default; - } - var pair = new KeyValuePair(KeyValuePair._FromAbi(thisPtr)); - return new global::System.Collections.Generic.KeyValuePair(pair.Key, pair.Value); - } - - public static IntPtr FromManaged(global::System.Collections.Generic.KeyValuePair obj) => - CreateMarshaler2(obj).Detach(); - - internal static unsafe void CopyManaged(global::System.Collections.Generic.KeyValuePair o, IntPtr dest) - { - *(IntPtr*)dest.ToPointer() = CreateMarshaler2(o).Detach(); - } - - internal static MarshalInterfaceHelper>.MarshalerArray CreateMarshalerArray(global::System.Collections.Generic.KeyValuePair[] array) => - MarshalInterfaceHelper>.CreateMarshalerArray2(array, (o) => CreateMarshaler2(o)); - - internal static (int length, IntPtr data) GetAbiArray(object box) => MarshalInterfaceHelper>.GetAbiArray(box); - - internal static global::System.Collections.Generic.KeyValuePair[] FromAbiArray(object box) => - MarshalInterfaceHelper>.FromAbiArray(box, (o) => FromAbi(o)); - - public static (int length, IntPtr data) FromManagedArray(global::System.Collections.Generic.KeyValuePair[] array) => - MarshalInterfaceHelper>.FromManagedArray(array, (o) => FromManaged(o)); - - internal static unsafe void CopyManagedArray(global::System.Collections.Generic.KeyValuePair[] array, IntPtr data) => - MarshalInterfaceHelper>.CopyManagedArray(array, data, (o, dest) => CopyManaged(o, dest)); - - public static void DisposeMarshaler(IObjectReference value) => - MarshalInterfaceHelper>.DisposeMarshaler(value); - - public static void DisposeAbi(IntPtr abi) => - MarshalInterfaceHelper>.DisposeAbi(abi); - - public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(KeyValuePair)); - - internal sealed class ToIKeyValuePair : global::Windows.Foundation.Collections.IKeyValuePair - { - private readonly global::System.Collections.Generic.KeyValuePair _pair; - - public ToIKeyValuePair([In] ref global::System.Collections.Generic.KeyValuePair pair) => _pair = pair; - - public K Key => _pair.Key; - - public V Value => _pair.Value; - } - - internal struct Enumerator : global::System.Collections.Generic.IEnumerator> - { - private readonly global::System.Collections.Generic.IEnumerator> _enum; - - internal Enumerator(global::System.Collections.Generic.IEnumerator> enumerator) => _enum = enumerator; - - public bool MoveNext() => _enum.MoveNext(); - - public global::Windows.Foundation.Collections.IKeyValuePair Current - { - get - { - var current = _enum.Current; - return new ToIKeyValuePair(ref current); - } - } - - object IEnumerator.Current => Current; - - void IEnumerator.Reset() => _enum.Reset(); - - public void Dispose() { } - } - - [Guid("02B51929-C1C4-4A7E-8940-0312B5C18500")] - public struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - public global::System.Delegate get_Key_0; - public global::System.Delegate get_Value_1; - public static Guid PIID = GuidGenerator.CreateIID(typeof(KeyValuePair)); - private static readonly Type get_Key_0_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType.MakeByRefType(), typeof(int) }); - private static readonly Type get_Value_1_Type = Expression.GetDelegateType(new Type[] { typeof(void*), Marshaler.AbiType.MakeByRefType(), typeof(int) }); - - internal unsafe Vftbl(IntPtr thisPtr) - { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); - get_Key_0 = Marshal.GetDelegateForFunctionPointer(vftbl[6], get_Key_0_Type); - get_Value_1 = Marshal.GetDelegateForFunctionPointer(vftbl[7], get_Value_1_Type); - } - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - static unsafe Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - get_Key_0 = global::System.Delegate.CreateDelegate(get_Key_0_Type, typeof(Vftbl).GetMethod("Do_Abi_get_Key_0", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType)), - get_Value_1 = global::System.Delegate.CreateDelegate(get_Value_1_Type, typeof(Vftbl).GetMethod("Do_Abi_get_Value_1", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType)) - }; - var nativeVftbl = (IntPtr*)Marshal.AllocCoTaskMem(Marshal.SizeOf() + sizeof(IntPtr) * 2); - Marshal.StructureToPtr(AbiToProjectionVftable.IInspectableVftbl, (IntPtr)nativeVftbl, false); - nativeVftbl[6] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.get_Key_0); - nativeVftbl[7] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.get_Value_1); - - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - private static ConditionalWeakTable _adapterTable = - new ConditionalWeakTable(); - - private static ToIKeyValuePair FindAdapter(IntPtr thisPtr) - { - var __this = (global::System.Collections.Generic.KeyValuePair)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - return _adapterTable.GetValue(__this, (pair) => new ToIKeyValuePair(ref __this)); - } - - private static unsafe int Do_Abi_get_Key_0(void* thisPtr, out KAbi __return_value__) - { - K ____return_value__ = default; - __return_value__ = default; - try - { - ____return_value__ = FindAdapter(new IntPtr(thisPtr)).Key; - __return_value__ = (KAbi)Marshaler.FromManaged(____return_value__); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - private static unsafe int Do_Abi_get_Value_1(void* thisPtr, out VAbi __return_value__) - { - V ____return_value__ = default; - __return_value__ = default; - try - { - ____return_value__ = FindAdapter(new IntPtr(thisPtr)).Value; - __return_value__ = (VAbi)Marshaler.FromManaged(____return_value__); - } - catch (Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - public static ObjectReference _FromAbi(IntPtr thisPtr) - { - if (thisPtr == IntPtr.Zero) - { - return null; - } - var vftblT = new Vftbl(thisPtr); - return ObjectReference.FromAbi(thisPtr, vftblT); - } - - public static Guid PIID = Vftbl.PIID; - - public static implicit operator KeyValuePair(IObjectReference obj) => (obj != null) ? new KeyValuePair(obj) : null; - public static implicit operator KeyValuePair(ObjectReference obj) => (obj != null) ? new KeyValuePair(obj) : null; - protected readonly ObjectReference _obj; - public IObjectReference ObjRef { get => _obj; } - - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public KeyValuePair(IObjectReference obj) : this(obj.As()) { } - public KeyValuePair(ObjectReference obj) - { - _obj = obj; - } - - public unsafe K Key - { - get - { - var __params = new object[] { ThisPtr, null }; - try - { - _obj.Vftbl.get_Key_0.DynamicInvokeAbi(__params); - return Marshaler.FromAbi(__params[1]); - } - finally - { - Marshaler.DisposeAbi(__params[1]); - } - } - } - - public unsafe V Value - { - get - { - var __params = new object[] { ThisPtr, null }; - try - { - _obj.Vftbl.get_Value_1.DynamicInvokeAbi(__params); - return Marshaler.FromAbi(__params[1]); - } - finally - { - Marshaler.DisposeAbi(__params[1]); - } - } - } - } +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using WinRT; + +#pragma warning disable 0169 // warning CS0169: The field '...' is never used +#pragma warning disable 0649 // warning CS0169: Field '...' is never assigned to + +namespace Windows.Foundation.Collections +{ + [Guid("02B51929-C1C4-4A7E-8940-0312B5C18500")] + interface IKeyValuePair + { + K Key { get; } + V Value { get; } + } +} + +namespace ABI.System.Collections.Generic +{ + using global::System; + using global::System.ComponentModel; + using global::System.Diagnostics.CodeAnalysis; + using global::WinRT.Interop; + +#if EMBED + internal +#else + public +#endif + static class KeyValuePairMethods + { + // These function pointers will be set by IKeyValuePairMethods + // when it is called by the source generated type or by the fallback + // mechanism if the source generated type wasn't used. + internal volatile unsafe static delegate* _GetKey; + internal volatile unsafe static delegate* _GetValue; + internal volatile static bool _RcwHelperInitialized; + + static KeyValuePairMethods() + { + ComWrappersSupport.RegisterHelperType(typeof(global::System.Collections.Generic.KeyValuePair), typeof(global::ABI.System.Collections.Generic.KeyValuePair)); + } + + internal static unsafe void EnsureInitialized() + { +#if NET + // Early return to ensure things are trimmed correctly on NAOT. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. + // Here we just always return true and rely on the AOT generator doing what's needed. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + if (!_RcwHelperInitialized) + { + [DoesNotReturn] + static void ThrowNotInitialized() + { + throw new NotImplementedException( + $"Type '{typeof(global::System.Collections.Generic.KeyValuePair)}' was called without initializing the RCW methods using 'KeyValuePairMethods.InitRcwHelper'. " + + $"Ensure the 'InitRcwHelper' method is called."); + } + + ThrowNotInitialized(); + } + + return; + } +#endif +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + InitRcwHelperFallbackIfNeeded(); +#pragma warning restore IL3050 + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2080", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] + [UnconditionalSuppressMessage("Trimming", "IL2081", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] +#endif + [MethodImpl(MethodImplOptions.NoInlining)] + static void InitRcwHelperFallbackIfNeeded() + { + // Handle the compat scenario where the source generator wasn't used and IDIC hasn't been used yet + // and due to that the function pointers haven't been initialized. + if (!_RcwHelperInitialized) + { + var initRcwHelperFallback = (Func)typeof(KeyValuePairMethods<,,,>).MakeGenericType(typeof(K), Marshaler.AbiType, typeof(V), Marshaler.AbiType). + GetMethod("InitRcwHelperFallback", BindingFlags.NonPublic | BindingFlags.Static). + CreateDelegate(typeof(Func)); + initRcwHelperFallback(); + } + } + } + + internal static unsafe K GetKey(IObjectReference obj) + { + EnsureInitialized(); + return _GetKey(obj); + } + + internal static unsafe V GetValue(IObjectReference obj) + { + EnsureInitialized(); + return _GetValue(obj); + } + + internal static readonly Guid PIID = GuidGenerator.CreateIIDUnsafe(typeof(KeyValuePair)); + public static Guid IID => PIID; + + private static IntPtr abiToProjectionVftablePtr; + public static IntPtr AbiToProjectionVftablePtr => abiToProjectionVftablePtr; + + internal static bool TryInitCCWVtable(IntPtr ptr) + { + return global::System.Threading.Interlocked.CompareExchange(ref abiToProjectionVftablePtr, ptr, IntPtr.Zero) == IntPtr.Zero; + } + + public static K Abi_get_Key_0(IntPtr thisPtr) + { + return KeyValuePair.FindAdapter(thisPtr).Key; + } + + public static V Abi_get_Value_1(IntPtr thisPtr) + { + return KeyValuePair.FindAdapter(thisPtr).Value; + } + } + +#if EMBED + internal +#else + public +#endif + static class KeyValuePairMethods where KAbi : unmanaged where VAbi : unmanaged + { + public unsafe static bool InitRcwHelper( + delegate* getKey, + delegate* getValue) + { + if (KeyValuePairMethods._RcwHelperInitialized) + { + return true; + } + + KeyValuePairMethods._GetKey = getKey; + KeyValuePairMethods._GetValue = getValue; + +#if NET + ComWrappersSupport.RegisterTypedRcwFactory( + typeof(global::System.Collections.Generic.KeyValuePair), + ComWrappersSupport.CreateReferenceCachingFactory(KeyValuePair.CreateRcw)); +#endif + KeyValuePairMethods._RcwHelperInitialized = true; + return true; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private unsafe static bool InitRcwHelperFallback() + { + return InitRcwHelper(&get_Key, &get_Value); + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe K get_Key(IObjectReference obj) + { + var ThisPtr = obj.ThisPtr; + KAbi keyAbi = default; + try + { + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6](ThisPtr, &keyAbi)); + GC.KeepAlive(obj); + return Marshaler.FromAbi(keyAbi); + } + finally + { + Marshaler.DisposeAbi(keyAbi); + } + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe V get_Value(IObjectReference obj) + { + var ThisPtr = obj.ThisPtr; + VAbi valueAbi = default; + try + { + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[7](ThisPtr, &valueAbi)); + GC.KeepAlive(obj); + return Marshaler.FromAbi(valueAbi); + } + finally + { + Marshaler.DisposeAbi(valueAbi); + } + } + + public static unsafe bool InitCcw( + delegate* unmanaged[Stdcall] getKey, + delegate* unmanaged[Stdcall] getValue) + { + if (KeyValuePairMethods.AbiToProjectionVftablePtr != default) + { + return false; + } + +#if NET + var abiToProjectionVftablePtr = (IntPtr)NativeMemory.AllocZeroed((nuint)(sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * 2)); +#else + var abiToProjectionVftablePtr = (IntPtr)Marshal.AllocCoTaskMem((sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * 2)); +#endif + *(IInspectable.Vftbl*)abiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[6] = getKey; + ((delegate* unmanaged[Stdcall]*)abiToProjectionVftablePtr)[7] = getValue; + + if (!KeyValuePairMethods.TryInitCCWVtable(abiToProjectionVftablePtr)) + { +#if NET + NativeMemory.Free((void*)abiToProjectionVftablePtr); +#else + Marshal.FreeCoTaskMem(abiToProjectionVftablePtr); +#endif + return false; + } + + KeyValuePairHelper.TryAddKeyValuePairCCW( + typeof(global::System.Collections.Generic.KeyValuePair), + KeyValuePairMethods.PIID, + KeyValuePairMethods.AbiToProjectionVftablePtr); + + return true; + } + + private static global::System.Delegate[] DelegateCache; + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + internal static unsafe void InitFallbackCCWVtable() + { + InitRcwHelperFallback(); + + Type get_Key_0_Type = Projections.GetAbiDelegateType(new Type[] { typeof(IntPtr), typeof(KAbi*), typeof(int) }); + Type get_Value_1_Type = Projections.GetAbiDelegateType(new Type[] { typeof(IntPtr), typeof(VAbi*), typeof(int) }); + + DelegateCache = new global::System.Delegate[] + { + global::System.Delegate.CreateDelegate(get_Key_0_Type, typeof(KeyValuePairMethods).GetMethod(nameof(Do_Abi_get_Key_0), BindingFlags.NonPublic | BindingFlags.Static)), + global::System.Delegate.CreateDelegate(get_Value_1_Type, typeof(KeyValuePairMethods).GetMethod(nameof(Do_Abi_get_Value_1), BindingFlags.NonPublic | BindingFlags.Static)), + }; + + InitCcw( + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[0]), + (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(DelegateCache[1]) + ); + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_get_Key_0(IntPtr thisPtr, KAbi* __return_value__) + { + K ____return_value__ = default; + *__return_value__ = default; + try + { + ____return_value__ = KeyValuePair.FindAdapter(thisPtr).Key; + *__return_value__ = (KAbi)Marshaler.FromManaged(____return_value__); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_get_Value_1(IntPtr thisPtr, VAbi* __return_value__) + { + V ____return_value__ = default; + *__return_value__ = default; + try + { + ____return_value__ = KeyValuePair.FindAdapter(thisPtr).Value; + *__return_value__ = (VAbi)Marshaler.FromManaged(____return_value__); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + + [Guid("02B51929-C1C4-4A7E-8940-0312B5C18500")] +#if EMBED + internal +#else + public +#endif + class KeyValuePair : global::Windows.Foundation.Collections.IKeyValuePair + { + public static IObjectReference CreateMarshaler(global::System.Collections.Generic.KeyValuePair obj) => + MarshalInterface>.CreateMarshaler(obj); + + public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.Generic.KeyValuePair obj) => + MarshalInterface>.CreateMarshaler2(obj); + + public static IntPtr GetAbi(IObjectReference objRef) => + objRef?.ThisPtr ?? IntPtr.Zero; + + public static object CreateRcw(IInspectable obj) + { + var pair = new KeyValuePair(obj.ObjRef); + return (object)new global::System.Collections.Generic.KeyValuePair(pair.Key, pair.Value); + } + + public static global::System.Collections.Generic.KeyValuePair FromAbi(IntPtr thisPtr) + { + if (thisPtr == IntPtr.Zero) + { + return default; + } + + var pair = new KeyValuePair(_FromAbi(thisPtr)); + return new global::System.Collections.Generic.KeyValuePair(pair.Key, pair.Value); + } + + public static IntPtr FromManaged(global::System.Collections.Generic.KeyValuePair obj) => + CreateMarshaler2(obj).Detach(); + + internal static unsafe void CopyManaged(global::System.Collections.Generic.KeyValuePair o, IntPtr dest) + { + *(IntPtr*)dest.ToPointer() = CreateMarshaler2(o).Detach(); + } + + internal static MarshalInterfaceHelper>.MarshalerArray CreateMarshalerArray(global::System.Collections.Generic.KeyValuePair[] array) => + MarshalInterfaceHelper>.CreateMarshalerArray2(array, (o) => CreateMarshaler2(o)); + + internal static (int length, IntPtr data) GetAbiArray(object box) => MarshalInterfaceHelper>.GetAbiArray(box); + + internal static global::System.Collections.Generic.KeyValuePair[] FromAbiArray(object box) => + MarshalInterfaceHelper>.FromAbiArray(box, (o) => FromAbi(o)); + + public static (int length, IntPtr data) FromManagedArray(global::System.Collections.Generic.KeyValuePair[] array) => + MarshalInterfaceHelper>.FromManagedArray(array, (o) => FromManaged(o)); + + internal static unsafe void CopyManagedArray(global::System.Collections.Generic.KeyValuePair[] array, IntPtr data) => + MarshalInterfaceHelper>.CopyManagedArray(array, data, (o, dest) => CopyManaged(o, dest)); + + public static void CopyAbiArray(global::System.Collections.Generic.KeyValuePair[] array, object box) => MarshalInterfaceHelper>.CopyAbiArray(array, box, FromAbi); + + public static void DisposeMarshaler(IObjectReference value) => + MarshalInterfaceHelper>.DisposeMarshaler(value); + + public static void DisposeAbi(IntPtr abi) => + MarshalInterfaceHelper>.DisposeAbi(abi); + + public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(KeyValuePair)); + + internal sealed class ToIKeyValuePair : global::Windows.Foundation.Collections.IKeyValuePair + { + private readonly global::System.Collections.Generic.KeyValuePair _pair; + + public ToIKeyValuePair([In] ref global::System.Collections.Generic.KeyValuePair pair) => _pair = pair; + + public K Key => _pair.Key; + + public V Value => _pair.Value; + } + + internal struct Enumerator : global::System.Collections.Generic.IEnumerator> + { + private readonly global::System.Collections.Generic.IEnumerator> _enum; + + internal Enumerator(global::System.Collections.Generic.IEnumerator> enumerator) => _enum = enumerator; + + public bool MoveNext() => _enum.MoveNext(); + + public global::Windows.Foundation.Collections.IKeyValuePair Current + { + get + { + var current = _enum.Current; + return new ToIKeyValuePair(ref current); + } + } + + object IEnumerator.Current => Current; + + void IEnumerator.Reset() => _enum.Reset(); + + public void Dispose() { } + } + + public static readonly IntPtr AbiToProjectionVftablePtr; + static KeyValuePair() + { +#if NET + if (RuntimeFeature.IsDynamicCodeCompiled) +#endif + { + // Simple invocation guarded by a direct runtime feature check to help the linker. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + InitFallbackCCWVTableIfNeeded(); +#pragma warning restore IL3050 + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2080", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] + [UnconditionalSuppressMessage("Trimming", "IL2081", Justification = AttributeMessages.AbiTypesNeverHaveConstructors)] +#endif + [MethodImpl(MethodImplOptions.NoInlining)] + static void InitFallbackCCWVTableIfNeeded() + { + if (KeyValuePairMethods.AbiToProjectionVftablePtr == default) + { + // Handle the compat scenario where the source generator wasn't used or IDIC was used. + var initFallbackCCWVtable = (Action)typeof(KeyValuePairMethods<,,,>).MakeGenericType(typeof(K), Marshaler.AbiType, typeof(V), Marshaler.AbiType). + GetMethod("InitFallbackCCWVtable", BindingFlags.NonPublic | BindingFlags.Static). + CreateDelegate(typeof(Action)); + initFallbackCCWVtable(); + } + } + } + + AbiToProjectionVftablePtr = KeyValuePairMethods.AbiToProjectionVftablePtr; + } + + // This is left here for backwards compat purposes where older generated + // projections can be using FindVftblType and using this to cast. + [Guid("02B51929-C1C4-4A7E-8940-0312B5C18500")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + + public static readonly IntPtr AbiToProjectionVftablePtr = ABI.System.Collections.Generic.KeyValuePair.AbiToProjectionVftablePtr; + + public static readonly Guid PIID = ABI.System.Collections.Generic.KeyValuePair.PIID; + } + + private static readonly ConditionalWeakTable _adapterTable = new(); + + internal static ToIKeyValuePair FindAdapter(IntPtr thisPtr) + { + var __this = (global::System.Collections.Generic.KeyValuePair)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + return _adapterTable.GetValue(__this, (pair) => new ToIKeyValuePair(ref __this)); + } + + public static ObjectReference _FromAbi(IntPtr thisPtr) + { + if (thisPtr == IntPtr.Zero) + { + return null; + } + return ObjectReference.FromAbi(thisPtr, PIID); + } + + public static readonly Guid PIID = KeyValuePairMethods.IID; + +#if !NET + public static implicit operator KeyValuePair(IObjectReference obj) => (obj != null) ? new KeyValuePair(obj) : null; + public static implicit operator KeyValuePair(ObjectReference obj) => (obj != null) ? new KeyValuePair(obj) : null; +#endif + protected readonly ObjectReference _obj; + public IObjectReference ObjRef { get => _obj; } + + public IntPtr ThisPtr => _obj.ThisPtr; + + public KeyValuePair(IObjectReference obj) : this(obj.As(PIID)) { } + public KeyValuePair(ObjectReference obj) + { + _obj = obj; + } + + public unsafe K Key + { + get + { + return KeyValuePairMethods.GetKey(_obj); + } + } + + public unsafe V Value + { + get + { + return KeyValuePairMethods.GetValue(_obj); + } + } + } } \ No newline at end of file diff --git a/src/WinRT.Runtime/Projections/NotifyCollectionChangedAction.cs b/src/WinRT.Runtime/Projections/NotifyCollectionChangedAction.cs index b6ccf1bc7..dccf0cc49 100644 --- a/src/WinRT.Runtime/Projections/NotifyCollectionChangedAction.cs +++ b/src/WinRT.Runtime/Projections/NotifyCollectionChangedAction.cs @@ -1,10 +1,15 @@ // Copyright (c) Microsoft Corporation. // Licensed under the MIT License. +using WinRT; + namespace ABI.System.Collections.Specialized { static class NotifyCollectionChangedAction { - public static string GetGuidSignature() => "enum(Microsoft.UI.Xaml.Interop.NotifyCollectionChangedAction;i4)"; + public static string GetGuidSignature() => + FeatureSwitches.UseWindowsUIXamlProjections + ? "enum(Windows.UI.Xaml.Interop.NotifyCollectionChangedAction;i4)" + : "enum(Microsoft.UI.Xaml.Interop.NotifyCollectionChangedAction;i4)"; } } diff --git a/src/WinRT.Runtime/Projections/NotifyCollectionChangedEventArgs.cs b/src/WinRT.Runtime/Projections/NotifyCollectionChangedEventArgs.cs index 448d6f602..7801cc921 100644 --- a/src/WinRT.Runtime/Projections/NotifyCollectionChangedEventArgs.cs +++ b/src/WinRT.Runtime/Projections/NotifyCollectionChangedEventArgs.cs @@ -1,279 +1,215 @@ // Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using ABI.Microsoft.UI.Xaml.Interop; -using System; -using System.ComponentModel; -using System.Runtime.InteropServices; -using WinRT; -using WinRT.Interop; - -namespace ABI.Microsoft.UI.Xaml.Interop -{ - [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] - [Guid("DA049FF2-D2E0-5FE8-8C7B-F87F26060B6F")] - internal sealed unsafe class INotifyCollectionChangedEventArgs - { - [Guid("DA049FF2-D2E0-5FE8-8C7B-F87F26060B6F")] - [StructLayout(LayoutKind.Sequential)] - public struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _get_Action_0; - public delegate* unmanaged[Stdcall] get_Action_0 => (delegate* unmanaged[Stdcall])_get_Action_0; - private void* _get_NewItems_1; - public delegate* unmanaged[Stdcall] get_NewItems_1 => (delegate* unmanaged[Stdcall])_get_NewItems_1; - private void* _get_OldItems_2; - public delegate* unmanaged[Stdcall] get_OldItems_2 => (delegate* unmanaged[Stdcall])_get_OldItems_2; - private void* _get_NewStartingIndex_3; - public delegate* unmanaged[Stdcall] get_NewStartingIndex_3 => (delegate* unmanaged[Stdcall])_get_NewStartingIndex_3; - private void* _get_OldStartingIndex_4; - public delegate* unmanaged[Stdcall] get_OldStartingIndex_4 => (delegate* unmanaged[Stdcall])_get_OldStartingIndex_4; - } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator INotifyCollectionChangedEventArgs(IObjectReference obj) => (obj != null) ? new INotifyCollectionChangedEventArgs(obj) : null; - private readonly ObjectReference _obj; - public IObjectReference ObjRef { get => _obj; } - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public INotifyCollectionChangedEventArgs(IObjectReference obj) : this(obj.As()) { } - internal INotifyCollectionChangedEventArgs(ObjectReference obj) - { - _obj = obj; - } - - public unsafe global::System.Collections.Specialized.NotifyCollectionChangedAction Action - { - get - { - global::System.Collections.Specialized.NotifyCollectionChangedAction __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_Action_0(ThisPtr, &__retval)); - return __retval; - } - } - - public unsafe global::System.Collections.IList NewItems - { - get - { - IntPtr __retval = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_NewItems_1(ThisPtr, &__retval)); - return MarshalInterface.FromAbi(__retval); - } - finally - { - MarshalInterface.DisposeAbi(__retval); - } - } - } - - public unsafe int NewStartingIndex - { - get - { - int __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_NewStartingIndex_3(ThisPtr, &__retval)); - return __retval; - } - } - - public unsafe global::System.Collections.IList OldItems - { - get - { - IntPtr __retval = default; - try - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_OldItems_2(ThisPtr, &__retval)); - return MarshalInterface.FromAbi(__retval); - } - finally - { - MarshalInterface.DisposeAbi(__retval); - } - } - } - - public unsafe int OldStartingIndex - { - get - { - int __retval = default; - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.get_OldStartingIndex_4(ThisPtr, &__retval)); - return __retval; - } - } - } - - [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] - [Guid("5108EBA4-4892-5A20-8374-A96815E0FD27")] - internal sealed unsafe class WinRTNotifyCollectionChangedEventArgsRuntimeClassFactory - { - [Guid("5108EBA4-4892-5A20-8374-A96815E0FD27")] - [StructLayout(LayoutKind.Sequential)] - public struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _CreateInstanceWithAllParameters_0; - public delegate* unmanaged[Stdcall] CreateInstanceWithAllParameters_0 => (delegate* unmanaged[Stdcall])_CreateInstanceWithAllParameters_0; - } - public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator WinRTNotifyCollectionChangedEventArgsRuntimeClassFactory(IObjectReference obj) => (obj != null) ? new WinRTNotifyCollectionChangedEventArgsRuntimeClassFactory(obj) : null; - public static implicit operator WinRTNotifyCollectionChangedEventArgsRuntimeClassFactory(ObjectReference obj) => (obj != null) ? new WinRTNotifyCollectionChangedEventArgsRuntimeClassFactory(obj) : null; - private readonly ObjectReference _obj; - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public WinRTNotifyCollectionChangedEventArgsRuntimeClassFactory(IObjectReference obj) : this(obj.As()) { } - public WinRTNotifyCollectionChangedEventArgsRuntimeClassFactory(ObjectReference obj) - { - _obj = obj; - } - - public unsafe IObjectReference CreateInstanceWithAllParameters(global::System.Collections.Specialized.NotifyCollectionChangedAction action, global::System.Collections.IList newItems, global::System.Collections.IList oldItems, int newIndex, int oldIndex, object baseInterface, out IObjectReference innerInterface) - { - ObjectReferenceValue __newItems = default; - ObjectReferenceValue __oldItems = default; - ObjectReferenceValue __baseInterface = default; - IntPtr __innerInterface = default; - IntPtr __retval = default; - try - { - __newItems = MarshalInterface.CreateMarshaler2(newItems); - __oldItems = MarshalInterface.CreateMarshaler2(oldItems); - __baseInterface = MarshalInspectable.CreateMarshaler2(baseInterface); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.CreateInstanceWithAllParameters_0(ThisPtr, action, MarshalInterface.GetAbi(__newItems), MarshalInterface.GetAbi(__oldItems), newIndex, oldIndex, MarshalInspectable.GetAbi(__baseInterface), out __innerInterface, out __retval)); - innerInterface = ObjectReference.FromAbi(__innerInterface); - return ObjectReference.FromAbi(__retval); - } - finally - { - MarshalInterface.DisposeMarshaler(__newItems); - MarshalInterface.DisposeMarshaler(__oldItems); - MarshalInspectable.DisposeMarshaler(__baseInterface); - MarshalInspectable.DisposeAbi(__innerInterface); - MarshalInspectable.DisposeAbi(__retval); - } +// Licensed under the MIT License. + +using ABI.Microsoft.UI.Xaml.Interop; +using System; +using System.ComponentModel; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using WinRT; +using WinRT.Interop; + +namespace ABI.Microsoft.UI.Xaml.Interop +{ +#if !NET + [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] +#endif + [Guid("5108EBA4-4892-5A20-8374-A96815E0FD27")] + internal sealed unsafe class WinRTNotifyCollectionChangedEventArgsRuntimeClassFactory + { + private readonly IObjectReference _obj; + public IntPtr ThisPtr => _obj.ThisPtr; + + public static readonly WinRTNotifyCollectionChangedEventArgsRuntimeClassFactory Instance = new(); + + private WinRTNotifyCollectionChangedEventArgsRuntimeClassFactory() + { +#if NET + _obj = FeatureSwitches.UseWindowsUIXamlProjections + ? ActivationFactory.Get("Windows.UI.Xaml.Interop.NotifyCollectionChangedEventArgs", IID.IID_WUX_INotifyCollectionChangedEventArgsFactory) + : ActivationFactory.Get("Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventArgs", IID.IID_MUX_INotifyCollectionChangedEventArgsFactory); +#else + _obj = ActivationFactory.Get("Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventArgs", IID.IID_MUX_INotifyCollectionChangedEventArgsFactory); +#endif + } + + public unsafe ObjectReferenceValue CreateInstanceWithAllParameters(global::System.Collections.Specialized.NotifyCollectionChangedAction action, global::System.Collections.IList newItems, global::System.Collections.IList oldItems, int newIndex, int oldIndex) + { + ObjectReferenceValue __newItems = default; + ObjectReferenceValue __oldItems = default; + IntPtr __innerInterface = default; + IntPtr __retval = default; + try + { +#if NET + __newItems = MarshalInterface.CreateMarshaler2(newItems, global::ABI.System.Collections.IListMethods.IID); + __oldItems = MarshalInterface.CreateMarshaler2(oldItems, global::ABI.System.Collections.IListMethods.IID); +#else + __newItems = MarshalInterface.CreateMarshaler2(newItems); + __oldItems = MarshalInterface.CreateMarshaler2(oldItems); +#endif + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6]( + ThisPtr, + action, + MarshalInterface.GetAbi(__newItems), + MarshalInterface.GetAbi(__oldItems), + newIndex, + oldIndex, + IntPtr.Zero, + &__innerInterface, + &__retval)); + GC.KeepAlive(_obj); + return new ObjectReferenceValue(__retval); + } + finally + { + __newItems.Dispose(); + __oldItems.Dispose(); + MarshalInspectable.DisposeAbi(__innerInterface); + } + } + } +} + +namespace ABI.System.Collections.Specialized +{ + [EditorBrowsable(EditorBrowsableState.Never)] + [StructLayout(LayoutKind.Sequential)] + [WuxMuxProjectedType] +#if EMBED + internal +#else + public +#endif + struct NotifyCollectionChangedEventArgs + { + private static ref readonly Guid Interface_IID => ref FeatureSwitches.UseWindowsUIXamlProjections + ? ref IID.IID_WUX_INotifyCollectionChangedEventArgs + : ref IID.IID_MUX_INotifyCollectionChangedEventArgs; + + public static IObjectReference CreateMarshaler(global::System.Collections.Specialized.NotifyCollectionChangedEventArgs value) + { + if (value is null) + { + return null; + } + + ObjectReferenceValue _value = default; + try + { + _value = CreateMarshaler2(value); + return ObjectReference.FromAbi(_value.GetAbi(), IID.IID_IUnknown); + } + finally + { + _value.Dispose(); + } + } + + public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.Specialized.NotifyCollectionChangedEventArgs value) + { + if (value is null) + { + return new ObjectReferenceValue(); + } + + return WinRTNotifyCollectionChangedEventArgsRuntimeClassFactory.Instance.CreateInstanceWithAllParameters(value.Action, value.NewItems, value.OldItems, value.NewStartingIndex, value.OldStartingIndex); } - public unsafe ObjectReferenceValue CreateInstanceWithAllParameters(global::System.Collections.Specialized.NotifyCollectionChangedAction action, global::System.Collections.IList newItems, global::System.Collections.IList oldItems, int newIndex, int oldIndex) - { - ObjectReferenceValue __newItems = default; - ObjectReferenceValue __oldItems = default; - IntPtr __innerInterface = default; - IntPtr __retval = default; - try - { - __newItems = MarshalInterface.CreateMarshaler2(newItems); - __oldItems = MarshalInterface.CreateMarshaler2(oldItems); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.CreateInstanceWithAllParameters_0(ThisPtr, action, MarshalInterface.GetAbi(__newItems), MarshalInterface.GetAbi(__oldItems), newIndex, oldIndex, IntPtr.Zero, out __innerInterface, out __retval)); - return new ObjectReferenceValue(__retval); - } - finally - { - __newItems.Dispose(); - __oldItems.Dispose(); - MarshalInspectable.DisposeAbi(__innerInterface); - } - } - } -} - -namespace ABI.System.Collections.Specialized -{ - [EditorBrowsable(EditorBrowsableState.Never)] - [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - struct NotifyCollectionChangedEventArgs - { - private sealed class ActivationFactory : BaseActivationFactory - { - public ActivationFactory() : base("Microsoft.UI.Xaml.Interop", "Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventArgs") - { + public static IntPtr GetAbi(IObjectReference m) => m?.ThisPtr ?? IntPtr.Zero; + + public static global::System.Collections.Specialized.NotifyCollectionChangedEventArgs FromAbi(IntPtr ptr) + { + if (ptr == IntPtr.Zero) + { + return null; + } + + return CreateNotifyCollectionChangedEventArgs(ptr); + } + + private static unsafe global::System.Collections.Specialized.NotifyCollectionChangedEventArgs CreateNotifyCollectionChangedEventArgs(IntPtr ptr) + { + IntPtr thisPtr = default; + global::System.Collections.Specialized.NotifyCollectionChangedAction action = default; + IntPtr newItems = default; + IntPtr oldItems = default; + int newStartingIndex = default; + int oldStartingIndex = default; + + try + { + // Call can come from CreateObject which means it might not be on the right interface. + global::WinRT.ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(ptr, ref Unsafe.AsRef(in Interface_IID), out thisPtr)); + + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[6](thisPtr, &action)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[7](thisPtr, &newItems)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[8](thisPtr, &oldItems)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[9](thisPtr, &newStartingIndex)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[10](thisPtr, &oldStartingIndex)); + return CreateNotifyCollectionChangedEventArgs( + action, + MarshalInterface.FromAbi(newItems), + MarshalInterface.FromAbi(oldItems), + newStartingIndex, + oldStartingIndex); + } + finally + { + MarshalInterface.DisposeAbi(newItems); + MarshalInterface.DisposeAbi(oldItems); + MarshalExtensions.ReleaseIfNotNull(thisPtr); } + } + + private static global::System.Collections.Specialized.NotifyCollectionChangedEventArgs CreateNotifyCollectionChangedEventArgs( + global::System.Collections.Specialized.NotifyCollectionChangedAction action, + global::System.Collections.IList newItems, + global::System.Collections.IList oldItems, + int newStartingIndex, + int oldStartingIndex) => + action switch + { + global::System.Collections.Specialized.NotifyCollectionChangedAction.Add => new global::System.Collections.Specialized.NotifyCollectionChangedEventArgs(action, newItems, newStartingIndex), + global::System.Collections.Specialized.NotifyCollectionChangedAction.Remove => new global::System.Collections.Specialized.NotifyCollectionChangedEventArgs(action, oldItems, oldStartingIndex), + global::System.Collections.Specialized.NotifyCollectionChangedAction.Replace => new global::System.Collections.Specialized.NotifyCollectionChangedEventArgs(action, newItems, oldItems, newStartingIndex), + global::System.Collections.Specialized.NotifyCollectionChangedAction.Move => new global::System.Collections.Specialized.NotifyCollectionChangedEventArgs(action, newItems, newStartingIndex, oldStartingIndex), + global::System.Collections.Specialized.NotifyCollectionChangedAction.Reset => new global::System.Collections.Specialized.NotifyCollectionChangedEventArgs(global::System.Collections.Specialized.NotifyCollectionChangedAction.Reset), + _ => throw new ArgumentException(), + }; - internal static WinRTNotifyCollectionChangedEventArgsRuntimeClassFactory Instance = - new ActivationFactory()._As(); - } - - public static IObjectReference CreateMarshaler(global::System.Collections.Specialized.NotifyCollectionChangedEventArgs value) - { - if (value is null) - { - return null; - } - - return ActivationFactory.Instance.CreateInstanceWithAllParameters(value.Action, value.NewItems, value.OldItems, value.NewStartingIndex, value.OldStartingIndex, null, out _); + public static unsafe void CopyManaged(global::System.Collections.Specialized.NotifyCollectionChangedEventArgs o, IntPtr dest) + { + *(IntPtr*)dest.ToPointer() = CreateMarshaler2(o).Detach(); + } + + public static IntPtr FromManaged(global::System.Collections.Specialized.NotifyCollectionChangedEventArgs value) + { + if (value is null) + { + return IntPtr.Zero; + } + return CreateMarshaler2(value).Detach(); } - public static ObjectReferenceValue CreateMarshaler2(global::System.Collections.Specialized.NotifyCollectionChangedEventArgs value) - { - if (value is null) - { - return new ObjectReferenceValue(); - } - - return ActivationFactory.Instance.CreateInstanceWithAllParameters(value.Action, value.NewItems, value.OldItems, value.NewStartingIndex, value.OldStartingIndex); - } - - public static IntPtr GetAbi(IObjectReference m) => m?.ThisPtr ?? IntPtr.Zero; - - public static global::System.Collections.Specialized.NotifyCollectionChangedEventArgs FromAbi(IntPtr ptr) - { - if (ptr == IntPtr.Zero) - { - return null; - } - - INotifyCollectionChangedEventArgs args = INotifyCollectionChangedEventArgs.FromAbi(ptr); - return CreateNotifyCollectionChangedEventArgs(args.Action, args.NewItems, args.OldItems, args.NewStartingIndex, args.OldStartingIndex); - } - - private static global::System.Collections.Specialized.NotifyCollectionChangedEventArgs CreateNotifyCollectionChangedEventArgs( - global::System.Collections.Specialized.NotifyCollectionChangedAction action, - global::System.Collections.IList newItems, - global::System.Collections.IList oldItems, - int newStartingIndex, - int oldStartingIndex) => - action switch - { - global::System.Collections.Specialized.NotifyCollectionChangedAction.Add => new global::System.Collections.Specialized.NotifyCollectionChangedEventArgs(action, newItems, newStartingIndex), - global::System.Collections.Specialized.NotifyCollectionChangedAction.Remove => new global::System.Collections.Specialized.NotifyCollectionChangedEventArgs(action, oldItems, oldStartingIndex), - global::System.Collections.Specialized.NotifyCollectionChangedAction.Replace => new global::System.Collections.Specialized.NotifyCollectionChangedEventArgs(action, newItems, oldItems, newStartingIndex), - global::System.Collections.Specialized.NotifyCollectionChangedAction.Move => new global::System.Collections.Specialized.NotifyCollectionChangedEventArgs(action, newItems, newStartingIndex, oldStartingIndex), - global::System.Collections.Specialized.NotifyCollectionChangedAction.Reset => new global::System.Collections.Specialized.NotifyCollectionChangedEventArgs(global::System.Collections.Specialized.NotifyCollectionChangedAction.Reset), - _ => throw new ArgumentException(), - }; - - public static unsafe void CopyManaged(global::System.Collections.Specialized.NotifyCollectionChangedEventArgs o, IntPtr dest) - { - *(IntPtr*)dest.ToPointer() = CreateMarshaler2(o).Detach(); - } - - public static IntPtr FromManaged(global::System.Collections.Specialized.NotifyCollectionChangedEventArgs value) - { - if (value is null) - { - return IntPtr.Zero; - } - return CreateMarshaler2(value).Detach(); - } - - public static void DisposeMarshaler(IObjectReference m) { m?.Dispose(); } - public static void DisposeAbi(IntPtr abi) { MarshalInspectable.DisposeAbi(abi); } - - public static string GetGuidSignature() - { - return "rc(Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventArgs;{4cf68d33-e3f2-4964-b85e-945b4f7e2f21})"; - } - } -} + public static void DisposeMarshaler(IObjectReference m) { m?.Dispose(); } + public static void DisposeAbi(IntPtr abi) { MarshalInspectable.DisposeAbi(abi); } + + public static unsafe MarshalInterfaceHelper.MarshalerArray CreateMarshalerArray(global::System.Collections.Specialized.NotifyCollectionChangedEventArgs[] array) => MarshalInterfaceHelper.CreateMarshalerArray2(array, CreateMarshaler2); + public static (int length, IntPtr data) GetAbiArray(object box) => MarshalInterfaceHelper.GetAbiArray(box); + public static unsafe global::System.Collections.Specialized.NotifyCollectionChangedEventArgs[] FromAbiArray(object box) => MarshalInterfaceHelper.FromAbiArray(box, FromAbi); + public static void CopyAbiArray(global::System.Collections.Specialized.NotifyCollectionChangedEventArgs[] array, object box) => MarshalInterfaceHelper.CopyAbiArray(array, box, FromAbi); + public static (int length, IntPtr data) FromManagedArray(global::System.Collections.Specialized.NotifyCollectionChangedEventArgs[] array) => MarshalInterfaceHelper.FromManagedArray(array, FromManaged); + public static void DisposeMarshalerArray(MarshalInterfaceHelper.MarshalerArray array) => MarshalInterfaceHelper.DisposeMarshalerArray(array); + public static unsafe void DisposeAbiArray(object box) => MarshalInspectable.DisposeAbiArray(box); + + public static string GetGuidSignature() + { + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + return "rc(Windows.UI.Xaml.Interop.NotifyCollectionChangedEventArgs;{4cf68d33-e3f2-4964-b85e-945b4f7e2f21})"; + } + return "rc(Microsoft.UI.Xaml.Interop.NotifyCollectionChangedEventArgs;{da049ff2-d2e0-5fe8-8c7b-f87f26060b6f})"; + } + } +} diff --git a/src/WinRT.Runtime/Projections/NotifyCollectionChangedEventHandler.cs b/src/WinRT.Runtime/Projections/NotifyCollectionChangedEventHandler.cs index e1ab5163d..545230b20 100644 --- a/src/WinRT.Runtime/Projections/NotifyCollectionChangedEventHandler.cs +++ b/src/WinRT.Runtime/Projections/NotifyCollectionChangedEventHandler.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; @@ -13,6 +13,7 @@ namespace ABI.System.Collections.Specialized [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] [Guid("8B0909DC-2005-5D93-BF8A-725F017BAA8D")] + [WuxMuxProjectedType] #if EMBED internal #else @@ -38,35 +39,38 @@ static unsafe NotifyCollectionChangedEventHandler() Invoke = (IntPtr)(delegate* unmanaged[Stdcall])&Do_Abi_Invoke #endif }; - var nativeVftbl = ComWrappersSupport.AllocateVtableMemory(typeof(NotifyCollectionChangedEventHandler), Marshal.SizeOf()); - Marshal.StructureToPtr(AbiToProjectionVftable, nativeVftbl, false); + var nativeVftbl = ComWrappersSupport.AllocateVtableMemory(typeof(NotifyCollectionChangedEventHandler), sizeof(global::WinRT.Interop.IDelegateVftbl)); + *(global::WinRT.Interop.IDelegateVftbl*)nativeVftbl = AbiToProjectionVftable; AbiToProjectionVftablePtr = nativeVftbl; + ComWrappersSupport.RegisterDelegateFactory(typeof(global::System.Collections.Specialized.NotifyCollectionChangedEventHandler), CreateRcw); } public static global::System.Delegate AbiInvokeDelegate { get; } - private static readonly Guid IID = new(0x8B0909DC, 0x2005, 0x5D93, 0xBF, 0x8A, 0x72, 0x5F, 0x01, 0x7B, 0xAA, 0x8D); + public static Guid IID => FeatureSwitches.UseWindowsUIXamlProjections + ? global::WinRT.Interop.IID.IID_WUX_NotifyCollectionChangedEventHandler + : global::WinRT.Interop.IID.IID_MUX_NotifyCollectionChangedEventHandler; public static unsafe IObjectReference CreateMarshaler(global::System.Collections.Specialized.NotifyCollectionChangedEventHandler managedDelegate) => - managedDelegate is null ? null : MarshalDelegate.CreateMarshaler(managedDelegate, IID); - + managedDelegate is null ? null : MarshalDelegate.CreateMarshaler(managedDelegate, IID); + public static unsafe ObjectReferenceValue CreateMarshaler2(global::System.Collections.Specialized.NotifyCollectionChangedEventHandler managedDelegate) => MarshalDelegate.CreateMarshaler2(managedDelegate, IID); public static IntPtr GetAbi(IObjectReference value) => MarshalInterfaceHelper.GetAbi(value); public static unsafe global::System.Collections.Specialized.NotifyCollectionChangedEventHandler FromAbi(IntPtr nativeDelegate) - { + { return MarshalDelegate.FromAbi(nativeDelegate); } - public static global::System.Collections.Specialized.NotifyCollectionChangedEventHandler CreateRcw(IntPtr ptr) - { - return new global::System.Collections.Specialized.NotifyCollectionChangedEventHandler(new NativeDelegateWrapper(ComWrappersSupport.GetObjectReferenceForInterface(ptr, IID)).Invoke); + public static global::System.Collections.Specialized.NotifyCollectionChangedEventHandler CreateRcw(IntPtr ptr) + { + return new global::System.Collections.Specialized.NotifyCollectionChangedEventHandler(new NativeDelegateWrapper(ComWrappersSupport.GetObjectReferenceForInterface(ptr, IID)).Invoke); } - [global::WinRT.ObjectReferenceWrapper(nameof(_nativeDelegate))] #if !NET + [global::WinRT.ObjectReferenceWrapper(nameof(_nativeDelegate))] private sealed class NativeDelegateWrapper #else private sealed class NativeDelegateWrapper : IWinRTObject @@ -114,6 +118,7 @@ public unsafe void Invoke(object sender, global::System.Collections.Specialized. __sender = MarshalInspectable.CreateMarshaler2(sender); __e = global::ABI.System.Collections.Specialized.NotifyCollectionChangedEventArgs.CreateMarshaler2(e); global::WinRT.ExceptionHelpers.ThrowExceptionForHR(abiInvoke(ThisPtr, MarshalInspectable.GetAbi(__sender), MarshalInspectable.GetAbi(__e))); + GC.KeepAlive(_nativeDelegate); } finally { @@ -131,13 +136,21 @@ public static IntPtr FromManaged(global::System.Collections.Specialized.NotifyCo public static void DisposeAbi(IntPtr abi) => MarshalInterfaceHelper.DisposeAbi(abi); + public static unsafe MarshalInterfaceHelper.MarshalerArray CreateMarshalerArray(global::System.Collections.Specialized.NotifyCollectionChangedEventHandler[] array) => MarshalInterfaceHelper.CreateMarshalerArray2(array, CreateMarshaler2); + public static (int length, IntPtr data) GetAbiArray(object box) => MarshalInterfaceHelper.GetAbiArray(box); + public static unsafe global::System.Collections.Specialized.NotifyCollectionChangedEventHandler[] FromAbiArray(object box) => MarshalInterfaceHelper.FromAbiArray(box, FromAbi); + public static void CopyAbiArray(global::System.Collections.Specialized.NotifyCollectionChangedEventHandler[] array, object box) => MarshalInterfaceHelper.CopyAbiArray(array, box, FromAbi); + public static (int length, IntPtr data) FromManagedArray(global::System.Collections.Specialized.NotifyCollectionChangedEventHandler[] array) => MarshalInterfaceHelper.FromManagedArray(array, FromManaged); + public static void DisposeMarshalerArray(MarshalInterfaceHelper.MarshalerArray array) => MarshalInterfaceHelper.DisposeMarshalerArray(array); + public static unsafe void DisposeAbiArray(object box) => MarshalInspectable.DisposeAbiArray(box); + #if NET [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] #endif private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr sender, IntPtr e) { try - { + { #if NET var invoke = ComWrappersSupport.FindObject(thisPtr); invoke.Invoke(MarshalInspectable.FromAbi(sender), global::ABI.System.Collections.Specialized.NotifyCollectionChangedEventArgs.FromAbi(e)); @@ -155,40 +168,63 @@ private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr sender, IntPtr e) } return 0; } + +#if NET + [SkipLocalsInit] + internal static ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + { + Span entries = stackalloc ComWrappers.ComInterfaceEntry[3]; + int count = 0; + + entries[count++] = new ComWrappers.ComInterfaceEntry + { + IID = IID, + Vtable = AbiToProjectionVftablePtr + }; + + if (FeatureSwitches.EnableIReferenceSupport) + { + entries[count++] = new ComWrappers.ComInterfaceEntry + { + IID = global::WinRT.Interop.IID.IID_IPropertyValue, + Vtable = ABI.Windows.Foundation.ManagedIPropertyValueImpl.AbiToProjectionVftablePtr + }; + + entries[count++] = new ComWrappers.ComInterfaceEntry + { + IID = global::ABI.System.Nullable_NotifyCollectionChangedEventHandler.IID, + Vtable = global::ABI.System.Nullable_NotifyCollectionChangedEventHandler.AbiToProjectionVftablePtr + }; + } + + return entries.Slice(0, count).ToArray(); + } +#endif } - internal sealed unsafe class NotifyCollectionChangedEventSource : EventSource + internal sealed unsafe class NotifyCollectionChangedEventHandlerEventSource : global::ABI.WinRT.Interop.EventSource { - internal NotifyCollectionChangedEventSource(IObjectReference obj, - delegate* unmanaged[Stdcall] addHandler, - delegate* unmanaged[Stdcall] removeHandler) - : base(obj, addHandler, removeHandler) + internal NotifyCollectionChangedEventHandlerEventSource(IObjectReference objectReference, int vtableIndexForAddHandler) + : base(objectReference, vtableIndexForAddHandler) { } protected override ObjectReferenceValue CreateMarshaler(global::System.Collections.Specialized.NotifyCollectionChangedEventHandler del) => NotifyCollectionChangedEventHandler.CreateMarshaler2(del); - protected override State CreateEventState() => - new EventState(_obj.ThisPtr, _index); + protected override global::ABI.WinRT.Interop.EventSourceState CreateEventSourceState() => + new EventState(ObjectReference.ThisPtr, Index); - private sealed class EventState : State + private sealed class EventState : global::ABI.WinRT.Interop.EventSourceState { public EventState(IntPtr obj, int index) : base(obj, index) { } - protected override Delegate GetEventInvoke() + protected override global::System.Collections.Specialized.NotifyCollectionChangedEventHandler GetEventInvoke() { - global::System.Collections.Specialized.NotifyCollectionChangedEventHandler handler = - (global::System.Object obj, global::System.Collections.Specialized.NotifyCollectionChangedEventArgs e) => - { - var localDel = (global::System.Collections.Specialized.NotifyCollectionChangedEventHandler) del; - if (localDel != null) - localDel.Invoke(obj, e); - }; - return handler; + return (obj, e) => targetDelegate?.Invoke(obj, e); } } } diff --git a/src/WinRT.Runtime/Projections/Nullable.cs b/src/WinRT.Runtime/Projections/Nullable.cs index 6bddb5b33..470fd8ba2 100644 --- a/src/WinRT.Runtime/Projections/Nullable.cs +++ b/src/WinRT.Runtime/Projections/Nullable.cs @@ -1,1638 +1,2568 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Linq.Expressions; -using System.Reflection; -using System.Runtime.InteropServices; -using WinRT; -using WinRT.Interop; - -namespace ABI.Windows.Foundation +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Concurrent; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using WinRT; +using WinRT.Interop; + +namespace ABI.Windows.Foundation { - using System; - - internal static class BoxedValueIReferenceImpl - { - private static global::ABI.System.Nullable.Vftbl AbiToProjectionVftable; + using System; + + internal static class BoxedValueIReferenceImpl + { + private static global::ABI.System.Nullable.Vftbl AbiToProjectionVftable; + public static IntPtr AbiToProjectionVftablePtr; + +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode", Justification = "ABI types used with MakeGenericType are not reflected on.")] +#endif + static unsafe BoxedValueIReferenceImpl() + { + AbiToProjectionVftable = new global::ABI.System.Nullable.Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, + get_Value_0 = GetValueDelegateForAbi(out IntPtr nativePtr) + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(BoxedValueIReferenceImpl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(global::WinRT.IInspectable.Vftbl*)nativeVftbl = AbiToProjectionVftable.IInspectableVftbl; + nativeVftbl[6] = nativePtr; + + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + + // This method is only for all blittable types. Note: this method could be an overload of + // the generic one below, but using a different one to simplify the reflection lookup in the + // fallback case below (so we can use GetMethod and find just the single match we need). + private static unsafe int Do_Abi_get_Value_0_Blittable(void* thisPtr, void* result) + { + if (result is null) + { + // Immediately return E_POINTER if the target is null + return unchecked((int)0x80004003); + } + + try + { + T unboxedValue = (T)global::WinRT.ComWrappersSupport.FindObject(new IntPtr(thisPtr)); + + Unsafe.WriteUnaligned(result, unboxedValue); + + return 0; + } + catch (global::System.Exception __exception__) + { + Unsafe.WriteUnaligned(result, default); + + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + } + + private static unsafe int Do_Abi_get_Value_0_DateTimeOffset(void* thisPtr, global::ABI.System.DateTimeOffset* result) + { + if (result is null) + { + return unchecked((int)0x80004003); + } + + try + { + T unboxedValue = (T)global::WinRT.ComWrappersSupport.FindObject(new IntPtr(thisPtr)); + + Unsafe.WriteUnaligned(result, global::ABI.System.DateTimeOffset.FromManaged(Unsafe.As(ref unboxedValue))); + + return 0; + } + catch (global::System.Exception __exception__) + { + Unsafe.WriteUnaligned(result, default); + + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + } + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.NotSupportedIfDynamicCodeIsNotAvailable)] +#endif + private static unsafe int Do_Abi_get_Value_0(void* thisPtr, out TAbi __return_value__) + { + T ____return_value__ = default; + + __return_value__ = default; + + try + { + ____return_value__ = (T)global::WinRT.ComWrappersSupport.FindObject(new IntPtr(thisPtr)); + __return_value__ = (TAbi)Marshaler.FromManaged(____return_value__); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + internal static unsafe Delegate GetValueDelegateForAbi(out IntPtr nativePtr) + { + if (typeof(T) == typeof(int) || + typeof(T) == typeof(byte) || + typeof(T) == typeof(bool) || + typeof(T) == typeof(sbyte) || + typeof(T) == typeof(short) || + typeof(T) == typeof(ushort) || + typeof(T) == typeof(char) || + typeof(T) == typeof(uint) || + typeof(T) == typeof(long) || + typeof(T) == typeof(ulong) || + typeof(T) == typeof(float) || + typeof(T) == typeof(double) || + typeof(T) == typeof(Guid) || + typeof(T) == typeof(global::System.TimeSpan) || + typeof(T) == typeof(global::Windows.Foundation.Point) || + typeof(T) == typeof(global::Windows.Foundation.Rect) || + typeof(T) == typeof(global::Windows.Foundation.Size) || + typeof(T) == typeof(global::System.Numerics.Matrix3x2) || + typeof(T) == typeof(global::System.Numerics.Matrix4x4) || + typeof(T) == typeof(global::System.Numerics.Plane) || + typeof(T) == typeof(global::System.Numerics.Quaternion) || + typeof(T) == typeof(global::System.Numerics.Vector2) || + typeof(T) == typeof(global::System.Numerics.Vector3) || + typeof(T) == typeof(global::System.Numerics.Vector4) || + (typeof(T).IsEnum && Enum.GetUnderlyingType(typeof(T)) == typeof(int)) || + (typeof(T).IsEnum && Enum.GetUnderlyingType(typeof(T)) == typeof(uint))) + { + global::ABI.System.Nullable_Delegates.GetValueDelegateAbi stub = new(Do_Abi_get_Value_0_Blittable); + + nativePtr = Marshal.GetFunctionPointerForDelegate(stub); + + return stub; + } + + if (typeof(T) == typeof(global::System.DateTimeOffset)) + { + global::ABI.System.Nullable_Delegates.GetValueDelegateAbiDateTimeOffset stub = new(Do_Abi_get_Value_0_DateTimeOffset); + + nativePtr = Marshal.GetFunctionPointerForDelegate(stub); + + return stub; + } + +#if NET + // Only when not on NAOT, use LINQ expressions to get the delegate type. + // This is not safe on AOT, so in that case we just can't get a delegate. + if (RuntimeFeature.IsDynamicCodeCompiled) +#endif + { +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + global::ABI.System.Nullable.Vftbl.get_Value_0_Type ??= Projections.GetAbiDelegateType(typeof(void*), Marshaler.AbiType.MakeByRefType(), typeof(int)); + + Delegate stub = Delegate.CreateDelegate( + global::ABI.System.Nullable.Vftbl.get_Value_0_Type, + typeof(BoxedValueIReferenceImpl).GetMethod(nameof(Do_Abi_get_Value_0), BindingFlags.NonPublic | BindingFlags.Static)!.MakeGenericMethod(Marshaler.AbiType)); + + nativePtr = Marshal.GetFunctionPointerForDelegate(stub); + + return stub; +#pragma warning restore IL3050 + } + + throw new NotSupportedException($"Failed to get the marshalling delegate for BoxedValueIReferenceImpl`1 with type '{typeof(T)}'."); + } + } + + internal static class BoxedValueIReferenceImpl + where T : struct + where TAbi : unmanaged + { public static IntPtr AbiToProjectionVftablePtr; + private static readonly global::ABI.System.Nullable_Delegates.GetValueDelegateAbi GetValue; + + static unsafe BoxedValueIReferenceImpl() + { + if (typeof(T) == typeof(TAbi)) + { + GetValue = Do_Abi_get_Value_0_Blittable; + } + else + { + GetValue = Do_Abi_get_Value_0; + } + + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(BoxedValueIReferenceImpl), sizeof(IInspectable.Vftbl) + sizeof(IntPtr)); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((IntPtr*)AbiToProjectionVftablePtr)[6] = Marshal.GetFunctionPointerForDelegate(GetValue); + } + + private static unsafe int Do_Abi_get_Value_0(void* thisPtr, void* __return_value__) + { + *(TAbi*)__return_value__ = default; + + try + { + T ____return_value__ = (T)global::WinRT.ComWrappersSupport.FindObject(new IntPtr(thisPtr)); + *(TAbi*)__return_value__ = (TAbi)MarshalNonBlittable.FromManaged(____return_value__); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe int Do_Abi_get_Value_0_Blittable(void* thisPtr, void* __return_value__) + { + *(TAbi*)__return_value__ = default; + + try + { + *(TAbi*)__return_value__ = (TAbi)global::WinRT.ComWrappersSupport.FindObject(new IntPtr(thisPtr)); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } +} + +namespace ABI.System +{ +#if !NET + [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] +#endif + [Guid("61C17706-2D65-11E0-9AE8-D48564015472")] +#if EMBED + internal +#else + public +#endif + class Nullable + { + public static IObjectReference CreateMarshaler(object value) + { + return value is null ? null : ComWrappersSupport.CreateCCWForObject(value, PIID); + } + + public static ObjectReferenceValue CreateMarshaler2(object value) => + ComWrappersSupport.CreateCCWForObjectForMarshaling(value, PIID); + + public static IntPtr GetAbi(IObjectReference m) => m?.ThisPtr ?? IntPtr.Zero; + + public static object FromAbi(IntPtr ptr) + { + if (ptr == IntPtr.Zero) + { + return null; + } + var vftblT = new Vftbl(ptr); + var wrapper = new Nullable(ObjectReference.FromAbi(ptr, vftblT, PIID)); + return wrapper.Value; + } + + public static unsafe void CopyManaged(object o, IntPtr dest) + { + *(IntPtr*)dest.ToPointer() = CreateMarshaler2(o).Detach(); + } + + public static IntPtr FromManaged(object value) + { + if (value is null) + { + return IntPtr.Zero; + } + return CreateMarshaler2(value).Detach(); + } + + public static void DisposeMarshaler(IObjectReference m) { m?.Dispose(); } + public static void DisposeAbi(IntPtr abi) { MarshalInspectable.DisposeAbi(abi); } + + public static string GetGuidSignature() => CreateGuidSignature(); + + private static string CreateGuidSignature() + { + if (typeof(T) == typeof(int)) return Nullable_int.GetGuidSignature(); + if (typeof(T) == typeof(byte)) return Nullable_byte.GetGuidSignature(); + if (typeof(T) == typeof(bool)) return Nullable_bool.GetGuidSignature(); + if (typeof(T) == typeof(sbyte)) return Nullable_sbyte.GetGuidSignature(); + if (typeof(T) == typeof(short)) return Nullable_short.GetGuidSignature(); + if (typeof(T) == typeof(ushort)) return Nullable_ushort.GetGuidSignature(); + if (typeof(T) == typeof(char)) return Nullable_char.GetGuidSignature(); + if (typeof(T) == typeof(uint)) return Nullable_uint.GetGuidSignature(); + if (typeof(T) == typeof(long)) return Nullable_long.GetGuidSignature(); + if (typeof(T) == typeof(ulong)) return Nullable_ulong.GetGuidSignature(); + if (typeof(T) == typeof(float)) return Nullable_float.GetGuidSignature(); + if (typeof(T) == typeof(double)) return Nullable_double.GetGuidSignature(); + if (typeof(T) == typeof(Guid)) return Nullable_guid.GetGuidSignature(); + if (typeof(T) == typeof(global::System.Type)) return Nullable_Type.GetGuidSignature(); + if (typeof(T) == typeof(global::System.TimeSpan)) return Nullable_TimeSpan.GetGuidSignature(); + if (typeof(T) == typeof(global::System.DateTimeOffset)) return Nullable_DateTimeOffset.GetGuidSignature(); + if (typeof(T) == typeof(global::Windows.Foundation.Point)) return IReferenceSignatures.Point; + if (typeof(T) == typeof(global::Windows.Foundation.Size)) return IReferenceSignatures.Size; + if (typeof(T) == typeof(global::Windows.Foundation.Rect)) return IReferenceSignatures.Rect; + if (typeof(T) == typeof(global::System.Numerics.Matrix3x2)) return IReferenceSignatures.Matrix3x2; + if (typeof(T) == typeof(global::System.Numerics.Matrix4x4)) return IReferenceSignatures.Matrix4x4; + if (typeof(T) == typeof(global::System.Numerics.Plane)) return IReferenceSignatures.Plane; + if (typeof(T) == typeof(global::System.Numerics.Quaternion)) return IReferenceSignatures.Quaternion; + if (typeof(T) == typeof(global::System.Numerics.Vector2)) return IReferenceSignatures.Vector2; + if (typeof(T) == typeof(global::System.Numerics.Vector3)) return IReferenceSignatures.Vector3; + if (typeof(T) == typeof(global::System.Numerics.Vector4)) return IReferenceSignatures.Vector4; + + return GuidGenerator.GetSignature(typeof(Nullable)); + } + + [Guid("61C17706-2D65-11E0-9AE8-D48564015472")] + public struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + public global::System.Delegate get_Value_0; + public static readonly Guid PIID = Nullable.PIID; + public static global::System.Type get_Value_0_Type; + + internal unsafe Vftbl(IntPtr thisPtr) + { + var vftblPtr = *(void***)thisPtr; + var vftbl = (IntPtr*)vftblPtr; + IInspectableVftbl = *(IInspectable.Vftbl*)vftblPtr; + get_Value_0 = GetValueDelegateForFunctionPointer(vftbl[6]); + } + + internal static Delegate GetValueDelegateForFunctionPointer(IntPtr ptr) + { + if (typeof(T) == typeof(int) || + typeof(T) == typeof(byte) || + typeof(T) == typeof(bool) || + typeof(T) == typeof(sbyte) || + typeof(T) == typeof(short) || + typeof(T) == typeof(ushort) || + typeof(T) == typeof(char) || + typeof(T) == typeof(uint) || + typeof(T) == typeof(long) || + typeof(T) == typeof(ulong) || + typeof(T) == typeof(float) || + typeof(T) == typeof(double) || + typeof(T) == typeof(Guid) || + typeof(T) == typeof(global::System.TimeSpan) || + typeof(T) == typeof(global::Windows.Foundation.Point) || + typeof(T) == typeof(global::Windows.Foundation.Rect) || + typeof(T) == typeof(global::Windows.Foundation.Size) || + typeof(T) == typeof(global::System.Numerics.Matrix3x2) || + typeof(T) == typeof(global::System.Numerics.Matrix4x4) || + typeof(T) == typeof(global::System.Numerics.Plane) || + typeof(T) == typeof(global::System.Numerics.Quaternion) || + typeof(T) == typeof(global::System.Numerics.Vector2) || + typeof(T) == typeof(global::System.Numerics.Vector3) || + typeof(T) == typeof(global::System.Numerics.Vector4) || + (typeof(T).IsEnum && Enum.GetUnderlyingType(typeof(T)) == typeof(int)) || + (typeof(T).IsEnum && Enum.GetUnderlyingType(typeof(T)) == typeof(uint))) + { + return Marshal.GetDelegateForFunctionPointer(ptr); + } + + if (typeof(T) == typeof(DateTimeOffset)) + { + return Marshal.GetDelegateForFunctionPointer(ptr); + } + +#if NET + // Only when not on NAOT, use LINQ expressions to get the delegate type. + // This is not safe on AOT, so in that case we just can't get a delegate. + if (RuntimeFeature.IsDynamicCodeCompiled) +#endif + { +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + get_Value_0_Type ??= Projections.GetAbiDelegateType(typeof(void*), Marshaler.AbiType.MakeByRefType(), typeof(int)); + + return Marshal.GetDelegateForFunctionPointer(ptr, get_Value_0_Type); +#pragma warning restore IL3050 + } + + throw new NotSupportedException($"Failed to get the marshalling delegate for Nullable`1 with type '{typeof(T)}'."); + } + } + + public static readonly Guid PIID = NullableType.GetIID(); + +#if !NET + public static implicit operator Nullable(IObjectReference obj) => (obj != null) ? new Nullable(obj) : null; + public static implicit operator Nullable(ObjectReference obj) => (obj != null) ? new Nullable(obj) : null; +#endif + protected readonly ObjectReference _obj; + public IntPtr ThisPtr => _obj.ThisPtr; + + public Nullable(IObjectReference obj) : this(obj.As(Vftbl.PIID)) { } + public Nullable(ObjectReference obj) + { + _obj = obj; + } + + public static unsafe T GetValue(IInspectable inspectable) + { + IntPtr nullablePtr = IntPtr.Zero; + try + { + ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in PIID), out nullablePtr)); + + Delegate marshallingDelegate = Vftbl.GetValueDelegateForFunctionPointer((*(IntPtr**)nullablePtr)[6]); + + // No need to use GC.KeepAlive here because 'nullablePtr' is already keeping + // the instance alive through the call. It's only needed in 'Value' below, + // because 'ThisPtr' is being used without anyone keeping 'this' alive too. + return GetValueFromAbi(nullablePtr, marshallingDelegate); + } + finally + { + MarshalExtensions.ReleaseIfNotNull(nullablePtr); + } + } + + public T Value + { + get + { + T result = GetValueFromAbi(ThisPtr, _obj.Vftbl.get_Value_0); + + GC.KeepAlive(this); + + return result; + } + } + + /// + /// Shared marshalling stub to get the underlying nullable value from a given IReference`1 native instance. + /// + /// The IReference`1 native instance to get the value from. + /// The marshalling delegate to get the value from the ABI. + /// The marshalled value retrieved from . + /// Thrown if no marshalling code for is available. + private static unsafe T GetValueFromAbi(IntPtr thisPtr, Delegate marshallingDelegate) + { + if (marshallingDelegate.GetType() == typeof(Nullable_Delegates.GetValueDelegateAbi)) + { + T result; + +#pragma warning disable CS8500 // We know that T is unmanaged + Marshal.ThrowExceptionForHR(((Nullable_Delegates.GetValueDelegateAbi)marshallingDelegate)((void*)thisPtr, &result)); +#pragma warning restore CS8500 + + return result; + } + + if (marshallingDelegate.GetType() == typeof(Nullable_Delegates.GetValueDelegateAbiDateTimeOffset)) + { + DateTimeOffset result; + + Marshal.ThrowExceptionForHR(((Nullable_Delegates.GetValueDelegateAbiDateTimeOffset)marshallingDelegate)((void*)thisPtr, &result)); + + global::System.DateTimeOffset managed = DateTimeOffset.FromAbi(result); + + return Unsafe.As(ref managed); + } + +#if NET + if (RuntimeFeature.IsDynamicCodeCompiled) +#endif + { + var __params = new object[] { thisPtr, null }; +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + try + { + marshallingDelegate.DynamicInvokeAbi(__params); + return Marshaler.FromAbi(__params[1]); + } + finally + { + Marshaler.DisposeAbi(__params[1]); + } +#pragma warning restore IL3050 + } + + throw new NotSupportedException($"Cannot retrieve the value for the current Nullable`1 instance with type '{typeof(T)}'."); + } + } + + // Used to handle boxing of strings and types where the C# compiler will de-duplicate them + // causing for the same instance to be reused with multiple different box instances. + // This is also used for delegates which are objects themselves in C# and are associated with + // their own ptr and thereby can not be associated with the ptr for the box / nullable. + internal sealed class Nullable + { + public Nullable(object boxedObject) + { + Value = boxedObject; + } + + public object Value { get; } + } + + [Guid("548cefbd-bc8a-5fa0-8df2-957440fc8bf4")] + internal static class Nullable_int + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};i4)"; + + [Guid("548cefbd-bc8a-5fa0-8df2-957440fc8bf4")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, int* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, int* __return_value__) + { + int ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (int)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = ____return_value__; + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + } + + [Guid("fd416dfb-2a07-52eb-aae3-dfce14116c05")] + internal static class Nullable_string + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};string)"; + + [Guid("fd416dfb-2a07-52eb-aae3-dfce14116c05")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, IntPtr* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, IntPtr* __return_value__) + { + string ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (string)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = MarshalString.FromManaged(____return_value__); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + + public static unsafe Nullable GetValue(IInspectable inspectable) + { + IntPtr nullablePtr = IntPtr.Zero; + IntPtr __retval = default; + try + { + ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in IID.IID_NullableString), out nullablePtr)); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); + return new Nullable(MarshalString.FromAbi(__retval)); + } + finally + { + MarshalString.DisposeAbi(__retval); + MarshalExtensions.ReleaseIfNotNull(nullablePtr); + } + } + } + + [Guid("e5198cc8-2873-55f5-b0a1-84ff9e4aad62")] + internal static class Nullable_byte + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};u1)"; + + [Guid("e5198cc8-2873-55f5-b0a1-84ff9e4aad62")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, byte* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, byte* __return_value__) + { + byte ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (byte)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = ____return_value__; + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + } + + [Guid("95500129-fbf6-5afc-89df-70642d741990")] + internal static class Nullable_sbyte + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};i1)"; + + [Guid("95500129-fbf6-5afc-89df-70642d741990")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, sbyte* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, sbyte* __return_value__) + { + sbyte ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (sbyte)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = ____return_value__; + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + } + + [Guid("6ec9e41b-6709-5647-9918-a1270110fc4e")] + internal static class Nullable_short + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};i2)"; + + [Guid("6ec9e41b-6709-5647-9918-a1270110fc4e")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, short* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, short* __return_value__) + { + short ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (short)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = ____return_value__; + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + } + + [Guid("5ab7d2c3-6b62-5e71-a4b6-2d49c4f238fd")] + internal static class Nullable_ushort + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};u2)"; + + [Guid("5ab7d2c3-6b62-5e71-a4b6-2d49c4f238fd")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, ushort* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, ushort* __return_value__) + { + ushort ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (ushort)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = ____return_value__; + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + } + + [Guid("513ef3af-e784-5325-a91e-97c2b8111cf3")] + internal static class Nullable_uint + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};u4)"; + + [Guid("513ef3af-e784-5325-a91e-97c2b8111cf3")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, uint* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, uint* __return_value__) + { + uint ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (uint)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = ____return_value__; + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + } + + [Guid("4dda9e24-e69f-5c6a-a0a6-93427365af2a")] + internal static class Nullable_long + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};i8)"; + + [Guid("4dda9e24-e69f-5c6a-a0a6-93427365af2a")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, long* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, long* __return_value__) + { + long ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (long)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = ____return_value__; + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + } + + [Guid("6755e376-53bb-568b-a11d-17239868309e")] + internal static class Nullable_ulong + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};u8)"; + + [Guid("6755e376-53bb-568b-a11d-17239868309e")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, ulong* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, ulong* __return_value__) + { + ulong ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (ulong)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = ____return_value__; + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + } + + [Guid("719cc2ba-3e76-5def-9f1a-38d85a145ea8")] + internal static class Nullable_float + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};f4)"; + + [Guid("719cc2ba-3e76-5def-9f1a-38d85a145ea8")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, float* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, float* __return_value__) + { + float ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (float)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = ____return_value__; + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + } + + [Guid("2f2d6c29-5473-5f3e-92e7-96572bb990e2")] + internal static class Nullable_double + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};f8)"; + + [Guid("2f2d6c29-5473-5f3e-92e7-96572bb990e2")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, double* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, double* __return_value__) + { + double ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (double)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = ____return_value__; + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + } + + [Guid("fb393ef3-bbac-5bd5-9144-84f23576f415")] + internal static class Nullable_char + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};c2)"; + + [Guid("fb393ef3-bbac-5bd5-9144-84f23576f415")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, char* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, char* __return_value__) + { + char ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (char)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = ____return_value__; + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + } + + [Guid("3c00fd60-2950-5939-a21a-2d12c5a01b8a")] + internal static class Nullable_bool + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};b1)"; + + [Guid("3c00fd60-2950-5939-a21a-2d12c5a01b8a")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, bool* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, bool* __return_value__) + { + bool ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (bool)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = ____return_value__; + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + } + + [Guid("7d50f649-632c-51f9-849a-ee49428933ea")] + internal static class Nullable_guid + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};g16)"; + + [Guid("7d50f649-632c-51f9-849a-ee49428933ea")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, Guid* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, Guid* __return_value__) + { + Guid ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (Guid)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = ____return_value__; + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + } + + [Guid("5541d8a7-497c-5aa4-86fc-7713adbf2a2c")] + internal static class Nullable_DateTimeOffset + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.Foundation.DateTime;i8))"; + + [Guid("5541d8a7-497c-5aa4-86fc-7713adbf2a2c")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, DateTimeOffset* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, DateTimeOffset* __return_value__) + { + global::System.DateTimeOffset ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (global::System.DateTimeOffset)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = DateTimeOffset.FromManaged(____return_value__); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + + public static unsafe object GetValue(IInspectable inspectable) + { + IntPtr nullablePtr = IntPtr.Zero; + DateTimeOffset __retval = default; + try + { + ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in IID.IID_NullableDateTimeOffset), out nullablePtr)); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); + return DateTimeOffset.FromAbi(__retval); + } + finally + { + DateTimeOffset.DisposeAbi(__retval); + MarshalExtensions.ReleaseIfNotNull(nullablePtr); + } + } + } + + [Guid("604d0c4c-91de-5c2a-935f-362f13eaf800")] + internal static class Nullable_TimeSpan + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.Foundation.TimeSpan;i8))"; + + [Guid("604d0c4c-91de-5c2a-935f-362f13eaf800")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, TimeSpan* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, TimeSpan* __return_value__) + { + global::System.TimeSpan ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (global::System.TimeSpan)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = TimeSpan.FromManaged(____return_value__); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + + public static unsafe object GetValue(IInspectable inspectable) + { + IntPtr nullablePtr = IntPtr.Zero; + TimeSpan __retval = default; + try + { + ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in IID.IID_NullableTimeSpan), out nullablePtr)); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); + return TimeSpan.FromAbi(__retval); + } + finally + { + TimeSpan.DisposeAbi(__retval); + MarshalExtensions.ReleaseIfNotNull(nullablePtr); + } + } + } + + [Guid("06dccc90-a058-5c88-87b7-6f3360a2fc16")] + internal static class Nullable_Object + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};cinterface(IInspectable))"; + + [Guid("06dccc90-a058-5c88-87b7-6f3360a2fc16")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, IntPtr* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, IntPtr* __return_value__) + { + object ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = MarshalInspectable.FromManaged(____return_value__); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + } + + [Guid("3830ad99-d8da-53f3-989b-fc92ad222778")] + internal static class Nullable_Type + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.UI.Xaml.Interop.TypeName;string;enum(Windows.UI.Xaml.Interop.TypeKind;i4)))"; + + [Guid("3830ad99-d8da-53f3-989b-fc92ad222778")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, Type* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, Type* __return_value__) + { + global::System.Type ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = (global::System.Type)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = Type.FromManaged(____return_value__); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + + public static unsafe Nullable GetValue(IInspectable inspectable) + { + IntPtr nullablePtr = IntPtr.Zero; + Type __retval = default; + try + { + ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in IID.IID_NullableType), out nullablePtr)); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); + return new Nullable(Type.FromAbi(__retval)); + } + finally + { + Type.DisposeAbi(__retval); + MarshalExtensions.ReleaseIfNotNull(nullablePtr); + } + } + } + + [Guid("6ff27a1e-4b6a-59b7-b2c3-d1f2ee474593")] + internal static class Nullable_Exception + { + public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.Foundation.HResult;i4))"; + + [Guid("6ff27a1e-4b6a-59b7-b2c3-d1f2ee474593")] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _Get_Value_0; + public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + + private static readonly Vftbl AbiToProjectionVftable; + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, Exception* value); + private static readonly GetValueDelegate delegateCache; +#endif + + static Vftbl() + { + AbiToProjectionVftable = new Vftbl + { + IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, +#if !NET + _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() +#else + _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 +#endif + }; + var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * 1); + *(Vftbl*)nativeVftbl = AbiToProjectionVftable; + AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; + } + +#if NET + [UnmanagedCallersOnly] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, Exception* __return_value__) + { + global::System.Exception ____return_value__ = default; -#if NET - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", Justification = "ABI types used with MakeGenericType are not reflected on.")] -#endif - static unsafe BoxedValueIReferenceImpl() - { - AbiToProjectionVftable = new global::ABI.System.Nullable.Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - get_Value_0 = global::System.Delegate.CreateDelegate(global::ABI.System.Nullable.Vftbl.get_Value_0_Type, typeof(BoxedValueIReferenceImpl).GetMethod("Do_Abi_get_Value_0", BindingFlags.NonPublic | BindingFlags.Static).MakeGenericMethod(Marshaler.AbiType)) - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(BoxedValueIReferenceImpl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable.IInspectableVftbl, (IntPtr)nativeVftbl, false); - nativeVftbl[6] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.get_Value_0); - - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - private static unsafe int Do_Abi_get_Value_0(void* thisPtr, out TAbi __return_value__) - { - T ____return_value__ = default; - - __return_value__ = default; - - try - { - ____return_value__ = (T)global::WinRT.ComWrappersSupport.FindObject(new IntPtr(thisPtr)); - __return_value__ = (TAbi)Marshaler.FromManaged(____return_value__); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } -} - -namespace ABI.System -{ - [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] - [Guid("61C17706-2D65-11E0-9AE8-D48564015472")] -#if EMBED - internal -#else - public -#endif - class Nullable - { - public static IObjectReference CreateMarshaler(object value) - { - return value is null ? null : ComWrappersSupport.CreateCCWForObject(value, PIID); - } - - public static ObjectReferenceValue CreateMarshaler2(object value) => - ComWrappersSupport.CreateCCWForObjectForMarshaling(value, PIID); - - public static IntPtr GetAbi(IObjectReference m) => m?.ThisPtr ?? IntPtr.Zero; - - public static object FromAbi(IntPtr ptr) - { - if (ptr == IntPtr.Zero) - { - return null; - } - var vftblT = new Vftbl(ptr); - var wrapper = new Nullable(ObjectReference.FromAbi(ptr, vftblT)); - return wrapper.Value; - } - - public static unsafe void CopyManaged(object o, IntPtr dest) - { - *(IntPtr*)dest.ToPointer() = CreateMarshaler2(o).Detach(); - } - - public static IntPtr FromManaged(object value) - { - if (value is null) - { - return IntPtr.Zero; - } - return CreateMarshaler2(value).Detach(); - } - - public static void DisposeMarshaler(IObjectReference m) { m?.Dispose(); } - public static void DisposeAbi(IntPtr abi) { MarshalInspectable.DisposeAbi(abi); } - - public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(Nullable)); - - [Guid("61C17706-2D65-11E0-9AE8-D48564015472")] - public struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - public global::System.Delegate get_Value_0; - public static Guid PIID = GuidGenerator.CreateIID(typeof(Nullable)); - public static readonly global::System.Type get_Value_0_Type = Expression.GetDelegateType(new global::System.Type[] { typeof(void*), Marshaler.AbiType.MakeByRefType(), typeof(int) }); - - internal unsafe Vftbl(IntPtr thisPtr) - { - var vftblPtr = Marshal.PtrToStructure(thisPtr); - var vftbl = (IntPtr*)vftblPtr.Vftbl; - IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); - get_Value_0 = Marshal.GetDelegateForFunctionPointer(vftbl[6], get_Value_0_Type); - } - } - - public static Guid PIID = Vftbl.PIID; - - public static implicit operator Nullable(IObjectReference obj) => (obj != null) ? new Nullable(obj) : null; - public static implicit operator Nullable(ObjectReference obj) => (obj != null) ? new Nullable(obj) : null; - protected readonly ObjectReference _obj; - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public Nullable(IObjectReference obj) : this(obj.As(Vftbl.PIID)) { } - public Nullable(ObjectReference obj) - { - _obj = obj; - } - - internal static unsafe T GetValue(IInspectable inspectable) + *__return_value__ = default; + + try + { + ____return_value__ = (global::System.Exception)global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = Exception.FromManaged(____return_value__); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + + public static unsafe Nullable GetValue(IInspectable inspectable) { IntPtr nullablePtr = IntPtr.Zero; - var __params = new object[] { IntPtr.Zero, null }; + Exception __retval = default; try { - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref PIID, out nullablePtr)); - __params[0] = nullablePtr; - Marshal.GetDelegateForFunctionPointer((*(IntPtr**)nullablePtr)[6], Vftbl.get_Value_0_Type).DynamicInvokeAbi(__params); - return Marshaler.FromAbi(__params[1]); + ExceptionHelpers.ThrowExceptionForHR( +#if NET8_0_OR_GREATER + Marshal.QueryInterface(inspectable.ThisPtr, in IID.IID_NullableException, out nullablePtr) +#else + Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in IID.IID_NullableException), out nullablePtr) +#endif + ); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); + return new Nullable(Exception.FromAbi(__retval)); } finally { - Marshaler.DisposeAbi(__params[1]); - Marshal.Release(nullablePtr); - } - } - - public unsafe T Value - { - get - { - var __params = new object[] { ThisPtr, null }; - try - { - _obj.Vftbl.get_Value_0.DynamicInvokeAbi(__params); - return Marshaler.FromAbi(__params[1]); - } - finally - { - Marshaler.DisposeAbi(__params[1]); - } - } - } + Exception.DisposeAbi(__retval); + MarshalExtensions.ReleaseIfNotNull(nullablePtr); + } + } } - // Used to handle boxing of strings and types where the C# compiler will de-duplicate them - // causing for the same instance to be reused with multiple different box instances. - // This is also used for delegates which are objects themselves in C# and are associated with - // their own ptr and thereby can not be associated with the ptr for the box / nullable. - internal sealed class Nullable + [Guid("25230F05-B49C-57EE-8961-5373D98E1AB1")] + internal static class Nullable_EventHandler { - public Nullable(object boxedObject) + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private static readonly Nullable_Delegates.GetValueDelegate _Get_Value_0; +#endif + + unsafe static Nullable_EventHandler() { - Value = boxedObject; + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Nullable_EventHandler), sizeof(IInspectable.Vftbl) + sizeof(IntPtr)); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; +#if !NET + ((IntPtr*)AbiToProjectionVftablePtr)[6] = Marshal.GetFunctionPointerForDelegate(_Get_Value_0 = Do_Abi_get_Value_0); +#else + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[6] = &Do_Abi_get_Value_0; +#endif } - public object Value { get; } - } +#if NET + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, IntPtr* __return_value__) + { + global::System.EventHandler ____return_value__ = default; - [Guid("548cefbd-bc8a-5fa0-8df2-957440fc8bf4")] - internal static class Nullable_int - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};i4)"; - internal static Guid IID = new(0x548cefbd, 0xbc8a, 0x5fa0, 0x8d, 0xf2, 0x95, 0x74, 0x40, 0xfc, 0x8b, 0xf4); - - [Guid("548cefbd-bc8a-5fa0-8df2-957440fc8bf4")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + *__return_value__ = default; - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, int* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, int* __return_value__) - { - int ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (int)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = ____return_value__; - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static int GetValue(IInspectable inspectable) - { - IntPtr nullablePtr = IntPtr.Zero; try { - int __retval = default; - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return __retval; + ____return_value__ = global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = EventHandler.FromManaged(____return_value__); } - finally + catch (global::System.Exception __exception__) { - Marshal.Release(nullablePtr); + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); } - } - } - - [Guid("fd416dfb-2a07-52eb-aae3-dfce14116c05")] - internal static class Nullable_string - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};string)"; - internal static Guid IID = new(0xfd416dfb, 0x2a07, 0x52eb, 0xaa, 0xe3, 0xdf, 0xce, 0x14, 0x11, 0x6c, 0x05); - - [Guid("fd416dfb-2a07-52eb-aae3-dfce14116c05")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + return 0; + } - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, IntPtr* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, IntPtr* __return_value__) - { - string ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (string)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = MarshalString.FromManaged(____return_value__); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static Nullable GetValue(IInspectable inspectable) + public static unsafe Nullable GetValue(IInspectable inspectable) { IntPtr nullablePtr = IntPtr.Zero; IntPtr __retval = default; try { - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); + ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in IID.IID_NullableEventHandler), out nullablePtr)); ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return new Nullable(MarshalString.FromAbi(__retval)); + return new Nullable(EventHandler.FromAbi(__retval)); } finally { - MarshalString.DisposeAbi(__retval); - Marshal.Release(nullablePtr); + EventHandler.DisposeAbi(__retval); + MarshalExtensions.ReleaseIfNotNull(nullablePtr); } - } + } } - [Guid("e5198cc8-2873-55f5-b0a1-84ff9e4aad62")] - internal static class Nullable_byte - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};u1)"; - internal static Guid IID = new(0xe5198cc8, 0x2873, 0x55f5, 0xb0, 0xa1, 0x84, 0xff, 0x9e, 0x4a, 0xad, 0x62); - - [Guid("e5198cc8-2873-55f5-b0a1-84ff9e4aad62")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + [Guid("1eeae0cb-8f57-5c37-a087-a55d46e2fe3f")] + internal static class Nullable_PropertyChangedEventHandler + { + public static readonly IntPtr AbiToProjectionVftablePtr; + + public static Guid IID => FeatureSwitches.UseWindowsUIXamlProjections + ? global::WinRT.Interop.IID.IID_WUX_NullablePropertyChangedEventHandler + : global::WinRT.Interop.IID.IID_MUX_NullablePropertyChangedEventHandler; + +#if !NET + private static readonly Nullable_Delegates.GetValueDelegate _Get_Value_0; +#endif - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, byte* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, byte* __return_value__) - { - byte ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (byte)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = ____return_value__; - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static byte GetValue(IInspectable inspectable) + unsafe static Nullable_PropertyChangedEventHandler() { - IntPtr nullablePtr = IntPtr.Zero; + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Nullable_PropertyChangedEventHandler), sizeof(IInspectable.Vftbl) + sizeof(IntPtr)); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; +#if !NET + ((IntPtr*)AbiToProjectionVftablePtr)[6] = Marshal.GetFunctionPointerForDelegate(_Get_Value_0 = Do_Abi_get_Value_0); +#else + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[6] = &Do_Abi_get_Value_0; +#endif + } + +#if NET + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, IntPtr* __return_value__) + { + global::System.ComponentModel.PropertyChangedEventHandler ____return_value__ = default; + + *__return_value__ = default; + try { - byte __retval = default; - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return __retval; + ____return_value__ = global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = global::ABI.System.ComponentModel.PropertyChangedEventHandler.FromManaged(____return_value__); } - finally + catch (global::System.Exception __exception__) { - Marshal.Release(nullablePtr); + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); } - } - } - - [Guid("95500129-fbf6-5afc-89df-70642d741990")] - internal static class Nullable_sbyte - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};i1)"; - internal static Guid IID = new(0x95500129, 0xfbf6, 0x5afc, 0x89, 0xdf, 0x70, 0x64, 0x2d, 0x74, 0x19, 0x90); - - [Guid("95500129-fbf6-5afc-89df-70642d741990")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + return 0; + } - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, sbyte* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, sbyte* __return_value__) - { - sbyte ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (sbyte)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = ____return_value__; - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static sbyte GetValue(IInspectable inspectable) + public static unsafe Nullable GetValue(IInspectable inspectable) { + Guid iid = IID; IntPtr nullablePtr = IntPtr.Zero; + IntPtr __retval = default; try { - sbyte __retval = default; - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return __retval; + ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in iid), out nullablePtr)); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); + return new Nullable(global::ABI.System.ComponentModel.PropertyChangedEventHandler.FromAbi(__retval)); } finally { - Marshal.Release(nullablePtr); + global::ABI.System.ComponentModel.PropertyChangedEventHandler.DisposeAbi(__retval); + MarshalExtensions.ReleaseIfNotNull(nullablePtr); } - } + } } - [Guid("6ec9e41b-6709-5647-9918-a1270110fc4e")] - internal static class Nullable_short - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};i2)"; - internal static Guid IID = new(0x6ec9e41b, 0x6709, 0x5647, 0x99, 0x18, 0xa1, 0x27, 0x01, 0x10, 0xfc, 0x4e); - - [Guid("6ec9e41b-6709-5647-9918-a1270110fc4e")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + [Guid("779d5a21-0e7d-5476-bb90-27fa3b4b8de5")] + internal static class Nullable_NotifyCollectionChangedEventHandler + { + public static readonly IntPtr AbiToProjectionVftablePtr; + + public static Guid IID => FeatureSwitches.UseWindowsUIXamlProjections + ? global::WinRT.Interop.IID.IID_WUX_NotifyCollectionChangedEventHandler + : global::WinRT.Interop.IID.IID_MUX_NotifyCollectionChangedEventHandler; + +#if !NET + private static readonly Nullable_Delegates.GetValueDelegate _Get_Value_0; +#endif - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, short* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, short* __return_value__) - { - short ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (short)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = ____return_value__; - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static short GetValue(IInspectable inspectable) + unsafe static Nullable_NotifyCollectionChangedEventHandler() { - IntPtr nullablePtr = IntPtr.Zero; + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Nullable_NotifyCollectionChangedEventHandler), sizeof(IInspectable.Vftbl) + sizeof(IntPtr)); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; +#if !NET + ((IntPtr*)AbiToProjectionVftablePtr)[6] = Marshal.GetFunctionPointerForDelegate(_Get_Value_0 = Do_Abi_get_Value_0); +#else + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[6] = &Do_Abi_get_Value_0; +#endif + } + +#if NET + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, IntPtr* __return_value__) + { + global::System.Collections.Specialized.NotifyCollectionChangedEventHandler ____return_value__ = default; + + *__return_value__ = default; + try { - short __retval = default; - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return __retval; + ____return_value__ = global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = global::ABI.System.Collections.Specialized.NotifyCollectionChangedEventHandler.FromManaged(____return_value__); } - finally + catch (global::System.Exception __exception__) { - Marshal.Release(nullablePtr); + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); } - } - } - - [Guid("5ab7d2c3-6b62-5e71-a4b6-2d49c4f238fd")] - internal static class Nullable_ushort - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};u2)"; - internal static Guid IID = new(0x5ab7d2c3, 0x6b62, 0x5e71, 0xa4, 0xb6, 0x2d, 0x49, 0xc4, 0xf2, 0x38, 0xfd); - - [Guid("5ab7d2c3-6b62-5e71-a4b6-2d49c4f238fd")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + return 0; + } - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, ushort* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, ushort* __return_value__) - { - ushort ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (ushort)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = ____return_value__; - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static ushort GetValue(IInspectable inspectable) + public static unsafe Nullable GetValue(IInspectable inspectable) { + Guid iid = IID; IntPtr nullablePtr = IntPtr.Zero; + IntPtr __retval = default; try { - ushort __retval = default; - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return __retval; + ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in iid), out nullablePtr)); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); + return new Nullable(global::ABI.System.Collections.Specialized.NotifyCollectionChangedEventHandler.FromAbi(__retval)); } finally { - Marshal.Release(nullablePtr); + global::ABI.System.Collections.Specialized.NotifyCollectionChangedEventHandler.DisposeAbi(__retval); + MarshalExtensions.ReleaseIfNotNull(nullablePtr); } - } + } } - [Guid("513ef3af-e784-5325-a91e-97c2b8111cf3")] - internal static class Nullable_uint - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};u4)"; - internal static Guid IID = new(0x513ef3af, 0xe784, 0x5325, 0xa9, 0x1e, 0x97, 0xc2, 0xb8, 0x11, 0x1c, 0xf3); - - [Guid("513ef3af-e784-5325-a91e-97c2b8111cf3")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + [Guid("61C17706-2D65-11E0-9AE8-D48564015472")] + internal static class Nullable_Delegate + { + internal static readonly ConcurrentDictionary DelegateGuidMapping = new(); + + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private static readonly Nullable_Delegates.GetValueDelegate _Get_Value_0; +#endif - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, uint* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, uint* __return_value__) - { - uint ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (uint)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = ____return_value__; - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static uint GetValue(IInspectable inspectable) + unsafe static Nullable_Delegate() { - IntPtr nullablePtr = IntPtr.Zero; + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Nullable_Delegate), sizeof(IInspectable.Vftbl) + sizeof(IntPtr)); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; +#if !NET + ((IntPtr*)AbiToProjectionVftablePtr)[6] = Marshal.GetFunctionPointerForDelegate(_Get_Value_0 = Do_Abi_get_Value_0); +#else + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[6] = &Do_Abi_get_Value_0; +#endif + } + +#if NET + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, IntPtr* __return_value__) + { + global::System.Delegate ____return_value__ = default; + + *__return_value__ = default; + try { - uint __retval = default; - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return __retval; + ____return_value__ = global::WinRT.ComWrappersSupport.FindObject (thisPtr); + *__return_value__ = MarshalDelegate.CreateMarshaler2(____return_value__, DelegateGuidMapping[____return_value__.GetType()]).Detach(); } - finally + catch (global::System.Exception __exception__) { - Marshal.Release(nullablePtr); + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); } - } + return 0; + } } - [Guid("4dda9e24-e69f-5c6a-a0a6-93427365af2a")] - internal static class Nullable_long - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};i8)"; - internal static Guid IID = new(0x4dda9e24, 0xe69f, 0x5c6a, 0xa0, 0xa6, 0x93, 0x42, 0x73, 0x65, 0xaf, 0x2a); - - [Guid("4dda9e24-e69f-5c6a-a0a6-93427365af2a")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + [Guid("61C17706-2D65-11E0-9AE8-D48564015472")] + internal static class Nullable_Delegate where T : global::System.Delegate + { + public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(Nullable)); + + public static readonly IntPtr AbiToProjectionVftablePtr; - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, long* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, long* __return_value__) - { - long ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (long)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = ____return_value__; - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static long GetValue(IInspectable inspectable) + private static readonly Nullable_Delegates.GetValueDelegate _Get_Value_0; + + unsafe static Nullable_Delegate() { - IntPtr nullablePtr = IntPtr.Zero; + _Get_Value_0 = Do_Abi_get_Value_0; + + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Nullable_Delegate), sizeof(IInspectable.Vftbl) + sizeof(IntPtr)); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; + ((IntPtr*)AbiToProjectionVftablePtr)[6] = Marshal.GetFunctionPointerForDelegate(_Get_Value_0); + } + + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, IntPtr* __return_value__) + { + T ____return_value__ = default; + + *__return_value__ = default; + try { - long __retval = default; - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return __retval; + ____return_value__ = global::WinRT.ComWrappersSupport.FindObject(thisPtr); + *__return_value__ = (IntPtr)MarshalGeneric.FromManaged(____return_value__); } - finally + catch (global::System.Exception __exception__) { - Marshal.Release(nullablePtr); + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); } - } - } + return 0; + } - [Guid("6755e376-53bb-568b-a11d-17239868309e")] - internal static class Nullable_ulong - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};u8)"; - internal static Guid IID = new(0x6755e376, 0x53bb, 0x568b, 0xa1, 0x1d, 0x17, 0x23, 0x98, 0x68, 0x30, 0x9e); - - [Guid("6755e376-53bb-568b-a11d-17239868309e")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + public static readonly Guid PIID = Nullable.PIID; - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, ulong* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, ulong* __return_value__) - { - ulong ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (ulong)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = ____return_value__; - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static ulong GetValue(IInspectable inspectable) + public static unsafe Nullable GetValue(IInspectable inspectable) { IntPtr nullablePtr = IntPtr.Zero; + IntPtr __retval = default; try { - ulong __retval = default; - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return __retval; + ExceptionHelpers.ThrowExceptionForHR( +#if NET8_0_OR_GREATER + Marshal.QueryInterface(inspectable.ThisPtr, in PIID, out nullablePtr) +#else + Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in PIID), out nullablePtr) +#endif + ); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); + return new Nullable(MarshalDelegate.FromAbi(__retval)); } finally { - Marshal.Release(nullablePtr); + MarshalExtensions.ReleaseIfNotNull(__retval); + MarshalExtensions.ReleaseIfNotNull(nullablePtr); } - } + } } - [Guid("719cc2ba-3e76-5def-9f1a-38d85a145ea8")] - internal static class Nullable_float - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};f4)"; - internal static Guid IID = new(0x719cc2ba, 0x3e76, 0x5def, 0x9f, 0x1a, 0x38, 0xd8, 0x5a, 0x14, 0x5e, 0xa8); - - [Guid("719cc2ba-3e76-5def-9f1a-38d85a145ea8")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + internal static class Nullable_IntEnum + { + public static readonly IntPtr AbiToProjectionVftablePtr; + +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, int* value); + private static readonly GetValueDelegate delegateCache; +#endif - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, float* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, float* __return_value__) - { - float ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (float)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = ____return_value__; - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static float GetValue(IInspectable inspectable) + unsafe static Nullable_IntEnum() { - IntPtr nullablePtr = IntPtr.Zero; + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Nullable_IntEnum), sizeof(IInspectable.Vftbl) + sizeof(IntPtr)); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; +#if !NET + ((IntPtr*)AbiToProjectionVftablePtr)[6] = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0); +#else + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[6] = &Do_Abi_get_Value_0; +#endif + } + +#if NET + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, int* __return_value__) + { + *__return_value__ = default; + try { - float __retval = default; - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return __retval; + *__return_value__ = (int)global::WinRT.ComWrappersSupport.FindObject(thisPtr); } - finally + catch (global::System.Exception __exception__) { - Marshal.Release(nullablePtr); + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); } - } - } + return 0; + } - [Guid("2f2d6c29-5473-5f3e-92e7-96572bb990e2")] - internal static class Nullable_double - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};f8)"; - internal static Guid IID = new(0x2f2d6c29, 0x5473, 0x5f3e, 0x92, 0xe7, 0x96, 0x57, 0x2b, 0xb9, 0x90, 0xe2); - - [Guid("2f2d6c29-5473-5f3e-92e7-96572bb990e2")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + internal static Guid GetIID(global::System.Type enumType) + { + return GuidGenerator.CreateIIDForGenericType("pinterface({61c17706-2d65-11e0-9ae8-d48564015472};enum(" + enumType.FullName + ";i4))"); + } - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, double* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, double* __return_value__) - { - double ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (double)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = ____return_value__; - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static double GetValue(IInspectable inspectable) + internal static unsafe object GetValue(global::System.Type enumType, IInspectable inspectable) { + var IID = GetIID(enumType); IntPtr nullablePtr = IntPtr.Zero; try { - double __retval = default; - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return __retval; + int __retval = default; + ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in IID), out nullablePtr)); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); + return Enum.ToObject(enumType, __retval); } finally { - Marshal.Release(nullablePtr); + MarshalExtensions.ReleaseIfNotNull(nullablePtr); } - } + } } - [Guid("fb393ef3-bbac-5bd5-9144-84f23576f415")] - internal static class Nullable_char - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};c2)"; - internal static Guid IID = new(0xfb393ef3, 0xbbac, 0x5bd5, 0x91, 0x44, 0x84, 0xf2, 0x35, 0x76, 0xf4, 0x15); - - [Guid("fb393ef3-bbac-5bd5-9144-84f23576f415")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + internal static class Nullable_FlagsEnum + { + public static readonly IntPtr AbiToProjectionVftablePtr; - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, char* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, char* __return_value__) - { - char ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (char)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = ____return_value__; - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static char GetValue(IInspectable inspectable) +#if !NET + private unsafe delegate int GetValueDelegate(IntPtr thisPtr, uint* value); + private static readonly GetValueDelegate delegateCache; +#endif + + unsafe static Nullable_FlagsEnum() { - IntPtr nullablePtr = IntPtr.Zero; + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Nullable_FlagsEnum), sizeof(IInspectable.Vftbl) + sizeof(IntPtr)); + *(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; +#if !NET + ((IntPtr*)AbiToProjectionVftablePtr)[6] = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0); +#else + ((delegate* unmanaged[Stdcall]*)AbiToProjectionVftablePtr)[6] = &Do_Abi_get_Value_0; +#endif + } + +#if NET + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] +#endif + private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, uint* __return_value__) + { + *__return_value__ = default; + try { - char __retval = default; - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return __retval; + *__return_value__ = (uint)global::WinRT.ComWrappersSupport.FindObject(thisPtr); } - finally + catch (global::System.Exception __exception__) { - Marshal.Release(nullablePtr); + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); } - } - } + return 0; + } - [Guid("3c00fd60-2950-5939-a21a-2d12c5a01b8a")] - internal static class Nullable_bool - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};b1)"; - internal static Guid IID = new(0x3c00fd60, 0x2950, 0x5939, 0xa2, 0x1a, 0x2d, 0x12, 0xc5, 0xa0, 0x1b, 0x8a); - - [Guid("3c00fd60-2950-5939-a21a-2d12c5a01b8a")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + internal static Guid GetIID(global::System.Type enumType) + { + return GuidGenerator.CreateIIDForGenericType("pinterface({61c17706-2d65-11e0-9ae8-d48564015472};enum(" + enumType.FullName + ";u4))"); + } - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, bool* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, bool* __return_value__) - { - bool ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (bool)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = ____return_value__; - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static bool GetValue(IInspectable inspectable) + internal static unsafe object GetValue(global::System.Type enumType, IInspectable inspectable) { + var IID = GetIID(enumType); IntPtr nullablePtr = IntPtr.Zero; try { - bool __retval = default; - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return __retval; + uint __retval = default; + ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in IID), out nullablePtr)); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); + return Enum.ToObject(enumType, __retval); } finally { - Marshal.Release(nullablePtr); + MarshalExtensions.ReleaseIfNotNull(nullablePtr); } - } + } } - [Guid("7d50f649-632c-51f9-849a-ee49428933ea")] - internal static class Nullable_guid - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};g16)"; - internal static Guid IID = new(0x7d50f649, 0x632c, 0x51f9, 0x84, 0x9a, 0xee, 0x49, 0x42, 0x89, 0x33, 0xea); - - [Guid("7d50f649-632c-51f9-849a-ee49428933ea")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + internal static class Nullable_Delegates + { + public unsafe delegate int GetValueDelegate(IntPtr thisPtr, IntPtr* value); + public unsafe delegate int GetValueDelegateAbi(void* thisPtr, void* value); + public unsafe delegate int GetValueDelegateAbiDateTimeOffset(void* ptr, DateTimeOffset* result); + } + + internal static class NullableBlittable where T: unmanaged + { + private static readonly Guid IID = NullableType.GetIID(); - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, Guid* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, Guid* __return_value__) - { - Guid ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (Guid)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = ____return_value__; - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static Guid GetValue(IInspectable inspectable) + public static unsafe object GetValue(IInspectable inspectable) { IntPtr nullablePtr = IntPtr.Zero; try { - Guid __retval = default; - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); + T __retval = default; + ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in IID), out nullablePtr)); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); return __retval; } finally { - Marshal.Release(nullablePtr); + MarshalExtensions.ReleaseIfNotNull(nullablePtr); } - } + } } - [Guid("5541d8a7-497c-5aa4-86fc-7713adbf2a2c")] - internal static class Nullable_DateTimeOffset - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.Foundation.DateTime;i8))"; - internal static Guid IID = new(0x5541d8a7, 0x497c, 0x5aa4, 0x86, 0xfc, 0x77, 0x13, 0xad, 0xbf, 0x2a, 0x2c); - - [Guid("5541d8a7-497c-5aa4-86fc-7713adbf2a2c")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + internal static class NullableType + { + internal static Guid GetIID() + { + if (typeof(T) == typeof(int)) return IID.IID_NullableInt; + if (typeof(T) == typeof(byte)) return IID.IID_NullableByte; + if (typeof(T) == typeof(bool)) return IID.IID_NullableBool; + if (typeof(T) == typeof(sbyte)) return IID.IID_NullableSByte; + if (typeof(T) == typeof(short)) return IID.IID_NullableShort; + if (typeof(T) == typeof(ushort)) return IID.IID_NullableUShort; + if (typeof(T) == typeof(char)) return IID.IID_NullableChar; + if (typeof(T) == typeof(uint)) return IID.IID_NullableUInt; + if (typeof(T) == typeof(long)) return IID.IID_NullableLong; + if (typeof(T) == typeof(ulong)) return IID.IID_NullableULong; + if (typeof(T) == typeof(float)) return IID.IID_NullableFloat; + if (typeof(T) == typeof(double)) return IID.IID_NullableDouble; + if (typeof(T) == typeof(Guid)) return IID.IID_NullableGuid; + if (typeof(T) == typeof(global::System.Type)) return IID.IID_NullableType; + if (typeof(T) == typeof(global::System.TimeSpan)) return IID.IID_NullableTimeSpan; + if (typeof(T) == typeof(global::System.DateTimeOffset)) return IID.IID_NullableDateTimeOffset; + if (typeof(T) == typeof(global::Windows.Foundation.Point)) return IID.IID_IReferenceOfPoint; + if (typeof(T) == typeof(global::Windows.Foundation.Size)) return IID.IID_IReferenceOfSize; + if (typeof(T) == typeof(global::Windows.Foundation.Rect)) return IID.IID_IReferenceOfRect; + if (typeof(T) == typeof(global::System.Numerics.Matrix3x2)) return IID.IID_IReferenceMatrix3x2; + if (typeof(T) == typeof(global::System.Numerics.Matrix4x4)) return IID.IID_IReferenceMatrix4x4; + if (typeof(T) == typeof(global::System.Numerics.Plane)) return IID.IID_IReferencePlane; + if (typeof(T) == typeof(global::System.Numerics.Quaternion)) return IID.IID_IReferenceQuaternion; + if (typeof(T) == typeof(global::System.Numerics.Vector2)) return IID.IID_IReferenceVector2; + if (typeof(T) == typeof(global::System.Numerics.Vector3)) return IID.IID_IReferenceVector3; + if (typeof(T) == typeof(global::System.Numerics.Vector4)) return IID.IID_IReferenceVector4; - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, DateTimeOffset* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, DateTimeOffset* __return_value__) - { - global::System.DateTimeOffset ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (global::System.DateTimeOffset)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = DateTimeOffset.FromManaged(____return_value__); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static global::System.DateTimeOffset GetValue(IInspectable inspectable) + return GuidGenerator.CreateIIDUnsafe(typeof(Nullable)); + } + + public static Func GetValueFactory(global::System.Type type) { - IntPtr nullablePtr = IntPtr.Zero; - DateTimeOffset __retval = default; - try + if (!FeatureSwitches.EnableIReferenceSupport) { - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return DateTimeOffset.FromAbi(__retval); + throw new NotSupportedException("Support for 'IReference' is not enabled."); } - finally + + return ComWrappersSupport.CreateReferenceCachingFactory(GetValueFactoryInternal(type)); + } + + private static Func GetValueFactoryInternal(global::System.Type type) + { + if (type == typeof(string)) return Nullable_string.GetValue; + if (type == typeof(int)) return NullableBlittable.GetValue; + if (type == typeof(byte)) return NullableBlittable.GetValue; + if (type == typeof(bool)) return NullableBlittable.GetValue; + if (type == typeof(sbyte)) return NullableBlittable.GetValue; + if (type == typeof(short)) return NullableBlittable.GetValue; + if (type == typeof(ushort)) return NullableBlittable.GetValue; + if (type == typeof(char)) return NullableBlittable.GetValue; + if (type == typeof(uint)) return NullableBlittable.GetValue; + if (type == typeof(long)) return NullableBlittable.GetValue; + if (type == typeof(ulong)) return NullableBlittable.GetValue; + if (type == typeof(float)) return NullableBlittable.GetValue; + if (type == typeof(double)) return NullableBlittable.GetValue; + if (type == typeof(Guid)) return NullableBlittable.GetValue; + if (type == typeof(global::System.Type)) return Nullable_Type.GetValue; + if (type == typeof(global::System.TimeSpan)) return Nullable_TimeSpan.GetValue; + if (type == typeof(global::System.Exception)) return Nullable_Exception.GetValue; + if (type == typeof(global::System.DateTimeOffset)) return Nullable_DateTimeOffset.GetValue; + if (type == typeof(global::Windows.Foundation.Point)) return NullableBlittable.GetValue; + if (type == typeof(global::Windows.Foundation.Size)) return NullableBlittable.GetValue; + if (type == typeof(global::Windows.Foundation.Rect)) return NullableBlittable.GetValue; + if (type == typeof(global::System.Numerics.Matrix3x2)) return NullableBlittable.GetValue; + if (type == typeof(global::System.Numerics.Matrix4x4)) return NullableBlittable.GetValue; + if (type == typeof(global::System.Numerics.Plane)) return NullableBlittable.GetValue; + if (type == typeof(global::System.Numerics.Quaternion)) return NullableBlittable.GetValue; + if (type == typeof(global::System.Numerics.Vector2)) return NullableBlittable.GetValue; + if (type == typeof(global::System.Numerics.Vector3)) return NullableBlittable.GetValue; + if (type == typeof(global::System.Numerics.Vector4)) return NullableBlittable.GetValue; + if (type == typeof(global::System.EventHandler)) return Nullable_EventHandler.GetValue; + if (type == typeof(global::System.ComponentModel.PropertyChangedEventHandler)) return Nullable_PropertyChangedEventHandler.GetValue; + if (type == typeof(global::System.Collections.Specialized.NotifyCollectionChangedEventHandler)) return Nullable_NotifyCollectionChangedEventHandler.GetValue; + if (type.IsEnum && Enum.GetUnderlyingType(type) == typeof(int)) return (inspectable) => Nullable_IntEnum.GetValue(type, inspectable); + if (type.IsEnum && Enum.GetUnderlyingType(type) == typeof(uint)) return (inspectable) => Nullable_FlagsEnum.GetValue(type, inspectable); + +#if NET + var winrtExposedClassAttribute = type.GetCustomAttribute(false); + if (winrtExposedClassAttribute == null) { - DateTimeOffset.DisposeAbi(__retval); - Marshal.Release(nullablePtr); + var authoringMetadaType = type.GetAuthoringMetadataType(); + if (authoringMetadaType != null) + { + winrtExposedClassAttribute = authoringMetadaType.GetCustomAttribute(false); + } } - } - } - [Guid("604d0c4c-91de-5c2a-935f-362f13eaf800")] - internal static class Nullable_TimeSpan - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.Foundation.TimeSpan;i8))"; - internal static Guid IID = new(0x604d0c4c, 0x91de, 0x5c2a, 0x93, 0x5f, 0x36, 0x2f, 0x13, 0xea, 0xf8, 0x00); - - [Guid("604d0c4c-91de-5c2a-935f-362f13eaf800")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + if (winrtExposedClassAttribute != null && winrtExposedClassAttribute.WinRTExposedTypeDetails != null) + { + if (Activator.CreateInstance(winrtExposedClassAttribute.WinRTExposedTypeDetails) is IWinRTNullableTypeDetails nullableTypeDetails) + { + return nullableTypeDetails.GetNullableValue; + } + } + + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"Failed to get the value from nullable with type '{type}'."); + } +#endif + + // Fallback for .NET standard and pre-existing projections. +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + if (type.IsDelegate()) + { + return ComWrappersSupport.CreateAbiNullableTFactory(typeof(Nullable_Delegate<>).MakeGenericType(type)); + } + else + { + return ComWrappersSupport.CreateNullableTFactory(typeof(global::System.Nullable<>).MakeGenericType(type)); + } +#pragma warning restore IL3050 + } - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, TimeSpan* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, TimeSpan* __return_value__) - { - global::System.TimeSpan ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (global::System.TimeSpan)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = TimeSpan.FromManaged(____return_value__); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static global::System.TimeSpan GetValue(IInspectable inspectable) + // Gets the nullable type representation for some built-in known types. + public static global::System.Type GetTypeAsNullableType(global::System.Type type) { - IntPtr nullablePtr = IntPtr.Zero; - TimeSpan __retval = default; - try + if (!FeatureSwitches.EnableIReferenceSupport) { - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return TimeSpan.FromAbi(__retval); + throw new NotSupportedException("Support for 'IReference' is not enabled."); } - finally + + if (type == typeof(int)) return typeof(global::System.Nullable); + if (type == typeof(byte)) return typeof(global::System.Nullable); + if (type == typeof(bool)) return typeof(global::System.Nullable); + if (type == typeof(sbyte)) return typeof(global::System.Nullable); + if (type == typeof(short)) return typeof(global::System.Nullable); + if (type == typeof(ushort)) return typeof(global::System.Nullable); + if (type == typeof(char)) return typeof(global::System.Nullable); + if (type == typeof(uint)) return typeof(global::System.Nullable); + if (type == typeof(long)) return typeof(global::System.Nullable); + if (type == typeof(ulong)) return typeof(global::System.Nullable); + if (type == typeof(float)) return typeof(global::System.Nullable); + if (type == typeof(double)) return typeof(global::System.Nullable); + if (type == typeof(Guid)) return typeof(global::System.Nullable); + if (type == typeof(global::System.TimeSpan)) return typeof(global::System.Nullable); + if (type == typeof(global::System.DateTimeOffset)) return typeof(global::System.Nullable); + if (type == typeof(global::Windows.Foundation.Point)) return typeof(global::System.Nullable); + if (type == typeof(global::Windows.Foundation.Size)) return typeof(global::System.Nullable); + if (type == typeof(global::Windows.Foundation.Rect)) return typeof(global::System.Nullable); + if (type == typeof(global::System.Numerics.Matrix3x2)) return typeof(global::System.Nullable); + if (type == typeof(global::System.Numerics.Matrix4x4)) return typeof(global::System.Nullable); + if (type == typeof(global::System.Numerics.Plane)) return typeof(global::System.Nullable); + if (type == typeof(global::System.Numerics.Quaternion)) return typeof(global::System.Nullable); + if (type == typeof(global::System.Numerics.Vector2)) return typeof(global::System.Nullable); + if (type == typeof(global::System.Numerics.Vector3)) return typeof(global::System.Nullable); + if (type == typeof(global::System.Numerics.Vector4)) return typeof(global::System.Nullable); + +#if NET + var winrtExposedClassAttribute = type.GetCustomAttribute(false); + if (winrtExposedClassAttribute == null) { - TimeSpan.DisposeAbi(__retval); - Marshal.Release(nullablePtr); + var authoringMetadaType = type.GetAuthoringMetadataType(); + if (authoringMetadaType != null) + { + winrtExposedClassAttribute = authoringMetadaType.GetCustomAttribute(false); + } } - } + + if (winrtExposedClassAttribute != null && winrtExposedClassAttribute.WinRTExposedTypeDetails != null) + { + if (Activator.CreateInstance(winrtExposedClassAttribute.WinRTExposedTypeDetails) is IWinRTNullableTypeDetails nullableTypeDetails) + { + return nullableTypeDetails.GetNullableType(); + } + } + + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + throw new NotSupportedException($"Failed to construct nullable with type '{type}'."); + } +#endif + + return null; + } } - [Guid("06dccc90-a058-5c88-87b7-6f3360a2fc16")] - internal static class Nullable_Object - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};cinterface(IInspectable))"; - internal static Guid IID = new(0x06dccc90, 0xa058, 0x5c88, 0x87, 0xb7, 0x6f, 0x33, 0x60, 0xa2, 0xfc, 0x16); - - [Guid("06dccc90-a058-5c88-87b7-6f3360a2fc16")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + internal static class IReferenceSignatures + { + public const string Point = "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.Foundation.Point;f4;f4))"; + public const string Size = "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.Foundation.Size;f4;f4))"; + public const string Rect = "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.Foundation.Rect;f4;f4;f4;f4))"; + public const string Matrix3x2 = "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.Foundation.Numerics.Matrix3x2;f4;f4;f4;f4;f4;f4))"; + public const string Matrix4x4 = "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.Foundation.Numerics.Matrix4x4;f4;f4;f4;f4;f4;f4;f4;f4;f4;f4;f4;f4;f4;f4;f4;f4))"; + public const string Plane = "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.Foundation.Numerics.Plane;struct(Windows.Foundation.Numerics.Vector3;f4;f4;f4);f4))"; + public const string Quaternion = "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.Foundation.Numerics.Quaternion;f4;f4;f4;f4))"; + public const string Vector2 = "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.Foundation.Numerics.Vector2;f4;f4))"; + public const string Vector3 = "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.Foundation.Numerics.Vector3;f4;f4;f4))"; + public const string Vector4 = "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.Foundation.Numerics.Vector4;f4;f4;f4;f4))"; + } +} - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, IntPtr* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, IntPtr* __return_value__) - { - object ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = MarshalInspectable.FromManaged(____return_value__); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } +#if NET + namespace WinRT +{ + internal interface IWinRTNullableTypeDetails + { + object GetNullableValue(IInspectable inspectable); + Type GetNullableType(); } - [Guid("3830ad99-d8da-53f3-989b-fc92ad222778")] - internal static class Nullable_Type - { - public static string GetGuidSignature() => "pinterface({61c17706-2d65-11e0-9ae8-d48564015472};struct(Windows.UI.Xaml.Interop.TypeName;string;enum(Windows.UI.Xaml.Interop.TypeKind;i4)))"; - internal static Guid IID = new(0x3830ad99, 0xd8da, 0x53f3, 0x98, 0x9b, 0xfc, 0x92, 0xad, 0x22, 0x27, 0x78); - - [Guid("3830ad99-d8da-53f3-989b-fc92ad222778")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _Get_Value_0; - public delegate* unmanaged[Stdcall] Get_Value_0 { get => (delegate* unmanaged[Stdcall])_Get_Value_0; set => _Get_Value_0 = value; } + public sealed class StructTypeDetails : IWinRTExposedTypeDetails, IWinRTNullableTypeDetails + where T: struct + where TAbi : unmanaged + { + private static readonly Guid PIID = ABI.System.Nullable.PIID; + + [SkipLocalsInit] + public ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + { + Span entries = stackalloc ComWrappers.ComInterfaceEntry[2]; + int count = 0; + + if (FeatureSwitches.EnableIReferenceSupport) + { + entries[count++] = new ComWrappers.ComInterfaceEntry + { + IID = global::WinRT.Interop.IID.IID_IPropertyValue, + Vtable = ABI.Windows.Foundation.ManagedIPropertyValueImpl.AbiToProjectionVftablePtr + }; + + entries[count++] = new ComWrappers.ComInterfaceEntry + { + IID = PIID, + Vtable = ABI.Windows.Foundation.BoxedValueIReferenceImpl.AbiToProjectionVftablePtr + }; + } + + return entries.Slice(0, count).ToArray(); + } - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - -#if !NET - private unsafe delegate int GetValueDelegate(IntPtr thisPtr, Type* value); - private static readonly GetValueDelegate delegateCache; -#endif - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, -#if !NET - _Get_Value_0 = Marshal.GetFunctionPointerForDelegate(delegateCache = Do_Abi_get_Value_0).ToPointer() -#else - _Get_Value_0 = (delegate* unmanaged)&Do_Abi_get_Value_0 -#endif - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable, (IntPtr)nativeVftbl, false); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - -#if NET - [UnmanagedCallersOnly] -#endif - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, Type* __return_value__) - { - global::System.Type ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = (global::System.Type)global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = Type.FromManaged(____return_value__); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - unsafe internal static Nullable GetValue(IInspectable inspectable) + unsafe object IWinRTNullableTypeDetails.GetNullableValue(IInspectable inspectable) { IntPtr nullablePtr = IntPtr.Zero; - Type __retval = default; + TAbi __retval = default; try { - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref IID, out nullablePtr)); - ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return new Nullable(Type.FromAbi(__retval)); + ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in PIID), out nullablePtr)); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); + + if (typeof(T) == typeof(TAbi)) + { + return __retval; + } + else + { + // If 'typeof(T) != typeof(TAbi)', then we know the type is not blittable. + // We can use the specific marshaller then to minimize binary size here. + return MarshalNonBlittable.FromAbi(__retval); + } } finally { - Type.DisposeAbi(__retval); - Marshal.Release(nullablePtr); + // We only need to dispose in the non blittable case. Otherwise, + // that value is returned directly to the caller and used as is. + if (typeof(T) != typeof(TAbi)) + { + MarshalNonBlittable.DisposeAbi(__retval); + } + + MarshalExtensions.ReleaseIfNotNull(nullablePtr); } - } + } + + Type IWinRTNullableTypeDetails.GetNullableType() => typeof(global::System.Nullable); } - [Guid("61C17706-2D65-11E0-9AE8-D48564015472")] - internal static class Nullable_Delegate where T : global::System.Delegate + public abstract class DelegateTypeDetails : IWinRTExposedTypeDetails, IWinRTNullableTypeDetails where T : global::System.Delegate { - public static string GetGuidSignature() => GuidGenerator.GetSignature(typeof(Nullable)); - - [Guid("61C17706-2D65-11E0-9AE8-D48564015472")] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private Nullable_Delegates.GetValueDelegate _Get_Value_0; - - public static Guid PIID = GuidGenerator.CreateIID(typeof(Nullable)); - - private static readonly Vftbl AbiToProjectionVftable; - public static readonly IntPtr AbiToProjectionVftablePtr; - - static Vftbl() - { - AbiToProjectionVftable = new Vftbl - { - IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, - _Get_Value_0 = Do_Abi_get_Value_0 - }; - var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); - Marshal.StructureToPtr(AbiToProjectionVftable.IInspectableVftbl, (IntPtr)nativeVftbl, false); - nativeVftbl[6] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable._Get_Value_0); - AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; - } - - private static unsafe int Do_Abi_get_Value_0(IntPtr thisPtr, IntPtr* __return_value__) - { - T ____return_value__ = default; - - *__return_value__ = default; - - try - { - ____return_value__ = global::WinRT.ComWrappersSupport.FindObject(thisPtr); - *__return_value__ = (IntPtr)Marshaler.FromManaged(____return_value__); - } - catch (global::System.Exception __exception__) - { - global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); - return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); - } - return 0; - } - } - - public static Guid PIID = Vftbl.PIID; - - unsafe internal static Nullable GetValue(IInspectable inspectable) + private static readonly Guid PIID = ABI.System.Nullable.PIID; + + public ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + { + return GetExposedInterfaces(GetDelegateInterface()); + } + + public static ComWrappers.ComInterfaceEntry[] GetExposedInterfaces(ComWrappers.ComInterfaceEntry delegateInterface) + { + // We are here making note of the delegate IID to use later on as part of our Nullable_Delegate + // support. This allows us to make use of the same vtable for all delegates rather than + // constructing a new one for each delegate type with GetFunctionPointerForDelegate. + // We are not doing our own custom check for whether we have already added this type + // because we expect this function to only be called once per type due to vtable caching. + ABI.System.Nullable_Delegate.DelegateGuidMapping.TryAdd(typeof(T), delegateInterface.IID); + + Span entries = stackalloc ComWrappers.ComInterfaceEntry[3]; + int count = 0; + + entries[count++] = delegateInterface; + + if (FeatureSwitches.EnableIReferenceSupport) + { + entries[count++] = new ComWrappers.ComInterfaceEntry + { + IID = global::WinRT.Interop.IID.IID_IPropertyValue, + Vtable = ABI.Windows.Foundation.ManagedIPropertyValueImpl.AbiToProjectionVftablePtr + }; + + entries[count++] = new ComWrappers.ComInterfaceEntry + { + IID = PIID, + Vtable = ABI.System.Nullable_Delegate.AbiToProjectionVftablePtr + }; + } + + return entries.Slice(0, count).ToArray(); + } + + public abstract ComWrappers.ComInterfaceEntry GetDelegateInterface(); + + unsafe object IWinRTNullableTypeDetails.GetNullableValue(IInspectable inspectable) { IntPtr nullablePtr = IntPtr.Zero; IntPtr __retval = default; try { - ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref PIID, out nullablePtr)); + ExceptionHelpers.ThrowExceptionForHR(Marshal.QueryInterface(inspectable.ThisPtr, ref Unsafe.AsRef(in PIID), out nullablePtr)); ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)nullablePtr)[6](nullablePtr, &__retval)); - return new Nullable(Marshaler.FromAbi(__retval)); + return new ABI.System.Nullable(MarshalDelegate.FromAbi(__retval)); } finally { - Marshaler.DisposeAbi(__retval); - Marshal.Release(nullablePtr); + MarshalExtensions.ReleaseIfNotNull(__retval); + MarshalExtensions.ReleaseIfNotNull(nullablePtr); } - } - } - - internal static class Nullable_Delegates + } + + // Delegates are handled separately. + Type IWinRTNullableTypeDetails.GetNullableType() => throw new NotImplementedException(); + } + + public sealed class EnumTypeDetails : IWinRTExposedTypeDetails, IWinRTNullableTypeDetails where T : unmanaged, Enum { - public unsafe delegate int GetValueDelegate(IntPtr thisPtr, IntPtr* value); - } -} + [SkipLocalsInit] + public ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + { + Span entries = stackalloc ComWrappers.ComInterfaceEntry[2]; + int count = 0; + + if (FeatureSwitches.EnableIReferenceSupport) + { + entries[count++] = new ComWrappers.ComInterfaceEntry + { + IID = global::WinRT.Interop.IID.IID_IPropertyValue, + Vtable = ABI.Windows.Foundation.ManagedIPropertyValueImpl.AbiToProjectionVftablePtr + }; + + if (typeof(T).IsDefined(typeof(FlagsAttribute))) + { + entries[count++] = new ComWrappers.ComInterfaceEntry + { + IID = ABI.System.Nullable_FlagsEnum.GetIID(typeof(T)), + Vtable = ABI.System.Nullable_FlagsEnum.AbiToProjectionVftablePtr + }; + } + else + { + entries[count++] = new ComWrappers.ComInterfaceEntry + { + IID = ABI.System.Nullable_IntEnum.GetIID(typeof(T)), + Vtable = ABI.System.Nullable_IntEnum.AbiToProjectionVftablePtr + }; + } + } + + return entries.Slice(0, count).ToArray(); + } + + Type IWinRTNullableTypeDetails.GetNullableType() => typeof(global::System.Nullable); + + // Unboxing enums are handled separately. + object IWinRTNullableTypeDetails.GetNullableValue(IInspectable inspectable) => throw new NotImplementedException(); + } +} +#endif diff --git a/src/WinRT.Runtime/Projections/PropertyChangedEventArgs.cs b/src/WinRT.Runtime/Projections/PropertyChangedEventArgs.cs index a29aafc68..16d374b6f 100644 --- a/src/WinRT.Runtime/Projections/PropertyChangedEventArgs.cs +++ b/src/WinRT.Runtime/Projections/PropertyChangedEventArgs.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; @@ -9,43 +9,22 @@ namespace ABI.Microsoft.UI.Xaml.Data { - [Guid("63D0C952-396B-54F4-AF8C-BA8724A427BF")] - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct IPropertyChangedEventArgsVftbl + internal sealed unsafe class PropertyChangedEventArgsRuntimeClassFactory { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _get_PropertyName_0; - public delegate* unmanaged[Stdcall] get_PropertyName_0 => (delegate* unmanaged[Stdcall])_get_PropertyName_0; - } - + private readonly IObjectReference _obj; - [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] - [Guid("7C0C27A8-0B41-5070-B160-FC9AE960A36C")] - internal sealed unsafe class WinRTPropertyChangedEventArgsRuntimeClassFactory - { - [Guid("7C0C27A8-0B41-5070-B160-FC9AE960A36C")] - [StructLayout(LayoutKind.Sequential)] - public struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _CreateInstance_0; - public delegate* unmanaged[Stdcall] CreateInstance_0 => (delegate* unmanaged[Stdcall])_CreateInstance_0; - } - public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator WinRTPropertyChangedEventArgsRuntimeClassFactory(IObjectReference obj) => (obj != null) ? new WinRTPropertyChangedEventArgsRuntimeClassFactory(obj) : null; - public static implicit operator WinRTPropertyChangedEventArgsRuntimeClassFactory(ObjectReference obj) => (obj != null) ? new WinRTPropertyChangedEventArgsRuntimeClassFactory(obj) : null; - private readonly ObjectReference _obj; - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public WinRTPropertyChangedEventArgsRuntimeClassFactory(IObjectReference obj) : this(obj.As()) { } - public WinRTPropertyChangedEventArgsRuntimeClassFactory(ObjectReference obj) + public PropertyChangedEventArgsRuntimeClassFactory() { - _obj = obj; +#if NET + _obj = FeatureSwitches.UseWindowsUIXamlProjections + ? ActivationFactory.Get("Windows.UI.Xaml.Data.PropertyChangedEventArgs", IID.IID_WUX_PropertyChangedEventArgsRuntimeClassFactory) + : ActivationFactory.Get("Microsoft.UI.Xaml.Data.PropertyChangedEventArgs", IID.IID_MUX_PropertyChangedEventArgsRuntimeClassFactory); +#else + _obj = ActivationFactory.Get("Microsoft.UI.Xaml.Data.PropertyChangedEventArgs", IID.IID_MUX_PropertyChangedEventArgsRuntimeClassFactory); +#endif } - public unsafe IObjectReference CreateInstance(string name, object baseInterface, out IObjectReference innerInterface) + public IObjectReference CreateInstance(string name, object baseInterface, out IObjectReference innerInterface) { IObjectReference __baseInterface = default; IntPtr __innerInterface = default; @@ -54,11 +33,19 @@ public unsafe IObjectReference CreateInstance(string name, object baseInterface, { MarshalString.Pinnable __name = new(name); fixed (void* ___name = __name) - { + { __baseInterface = MarshalInspectable.CreateMarshaler(baseInterface); - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.CreateInstance_0(ThisPtr, MarshalString.GetAbi(ref __name), MarshalInspectable.GetAbi(__baseInterface), &__innerInterface, &__retval)); - innerInterface = ObjectReference.FromAbi(__innerInterface); - return ObjectReference.Attach(ref __retval); + IntPtr thisPtr = _obj.ThisPtr; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR( + ((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[6])( + thisPtr, + MarshalString.GetAbi(ref __name), + MarshalInspectable.GetAbi(__baseInterface), + &__innerInterface, + &__retval)); + global::System.GC.KeepAlive(_obj); + innerInterface = ObjectReference.FromAbi(__innerInterface, IID.IID_IUnknown); + return ObjectReference.Attach(ref __retval, IID.IID_IUnknown); } } finally @@ -67,9 +54,9 @@ public unsafe IObjectReference CreateInstance(string name, object baseInterface, MarshalInspectable.DisposeAbi(__innerInterface); MarshalInspectable.DisposeAbi(__retval); } - } - - public unsafe ObjectReferenceValue CreateInstance(string name) + } + + public ObjectReferenceValue CreateInstance(string name) { IntPtr __innerInterface = default; IntPtr __retval = default; @@ -77,8 +64,16 @@ public unsafe ObjectReferenceValue CreateInstance(string name) { MarshalString.Pinnable __name = new(name); fixed (void* ___name = __name) - { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.CreateInstance_0(ThisPtr, MarshalString.GetAbi(ref __name), IntPtr.Zero, &__innerInterface, &__retval)); + { + IntPtr thisPtr = _obj.ThisPtr; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR( + ((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[6])( + thisPtr, + MarshalString.GetAbi(ref __name), + IntPtr.Zero, + &__innerInterface, + &__retval)); + global::System.GC.KeepAlive(_obj); return new ObjectReferenceValue(__retval); } } @@ -87,7 +82,6 @@ public unsafe ObjectReferenceValue CreateInstance(string name) MarshalInspectable.DisposeAbi(__innerInterface); } } - } } @@ -102,15 +96,7 @@ namespace ABI.System.ComponentModel #endif unsafe struct PropertyChangedEventArgs { - private sealed class ActivationFactory : BaseActivationFactory - { - public ActivationFactory() : base("Microsoft.UI.Xaml.Data", "Microsoft.UI.Xaml.Data.PropertyChangedEventArgs") - { - } - - internal static ABI.Microsoft.UI.Xaml.Data.WinRTPropertyChangedEventArgsRuntimeClassFactory Instance = - new ActivationFactory()._As(); - } + private static readonly ABI.Microsoft.UI.Xaml.Data.PropertyChangedEventArgsRuntimeClassFactory Instance = new(); public static IObjectReference CreateMarshaler(global::System.ComponentModel.PropertyChangedEventArgs value) { @@ -119,9 +105,9 @@ public static IObjectReference CreateMarshaler(global::System.ComponentModel.Pro return null; } - return ActivationFactory.Instance.CreateInstance(value.PropertyName, null, out _); - } - + return Instance.CreateInstance(value.PropertyName, null, out _); + } + public static ObjectReferenceValue CreateMarshaler2(global::System.ComponentModel.PropertyChangedEventArgs value) { if (value is null) @@ -129,7 +115,7 @@ public static ObjectReferenceValue CreateMarshaler2(global::System.ComponentMode return new ObjectReferenceValue(); } - return ActivationFactory.Instance.CreateInstance(value.PropertyName); + return Instance.CreateInstance(value.PropertyName); } public static IntPtr GetAbi(IObjectReference m) => m?.ThisPtr ?? IntPtr.Zero; @@ -144,7 +130,9 @@ public static ObjectReferenceValue CreateMarshaler2(global::System.ComponentMode IntPtr propertyName = IntPtr.Zero; try { - ExceptionHelpers.ThrowExceptionForHR((**(ABI.Microsoft.UI.Xaml.Data.IPropertyChangedEventArgsVftbl**)ptr).get_PropertyName_0(ptr, &propertyName)); + // We can use the Microsoft.UI.Xaml.Data.IPropertyChangedEventArgsVftbl here in both WUX and MUX because the vtables are laid out the same and we know + // that we have either a MUX or WUX IPropertyChangedEventArgs pointer in ptr. + ExceptionHelpers.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)ptr)[6])(ptr, &propertyName)); return new global::System.ComponentModel.PropertyChangedEventArgs(MarshalString.FromAbi(propertyName)); } finally @@ -170,9 +158,22 @@ public static IntPtr FromManaged(global::System.ComponentModel.PropertyChangedEv public static void DisposeMarshaler(IObjectReference m) { m?.Dispose(); } public static void DisposeAbi(IntPtr abi) { MarshalInspectable.DisposeAbi(abi); } + public static unsafe MarshalInterfaceHelper.MarshalerArray CreateMarshalerArray(global::System.ComponentModel.PropertyChangedEventArgs[] array) => MarshalInterfaceHelper.CreateMarshalerArray2(array, CreateMarshaler2); + public static (int length, IntPtr data) GetAbiArray(object box) => MarshalInterfaceHelper.GetAbiArray(box); + public static unsafe global::System.ComponentModel.PropertyChangedEventArgs[] FromAbiArray(object box) => MarshalInterfaceHelper.FromAbiArray(box, FromAbi); + public static void CopyAbiArray(global::System.ComponentModel.PropertyChangedEventArgs[] array, object box) => MarshalInterfaceHelper.CopyAbiArray(array, box, FromAbi); + public static (int length, IntPtr data) FromManagedArray(global::System.ComponentModel.PropertyChangedEventArgs[] array) => MarshalInterfaceHelper.FromManagedArray(array, FromManaged); + public static void DisposeMarshalerArray(MarshalInterfaceHelper.MarshalerArray array) => MarshalInterfaceHelper.DisposeMarshalerArray(array); + public static unsafe void DisposeAbiArray(object box) => MarshalInspectable.DisposeAbiArray(box); + public static string GetGuidSignature() { - return "rc(Microsoft.UI.Xaml.Data.NotifyPropertyChangedEventArgs;{4f33a9a0-5cf4-47a4-b16f-d7faaf17457e})"; + if (FeatureSwitches.UseWindowsUIXamlProjections) + { + return "rc(Windows.UI.Xaml.Data.PropertyChangedEventArgs;{4f33a9a0-5cf4-47a4-b16f-d7faaf17457e})"; + } + + return "rc(Microsoft.UI.Xaml.Data.PropertyChangedEventArgs;{63d0c952-396b-54f4-af8c-ba8724a427bf})"; } } } diff --git a/src/WinRT.Runtime/Projections/PropertyChangedEventHandler.cs b/src/WinRT.Runtime/Projections/PropertyChangedEventHandler.cs index b6ee19bda..b93199ae2 100644 --- a/src/WinRT.Runtime/Projections/PropertyChangedEventHandler.cs +++ b/src/WinRT.Runtime/Projections/PropertyChangedEventHandler.cs @@ -1,4 +1,4 @@ -// Copyright (c) Microsoft Corporation. +// Copyright (c) Microsoft Corporation. // Licensed under the MIT License. using System; @@ -12,6 +12,7 @@ namespace ABI.System.ComponentModel { [global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] [Guid("E3DE52F6-1E32-5DA6-BB2D-B5B6096C962D")] + [WuxMuxProjectedType] #if EMBED internal #else @@ -37,35 +38,38 @@ static unsafe PropertyChangedEventHandler() Invoke = (IntPtr)(delegate* unmanaged[Stdcall])&Do_Abi_Invoke #endif }; - var nativeVftbl = ComWrappersSupport.AllocateVtableMemory(typeof(PropertyChangedEventHandler), Marshal.SizeOf()); - Marshal.StructureToPtr(AbiToProjectionVftable, nativeVftbl, false); + var nativeVftbl = ComWrappersSupport.AllocateVtableMemory(typeof(PropertyChangedEventHandler), sizeof(global::WinRT.Interop.IDelegateVftbl)); + *(IDelegateVftbl*)nativeVftbl = AbiToProjectionVftable; AbiToProjectionVftablePtr = nativeVftbl; + ComWrappersSupport.RegisterDelegateFactory(typeof(global::System.ComponentModel.PropertyChangedEventHandler), CreateRcw); } - public static global::System.Delegate AbiInvokeDelegate { get; } - - private static readonly Guid IID = new(0xE3DE52F6, 0x1E32, 0x5DA6, 0xBB, 0x2D, 0xB5, 0xB6, 0x09, 0x6C, 0x96, 0x2D); + public static global::System.Delegate AbiInvokeDelegate { get; } + + public static Guid IID => FeatureSwitches.UseWindowsUIXamlProjections + ? global::WinRT.Interop.IID.IID_WUX_PropertyChangedEventHandler + : global::WinRT.Interop.IID.IID_MUX_PropertyChangedEventHandler; public static unsafe IObjectReference CreateMarshaler(global::System.ComponentModel.PropertyChangedEventHandler managedDelegate) => - managedDelegate is null ? null : MarshalDelegate.CreateMarshaler(managedDelegate, IID); - + managedDelegate is null ? null : MarshalDelegate.CreateMarshaler(managedDelegate, IID); + public static unsafe ObjectReferenceValue CreateMarshaler2(global::System.ComponentModel.PropertyChangedEventHandler managedDelegate) => MarshalDelegate.CreateMarshaler2(managedDelegate, IID); public static IntPtr GetAbi(IObjectReference value) => MarshalInterfaceHelper.GetAbi(value); public static unsafe global::System.ComponentModel.PropertyChangedEventHandler FromAbi(IntPtr nativeDelegate) - { + { return MarshalDelegate.FromAbi(nativeDelegate); - } - - public static global::System.ComponentModel.PropertyChangedEventHandler CreateRcw(IntPtr ptr) - { - return new global::System.ComponentModel.PropertyChangedEventHandler(new NativeDelegateWrapper(ComWrappersSupport.GetObjectReferenceForInterface(ptr, IID)).Invoke); } - [global::WinRT.ObjectReferenceWrapper(nameof(_nativeDelegate))] + public static global::System.ComponentModel.PropertyChangedEventHandler CreateRcw(IntPtr ptr) + { + return new global::System.ComponentModel.PropertyChangedEventHandler(new NativeDelegateWrapper(ComWrappersSupport.GetObjectReferenceForInterface(ptr, IID)).Invoke); + } + #if !NET + [global::WinRT.ObjectReferenceWrapper(nameof(_nativeDelegate))] private sealed class NativeDelegateWrapper #else private sealed class NativeDelegateWrapper : IWinRTObject @@ -113,6 +117,7 @@ public unsafe void Invoke(object sender, global::System.ComponentModel.PropertyC __sender = MarshalInspectable.CreateMarshaler2(sender); __e = global::ABI.System.ComponentModel.PropertyChangedEventArgs.CreateMarshaler2(e); global::WinRT.ExceptionHelpers.ThrowExceptionForHR(abiInvoke(ThisPtr, MarshalInspectable.GetAbi(__sender), MarshalInspectable.GetAbi(__e))); + global::System.GC.KeepAlive(_nativeDelegate); } finally { @@ -129,13 +134,21 @@ public static IntPtr FromManaged(global::System.ComponentModel.PropertyChangedEv public static void DisposeAbi(IntPtr abi) => MarshalInterfaceHelper.DisposeAbi(abi); + public static unsafe MarshalInterfaceHelper.MarshalerArray CreateMarshalerArray(global::System.ComponentModel.PropertyChangedEventHandler[] array) => MarshalInterfaceHelper.CreateMarshalerArray2(array, CreateMarshaler2); + public static (int length, IntPtr data) GetAbiArray(object box) => MarshalInterfaceHelper.GetAbiArray(box); + public static unsafe global::System.ComponentModel.PropertyChangedEventHandler[] FromAbiArray(object box) => MarshalInterfaceHelper.FromAbiArray(box, FromAbi); + public static void CopyAbiArray(global::System.ComponentModel.PropertyChangedEventHandler[] array, object box) => MarshalInterfaceHelper.CopyAbiArray(array, box, FromAbi); + public static (int length, IntPtr data) FromManagedArray(global::System.ComponentModel.PropertyChangedEventHandler[] array) => MarshalInterfaceHelper.FromManagedArray(array, FromManaged); + public static void DisposeMarshalerArray(MarshalInterfaceHelper.MarshalerArray array) => MarshalInterfaceHelper.DisposeMarshalerArray(array); + public static unsafe void DisposeAbiArray(object box) => MarshalInspectable.DisposeAbiArray(box); + #if NET [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] #endif private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr sender, IntPtr e) { try - { + { #if NET var invoke = ComWrappersSupport.FindObject(thisPtr); invoke.Invoke(MarshalInspectable.FromAbi(sender), global::ABI.System.ComponentModel.PropertyChangedEventArgs.FromAbi(e)); @@ -153,40 +166,63 @@ private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr sender, IntPtr e) } return 0; } + +#if NET + [SkipLocalsInit] + internal static ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + { + Span entries = stackalloc ComWrappers.ComInterfaceEntry[3]; + int count = 0; + + entries[count++] = new ComWrappers.ComInterfaceEntry + { + IID = IID, + Vtable = AbiToProjectionVftablePtr + }; + + if (FeatureSwitches.EnableIReferenceSupport) + { + entries[count++] = new ComWrappers.ComInterfaceEntry + { + IID = global::WinRT.Interop.IID.IID_IPropertyValue, + Vtable = ABI.Windows.Foundation.ManagedIPropertyValueImpl.AbiToProjectionVftablePtr + }; + + entries[count++] = new ComWrappers.ComInterfaceEntry + { + IID = global::ABI.System.Nullable_PropertyChangedEventHandler.IID, + Vtable = global::ABI.System.Nullable_PropertyChangedEventHandler.AbiToProjectionVftablePtr + }; + } + + return entries.Slice(0, count).ToArray(); + } +#endif } - internal sealed unsafe class PropertyChangedEventSource : EventSource + internal sealed unsafe class PropertyChangedEventSource : global::ABI.WinRT.Interop.EventSource { - internal PropertyChangedEventSource(IObjectReference obj, - delegate* unmanaged[Stdcall] addHandler, - delegate* unmanaged[Stdcall] removeHandler) - : base(obj, addHandler, removeHandler) + internal PropertyChangedEventSource(IObjectReference objectReference, int vtableIndexForAddHandler) + : base(objectReference, vtableIndexForAddHandler) { } protected override ObjectReferenceValue CreateMarshaler(global::System.ComponentModel.PropertyChangedEventHandler del) => PropertyChangedEventHandler.CreateMarshaler2(del); - protected override State CreateEventState() => - new EventState(_obj.ThisPtr, _index); + protected override global::ABI.WinRT.Interop.EventSourceState CreateEventSourceState() => + new EventState(ObjectReference.ThisPtr, Index); - private sealed class EventState : State + private sealed class EventState : global::ABI.WinRT.Interop.EventSourceState { public EventState(IntPtr obj, int index) : base(obj, index) { } - protected override Delegate GetEventInvoke() + protected override global::System.ComponentModel.PropertyChangedEventHandler GetEventInvoke() { - global::System.ComponentModel.PropertyChangedEventHandler handler = - (global::System.Object obj, global::System.ComponentModel.PropertyChangedEventArgs e) => - { - var localDel = (global::System.ComponentModel.PropertyChangedEventHandler) del; - if (localDel != null) - localDel.Invoke(obj, e); - }; - return handler; + return (obj, e) => targetDelegate?.Invoke(obj, e); } } } diff --git a/src/WinRT.Runtime/Projections/Type.ArrayMarshalling.cs b/src/WinRT.Runtime/Projections/Type.ArrayMarshalling.cs new file mode 100644 index 000000000..c7dc49428 --- /dev/null +++ b/src/WinRT.Runtime/Projections/Type.ArrayMarshalling.cs @@ -0,0 +1,187 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace ABI.System +{ + // This partial declaration contains additional marshalling methods for the ABI type for System.Type. + // These are hardcoded for the ABI.System.Type type, which makes them AOT-safe. Previously, these were + // relying on AOT-unfriendly code in MarshalNonBlittable. Because this is a well known type, we can + // just hardcode explicit marshalling methods for it instead to make using this type fully AOT-safe. + partial struct Type + { + internal struct MarshalerArray + { + public void Dispose() + { + if (_marshalers != null) + { + foreach (var marshaler in _marshalers) + { + DisposeMarshaler((Marshaler)marshaler); + } + } + if (_array != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(_array); + } + } + + public IntPtr _array; + public object[] _marshalers; + } + + internal static unsafe MarshalerArray CreateMarshalerArray(global::System.Type[] array) + { + MarshalerArray m = new MarshalerArray(); + if (array is null) + { + return m; + } + bool success = false; + try + { + int length = array.Length; + var abi_element_size = sizeof(Type); + var byte_length = length * abi_element_size; + m._array = Marshal.AllocCoTaskMem(byte_length); + m._marshalers = new object[length]; + var element = (byte*)m._array.ToPointer(); + for (int i = 0; i < length; i++) + { + m._marshalers[i] = CreateMarshaler(array[i]); + CopyAbi((Marshaler)m._marshalers[i], (IntPtr)element); + element += abi_element_size; + } + success = true; + return m; + } + finally + { + if (!success) + { + m.Dispose(); + } + } + } + + internal static (int length, IntPtr data) GetAbiArray(object box) + { + var m = (MarshalerArray)box; + return (m._marshalers?.Length ?? 0, m._array); + } + + internal static unsafe global::System.Type[] FromAbiArray(object box) + { + if (box is null) + { + return null; + } + var abi = ((int length, IntPtr data))box; + if (abi.data == IntPtr.Zero) + { + return null; + } + var array = new global::System.Type[abi.length]; + var data = (byte*)abi.data.ToPointer(); + var abi_element_size = sizeof(Type); + for (int i = 0; i < abi.length; i++) + { + var abi_element = Unsafe.ReadUnaligned(data); + array[i] = FromAbi(abi_element); + data += abi_element_size; + } + return array; + } + + internal static unsafe (int length, IntPtr data) FromManagedArray(global::System.Type[] array) + { + if (array is null) + { + return (0, IntPtr.Zero); + } + IntPtr data = IntPtr.Zero; + int i = 0; + bool success = false; + try + { + int length = array.Length; + var abi_element_size = sizeof(Type); + var byte_length = length * abi_element_size; + data = Marshal.AllocCoTaskMem(byte_length); + var bytes = (byte*)data.ToPointer(); + for (i = 0; i < length; i++) + { + CopyManaged(array[i], (IntPtr)bytes); + bytes += abi_element_size; + } + success = true; + return (i, data); + } + finally + { + if (!success) + { + DisposeAbiArray((i, data)); + } + } + } + + public static unsafe void CopyManagedArray(global::System.Type[] array, IntPtr data) + { + if (array is null) + { + return; + } + DisposeAbiArrayElements((array.Length, data)); + int i = 0; + bool success = false; + try + { + int length = array.Length; + var abi_element_size = sizeof(Type); + var byte_length = length * abi_element_size; + var bytes = (byte*)data.ToPointer(); + for (i = 0; i < length; i++) + { + CopyManaged(array[i], (IntPtr)bytes); + bytes += abi_element_size; + } + success = true; + } + finally + { + if (!success) + { + DisposeAbiArrayElements((i, data)); + } + } + } + + internal static void DisposeMarshalerArray(object box) => ((MarshalerArray)box).Dispose(); + + internal static unsafe void DisposeAbiArray(object box) + { + if (box == null) return; + var abi = ((int length, IntPtr data))box; + if (abi.data == IntPtr.Zero) return; + DisposeAbiArrayElements(abi); + Marshal.FreeCoTaskMem(abi.data); + } + + private static unsafe void DisposeAbiArrayElements((int length, IntPtr data) abi) + { + var data = (byte*)abi.data.ToPointer(); + var abi_element_size = sizeof(Type); + for (int i = 0; i < abi.length; i++) + { + var abi_element = Unsafe.ReadUnaligned(data); + DisposeAbi(abi_element); + data += abi_element_size; + } + } + } +} diff --git a/src/WinRT.Runtime/Projections/Type.cs b/src/WinRT.Runtime/Projections/Type.cs index 80bcd751d..8a968e0f6 100644 --- a/src/WinRT.Runtime/Projections/Type.cs +++ b/src/WinRT.Runtime/Projections/Type.cs @@ -23,7 +23,7 @@ internal enum TypeKind : int #else public #endif - struct Type + partial struct Type { private IntPtr Name; private TypeKind Kind; @@ -73,7 +73,7 @@ private static (String Name, TypeKind Kind) ToAbi(global::System.Type value) { kind = TypeKind.Primitive; } - else if (value == typeof(object) || value == typeof(string) || value == typeof(Guid) || value == typeof(System.Type)) + else if (value == typeof(object) || value == typeof(string) || value == typeof(Guid) || value == typeof(global::System.Type)) { kind = TypeKind.Metadata; } @@ -128,7 +128,7 @@ public static Type GetAbi(Marshaler m) } #if NET - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", + [UnconditionalSuppressMessage("Trimming", "IL2057", Justification = "Any types which are trimmed are not used by managed user code and there is fallback logic to handle that.")] #endif public static global::System.Type FromAbi(Type value) @@ -221,6 +221,7 @@ internal static FakeMetadataType GetFakeMetadataType(string name) public override string Name => throw new InvalidOperationException(); + [UnconditionalSuppressMessage("Trimming", "IL2094", Justification = "This method will always throw.")] public override ConstructorInfo[] GetConstructors(BindingFlags bindingAttr) { throw new InvalidOperationException(); @@ -241,64 +242,74 @@ public override object[] GetCustomAttributes(global::System.Type attributeType, throw new InvalidOperationException(); } + [UnconditionalSuppressMessage("Trimming", "IL2094", Justification = "This method will always throw.")] public override EventInfo GetEvent(string name, BindingFlags bindingAttr) { throw new InvalidOperationException(); } + [UnconditionalSuppressMessage("Trimming", "IL2094", Justification = "This method will always throw.")] public override EventInfo[] GetEvents(BindingFlags bindingAttr) { throw new InvalidOperationException(); } + [UnconditionalSuppressMessage("Trimming", "IL2094", Justification = "This method will always throw.")] public override FieldInfo GetField(string name, BindingFlags bindingAttr) { throw new InvalidOperationException(); } + [UnconditionalSuppressMessage("Trimming", "IL2094", Justification = "This method will always throw.")] public override FieldInfo[] GetFields(BindingFlags bindingAttr) { throw new InvalidOperationException(); } -#if NET6_0_OR_GREATER - [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] -#endif + [UnconditionalSuppressMessage("Trimming", "IL2093", Justification = "This method will always throw.")] + [UnconditionalSuppressMessage("Trimming", "IL2094", Justification = "This method will always throw.")] public override global::System.Type GetInterface(string name, bool ignoreCase) { throw new InvalidOperationException(); } + [UnconditionalSuppressMessage("Trimming", "IL2094", Justification = "This method will always throw.")] public override global::System.Type[] GetInterfaces() { throw new InvalidOperationException(); } + [UnconditionalSuppressMessage("Trimming", "IL2094", Justification = "This method will always throw.")] public override MemberInfo[] GetMembers(BindingFlags bindingAttr) { throw new InvalidOperationException(); } + [UnconditionalSuppressMessage("Trimming", "IL2094", Justification = "This method will always throw.")] public override MethodInfo[] GetMethods(BindingFlags bindingAttr) { throw new InvalidOperationException(); } + [UnconditionalSuppressMessage("Trimming", "IL2094", Justification = "This method will always throw.")] public override global::System.Type GetNestedType(string name, BindingFlags bindingAttr) { throw new InvalidOperationException(); } + [UnconditionalSuppressMessage("Trimming", "IL2094", Justification = "This method will always throw.")] public override global::System.Type[] GetNestedTypes(BindingFlags bindingAttr) { throw new InvalidOperationException(); } + [UnconditionalSuppressMessage("Trimming", "IL2094", Justification = "This method will always throw.")] public override PropertyInfo[] GetProperties(BindingFlags bindingAttr) { throw new InvalidOperationException(); } + [UnconditionalSuppressMessage("Trimming", "IL2094", Justification = "This method will always throw.")] public override object InvokeMember(string name, BindingFlags invokeAttr, Binder binder, object target, object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] namedParameters) { throw new InvalidOperationException(); @@ -314,16 +325,19 @@ protected override TypeAttributes GetAttributeFlagsImpl() throw new InvalidOperationException(); } + [UnconditionalSuppressMessage("Trimming", "IL2094", Justification = "This method will always throw.")] protected override ConstructorInfo GetConstructorImpl(BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, global::System.Type[] types, ParameterModifier[] modifiers) { throw new InvalidOperationException(); } + [UnconditionalSuppressMessage("Trimming", "IL2094", Justification = "This method will always throw.")] protected override MethodInfo GetMethodImpl(string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, global::System.Type[] types, ParameterModifier[] modifiers) { throw new InvalidOperationException(); } + [UnconditionalSuppressMessage("Trimming", "IL2094", Justification = "This method will always throw.")] protected override PropertyInfo GetPropertyImpl(string name, BindingFlags bindingAttr, Binder binder, global::System.Type returnType, global::System.Type[] types, ParameterModifier[] modifiers) { throw new InvalidOperationException(); diff --git a/src/WinRT.Runtime/Projections/Uri.cs b/src/WinRT.Runtime/Projections/Uri.cs index d375c4541..c89272a7f 100644 --- a/src/WinRT.Runtime/Projections/Uri.cs +++ b/src/WinRT.Runtime/Projections/Uri.cs @@ -1,172 +1,171 @@ // Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Runtime.InteropServices; -using WinRT; -using WinRT.Interop; - -namespace ABI.Windows.Foundation -{ - [Guid("9E365E57-48B2-4160-956F-C7385120BBFC")] - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct IUriRuntimeClassVftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - public IntPtr get_AbsoluteUri_0; - public IntPtr get_DisplayUri_1; - public IntPtr get_Domain_2; - public IntPtr get_Extension_3; - public IntPtr get_Fragment_4; - public IntPtr get_Host_5; - public IntPtr get_Password_6; - public IntPtr get_Path_7; - public IntPtr get_Query_8; - public IntPtr get_QueryParsed_9; - public void* _get_RawUri_10; - public delegate* unmanaged[Stdcall] get_RawUri_10 => (delegate* unmanaged[Stdcall])_get_RawUri_10; - public IntPtr get_SchemeName_11; - public IntPtr get_UserName_12; - public IntPtr get_Port_13; - public IntPtr get_Suspicious_14; - public IntPtr Equals_15; - public IntPtr CombineUri_16; - } -} - -namespace ABI.System -{ - - [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] - [Guid("44A9796F-723E-4FDF-A218-033E75B0C084")] - internal sealed class WinRTUriRuntimeClassFactory - { - [Guid("44A9796F-723E-4FDF-A218-033E75B0C084")] - [StructLayout(LayoutKind.Sequential)] - public unsafe struct Vftbl - { - internal IInspectable.Vftbl IInspectableVftbl; - private void* _CreateUri_0; - public delegate* unmanaged[Stdcall] CreateUri_0 => (delegate* unmanaged[Stdcall])_CreateUri_0; - public IntPtr _CreateWithRelativeUri; - } - public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator WinRTUriRuntimeClassFactory(IObjectReference obj) => (obj != null) ? new WinRTUriRuntimeClassFactory(obj) : null; - public static implicit operator WinRTUriRuntimeClassFactory(ObjectReference obj) => (obj != null) ? new WinRTUriRuntimeClassFactory(obj) : null; - private readonly ObjectReference _obj; - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public WinRTUriRuntimeClassFactory(IObjectReference obj) : this(obj.As()) { } - public WinRTUriRuntimeClassFactory(ObjectReference obj) - { - _obj = obj; - } - - public unsafe IObjectReference CreateUri(string uri) - { - IntPtr __retval = default; - MarshalString.Pinnable __uri = new(uri); - fixed (void* ___uri = __uri) +// Licensed under the MIT License. + +using System; +using System.Runtime.InteropServices; +using WinRT; +using WinRT.Interop; + +namespace ABI.Windows.Foundation +{ + [Guid("9E365E57-48B2-4160-956F-C7385120BBFC")] + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct IUriRuntimeClassVftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + public IntPtr get_AbsoluteUri_0; + public IntPtr get_DisplayUri_1; + public IntPtr get_Domain_2; + public IntPtr get_Extension_3; + public IntPtr get_Fragment_4; + public IntPtr get_Host_5; + public IntPtr get_Password_6; + public IntPtr get_Path_7; + public IntPtr get_Query_8; + public IntPtr get_QueryParsed_9; + public void* _get_RawUri_10; + public delegate* unmanaged[Stdcall] get_RawUri_10 => (delegate* unmanaged[Stdcall])_get_RawUri_10; + public IntPtr get_SchemeName_11; + public IntPtr get_UserName_12; + public IntPtr get_Port_13; + public IntPtr get_Suspicious_14; + public IntPtr Equals_15; + public IntPtr CombineUri_16; + } +} + +namespace ABI.System +{ +#if !NET + [global::WinRT.ObjectReferenceWrapper(nameof(_obj))] +#endif + [Guid("44A9796F-723E-4FDF-A218-033E75B0C084")] + internal sealed class WinRTUriRuntimeClassFactory + { + [Guid("44A9796F-723E-4FDF-A218-033E75B0C084")] + [StructLayout(LayoutKind.Sequential)] + public unsafe struct Vftbl + { + internal IInspectable.Vftbl IInspectableVftbl; + private void* _CreateUri_0; + public delegate* unmanaged[Stdcall] CreateUri_0 => (delegate* unmanaged[Stdcall])_CreateUri_0; + public IntPtr _CreateWithRelativeUri; + } + public static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr, IID.IID_UriRuntimeClassFactory); + + private readonly ObjectReference _obj; + public IntPtr ThisPtr => _obj.ThisPtr; + public WinRTUriRuntimeClassFactory(IObjectReference obj) : this(obj.As(IID.IID_UriRuntimeClassFactory)) { } + public WinRTUriRuntimeClassFactory(ObjectReference obj) + { + _obj = obj; + } + + public unsafe IObjectReference CreateUri(string uri) + { + IntPtr __retval = default; + MarshalString.Pinnable __uri = new(uri); + fixed (void* ___uri = __uri) + { + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6](ThisPtr, MarshalString.GetAbi(ref __uri), &__retval)); + GC.KeepAlive(_obj); + return ObjectReference.Attach(ref __retval, IID.IID_IUnknown); + } + } + + public unsafe ObjectReferenceValue CreateUriForMarshaling(string uri) + { + IntPtr __retval = default; + MarshalString.Pinnable __uri = new(uri); + fixed (void* ___uri = __uri) + { + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[6](ThisPtr, MarshalString.GetAbi(ref __uri), &__retval)); + GC.KeepAlive(_obj); + return new ObjectReferenceValue(__retval); + } + } + } + + + [StructLayout(LayoutKind.Sequential)] +#if EMBED + internal +#else + public +#endif + unsafe struct Uri + { + private static readonly WinRTUriRuntimeClassFactory Instance = new(ActivationFactory.Get("Windows.Foundation.Uri")); + + public static IObjectReference CreateMarshaler(global::System.Uri value) + { + if (value is null) + { + return null; + } + + return Instance.CreateUri(value.OriginalString); + } + + public static ObjectReferenceValue CreateMarshaler2(global::System.Uri value) + { + if (value is null) { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.CreateUri_0(ThisPtr, MarshalString.GetAbi(ref __uri), out __retval)); - return ObjectReference.Attach(ref __retval); - } + return new ObjectReferenceValue(); + } + + return Instance.CreateUriForMarshaling(value.OriginalString); } - public unsafe ObjectReferenceValue CreateUriForMarshaling(string uri) - { - IntPtr __retval = default; - MarshalString.Pinnable __uri = new(uri); - fixed (void* ___uri = __uri) + public static IntPtr GetAbi(IObjectReference m) => m?.ThisPtr ?? IntPtr.Zero; + + public static global::System.Uri FromAbi(IntPtr ptr) + { + if (ptr == IntPtr.Zero) { - global::WinRT.ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.CreateUri_0(ThisPtr, MarshalString.GetAbi(ref __uri), out __retval)); - return new ObjectReferenceValue(__retval); - } - } - } - - - [StructLayout(LayoutKind.Sequential)] -#if EMBED - internal -#else - public -#endif - unsafe struct Uri - { - private sealed class ActivationFactory : BaseActivationFactory - { - public ActivationFactory() : base("Windows.Foundation", "Windows.Foundation.Uri") - { + return null; } - internal static WinRTUriRuntimeClassFactory Instance = - new ActivationFactory()._As(); - } - - public static IObjectReference CreateMarshaler(global::System.Uri value) - { - if (value is null) - { - return null; - } - - return ActivationFactory.Instance.CreateUri(value.OriginalString); + IntPtr rawUri = IntPtr.Zero; + try + { + ExceptionHelpers.ThrowExceptionForHR((**(ABI.Windows.Foundation.IUriRuntimeClassVftbl**)ptr).get_RawUri_10(ptr, &rawUri)); + return new global::System.Uri(MarshalString.FromAbi(rawUri)); + } + finally + { + MarshalString.DisposeAbi(rawUri); + } } - public static ObjectReferenceValue CreateMarshaler2(global::System.Uri value) - { - if (value is null) - { - return new ObjectReferenceValue(); - } - - return ActivationFactory.Instance.CreateUriForMarshaling(value.OriginalString); - } - - public static IntPtr GetAbi(IObjectReference m) => m?.ThisPtr ?? IntPtr.Zero; - - public static global::System.Uri FromAbi(IntPtr ptr) - { - if (ptr == IntPtr.Zero) - { - return null; - } - - IntPtr rawUri = IntPtr.Zero; - try - { - ExceptionHelpers.ThrowExceptionForHR((**(ABI.Windows.Foundation.IUriRuntimeClassVftbl**)ptr).get_RawUri_10(ptr, &rawUri)); - return new global::System.Uri(MarshalString.FromAbi(rawUri)); - } - finally - { - MarshalString.DisposeAbi(rawUri); - } - } - - public static unsafe void CopyManaged(global::System.Uri o, IntPtr dest) - { - *(IntPtr*)dest.ToPointer() = CreateMarshaler2(o).Detach(); - } - - public static IntPtr FromManaged(global::System.Uri value) - { - if (value is null) - { - return IntPtr.Zero; - } - return CreateMarshaler2(value).Detach(); - } - - public static void DisposeMarshaler(IObjectReference m) { m?.Dispose(); } - public static void DisposeAbi(IntPtr abi) { MarshalInspectable.DisposeAbi(abi); } - - public static string GetGuidSignature() - { - return "rc(Windows.Foundation.Uri;{9e365e57-48b2-4160-956f-c7385120bbfc})"; - } - } -} + public static unsafe global::System.Uri[] FromAbiArray(object box) => MarshalInterfaceHelper.FromAbiArray(box, FromAbi); + + public static unsafe void CopyManaged(global::System.Uri o, IntPtr dest) + { + *(IntPtr*)dest.ToPointer() = CreateMarshaler2(o).Detach(); + } + + public static IntPtr FromManaged(global::System.Uri value) + { + if (value is null) + { + return IntPtr.Zero; + } + return CreateMarshaler2(value).Detach(); + } + + public static unsafe MarshalInterfaceHelper.MarshalerArray CreateMarshalerArray(global::System.Uri[] array) => MarshalInterfaceHelper.CreateMarshalerArray2(array, (o) => CreateMarshaler2(o)); + public static (int length, IntPtr data) GetAbiArray(object box) => MarshalInterfaceHelper.GetAbiArray(box); + public static void CopyAbiArray(global::System.Uri[] array, object box) => MarshalInterfaceHelper.CopyAbiArray(array, box, FromAbi); + public static (int length, IntPtr data) FromManagedArray(global::System.Uri[] array) => MarshalInterfaceHelper.FromManagedArray(array, (o) => FromManaged(o)); + public static void DisposeMarshalerArray(MarshalInterfaceHelper.MarshalerArray array) => MarshalInterfaceHelper.DisposeMarshalerArray(array); + public static void DisposeMarshaler(IObjectReference m) { m?.Dispose(); } + public static void DisposeAbi(IntPtr abi) { MarshalInspectable.DisposeAbi(abi); } + public static void DisposeAbiArray(object box) => MarshalInterfaceHelper.DisposeAbiArray(box); + + public static string GetGuidSignature() + { + return "rc(Windows.Foundation.Uri;{9e365e57-48b2-4160-956f-c7385120bbfc})"; + } + } +} diff --git a/src/WinRT.Runtime/SingleInterfaceOptimizedObject.net5.cs b/src/WinRT.Runtime/SingleInterfaceOptimizedObject.net5.cs index 466227d82..7281344af 100644 --- a/src/WinRT.Runtime/SingleInterfaceOptimizedObject.net5.cs +++ b/src/WinRT.Runtime/SingleInterfaceOptimizedObject.net5.cs @@ -3,6 +3,8 @@ using System; using System.Collections.Concurrent; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using WinRT.Interop; @@ -13,10 +15,10 @@ namespace WinRT #else public #endif - class SingleInterfaceOptimizedObject : IWinRTObject, IDynamicInterfaceCastable + sealed class SingleInterfaceOptimizedObject : IWinRTObject, IDynamicInterfaceCastable { - private Type _type; - private IObjectReference _obj; + private readonly Type _type; + private readonly IObjectReference _obj; public SingleInterfaceOptimizedObject(Type type, IObjectReference objRef) : this(type, objRef, true) @@ -29,15 +31,37 @@ internal SingleInterfaceOptimizedObject(Type type, IObjectReference objRef, bool if (requireQI) { Type helperType = type.FindHelperType(); - var vftblType = helperType.FindVftblType(); - if (vftblType is null) - { - _obj = objRef.As(GuidGenerator.GetIID(helperType)); - } - else + + if (RuntimeFeature.IsDynamicCodeCompiled) { - _obj = (IObjectReference)typeof(IObjectReference).GetMethod("As", Type.EmptyTypes).MakeGenericMethod(vftblType).Invoke(objRef, null); + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "If the 'Vftbl' type is kept, we can assume all its metadata will also have been rooted.")] + [MethodImpl(MethodImplOptions.NoInlining)] + static IObjectReference TryGetObjectReferenceViaVftbl(IObjectReference objRef, Type helperType) + { + var vftblType = helperType.FindVftblType(); + + if (vftblType is not null) + { +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 + return (IObjectReference)typeof(IObjectReference).GetMethod("As", Type.EmptyTypes).MakeGenericMethod(vftblType).Invoke(objRef, null); +#pragma warning restore IL3050 + } + + return null; + } + + IObjectReference objRefViaVftbl = TryGetObjectReferenceViaVftbl(objRef, helperType); + + if (objRefViaVftbl is not null) + { + _obj = objRefViaVftbl; + + return; + } } + + _obj = objRef.As(GuidGenerator.GetIID(helperType)); + } else { @@ -71,7 +95,13 @@ bool IDynamicInterfaceCastable.IsInterfaceImplemented(RuntimeTypeHandle interfac { return true; } - return (this as IWinRTObject).IsInterfaceImplementedFallback(interfaceType, throwIfNotImplemented); + + if (!FeatureSwitches.EnableIDynamicInterfaceCastableSupport) + { + return false; + } + + return ((IWinRTObject)this).IsInterfaceImplementedFallback(interfaceType, throwIfNotImplemented); } IObjectReference IWinRTObject.GetObjectReferenceForType(RuntimeTypeHandle interfaceType) @@ -80,7 +110,7 @@ IObjectReference IWinRTObject.GetObjectReferenceForType(RuntimeTypeHandle interf { return _obj; } - return (this as IWinRTObject).GetObjectReferenceForTypeFallback(interfaceType); + return ((IWinRTObject)this).GetObjectReferenceForTypeFallback(interfaceType); } } diff --git a/src/WinRT.Runtime/TypeExtensions.cs b/src/WinRT.Runtime/TypeExtensions.cs index fab82c746..fefb46f24 100644 --- a/src/WinRT.Runtime/TypeExtensions.cs +++ b/src/WinRT.Runtime/TypeExtensions.cs @@ -1,68 +1,115 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Concurrent; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Reflection; - -namespace WinRT -{ -#if EMBED - internal -#else - public -#endif - static class TypeExtensions - { - private readonly static ConcurrentDictionary HelperTypeCache = new ConcurrentDictionary(); - -#if NET +using System.Reflection; +using System.Runtime.CompilerServices; + +namespace WinRT +{ +#if EMBED + internal +#else + public +#endif + static class TypeExtensions + { + internal static readonly ConcurrentDictionary HelperTypeCache = new ConcurrentDictionary(); + +#if NET + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | + DynamicallyAccessedMemberTypes.PublicFields)] + [UnconditionalSuppressMessage("Trimming", "IL2073", Justification = "Matching trimming annotations are used at all callsites registering helper types present in the cache.")] +#endif + public static Type FindHelperType(this Type type) + { + return type.FindHelperType(true); + } + +#if NET [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | - DynamicallyAccessedMemberTypes.NonPublicMethods | - DynamicallyAccessedMemberTypes.PublicNestedTypes | - DynamicallyAccessedMemberTypes.PublicFields)] -#endif - public static Type FindHelperType(this Type type) - { - return HelperTypeCache.GetOrAdd(type, -#if NET + DynamicallyAccessedMemberTypes.PublicFields)] + [UnconditionalSuppressMessage("Trimming", "IL2073", Justification = "Matching trimming annotations are used at all callsites registering helper types present in the cache.")] +#endif + internal static Type FindHelperType(this Type type, bool throwIfNotAotSupported) + { +#if NET [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | - DynamicallyAccessedMemberTypes.NonPublicMethods | - DynamicallyAccessedMemberTypes.PublicNestedTypes | - DynamicallyAccessedMemberTypes.PublicFields)] -#endif - (type) => - { - if (typeof(Exception).IsAssignableFrom(type)) - { - type = typeof(Exception); - } - Type customMapping = Projections.FindCustomHelperTypeMapping(type); - if (customMapping is not null) - { - return customMapping; - } - - var helperTypeAtribute = type.GetCustomAttribute(); + DynamicallyAccessedMemberTypes.PublicFields)] +#endif + static Type FindHelperTypeNoCache(Type type) + { + if (typeof(Exception).IsAssignableFrom(type)) + { + type = typeof(Exception); + } + + Type customMapping = Projections.FindCustomHelperTypeMapping(type, false, true); + if (customMapping is not null) + { + return customMapping; + } + + var helperTypeAtribute = type.GetCustomAttribute(); if (helperTypeAtribute is not null) { return GetHelperTypeFromAttribute(helperTypeAtribute, type); - } - - return FindHelperTypeFallback(type); - }); + } + + var authoringMetadaType = type.GetAuthoringMetadataType(); + if (authoringMetadaType is not null) + { + helperTypeAtribute = authoringMetadaType.GetCustomAttribute(); + if (helperTypeAtribute is not null) + { + return GetHelperTypeFromAttribute(helperTypeAtribute, type); + } + } +#if NET + // Using AOT requires using updated projections, which would never let the code below + // be reached (as it's just a fallback path for legacy projections). So we can trim it. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return null; + } +#endif + return FindHelperTypeFallback(type); + } + + var helperType = HelperTypeCache.GetOrAdd(type, FindHelperTypeNoCache); +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + if (helperType == typeof(HelperTypeMetadataNotAvailableOnAot)) + { + return throwIfNotAotSupported ? throw new NotSupportedException($"Cannot retrieve a helper type for generic public type '{type}'.") : null; + } + } +#endif + return helperType; #if NET - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "No members of the generic type are dynamically accessed other than for the attributes on it.")] - [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicNestedTypes | DynamicallyAccessedMemberTypes.PublicFields)] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "No members of the generic type are dynamically accessed other than for the attributes on it.")] + [UnconditionalSuppressMessage("Trimming", "IL2055", Justification = "The type arguments are guaranteed to be valid for the generic ABI types.")] + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.PublicFields)] #endif static Type GetHelperTypeFromAttribute(WindowsRuntimeHelperTypeAttribute helperTypeAtribute, Type type) { - if (type.IsGenericType) + if (type.IsGenericType && !type.IsGenericTypeDefinition) { +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return typeof(HelperTypeMetadataNotAvailableOnAot); + } +#endif + +#pragma warning disable IL3050 // https://github.com/dotnet/runtime/issues/97273 return helperTypeAtribute.HelperType.MakeGenericType(type.GetGenericArguments()); +#pragma warning restore IL3050 } else { @@ -71,8 +118,11 @@ static Type GetHelperTypeFromAttribute(WindowsRuntimeHelperTypeAttribute helperT } #if NET - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "This is a fallback for compat purposes with existing projections. " + + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = + "This is a fallback for compat purposes with existing projections. " + + "Applications which make use of trimming will make use of updated projections that won't hit this code path.")] + [UnconditionalSuppressMessage("Trimming", "IL2057", Justification = + "This is a fallback for compat purposes with existing projections. " + "Applications which make use of trimming will make use of updated projections that won't hit this code path.")] #endif static Type FindHelperTypeFallback(Type type) @@ -86,107 +136,230 @@ static Type FindHelperTypeFallback(Type type) var helper = $"ABI.{fullTypeName}"; return type.Assembly.GetType(helper) ?? Type.GetType(helper); - } - } - -#if NET - [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | - DynamicallyAccessedMemberTypes.NonPublicMethods | - DynamicallyAccessedMemberTypes.PublicNestedTypes | - DynamicallyAccessedMemberTypes.PublicFields)] -#endif - public static Type GetHelperType(this Type type) - { - var helperType = type.FindHelperType(); - if (helperType is object) - return helperType; - throw new InvalidOperationException($"Target type is not a projected type: {type.FullName}."); - } - -#if NET - [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] -#endif - public static Type GetGuidType(this Type type) - { - return type.IsDelegate() ? type.GetHelperType() : type; - } - -#if NET - [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors | DynamicallyAccessedMemberTypes.PublicFields)] -#endif - public static Type FindVftblType( -#if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicNestedTypes)] -#endif - this Type helperType) - { - Type vftblType = helperType.GetNestedType("Vftbl"); - if (vftblType is null) - { - return null; - } - if (helperType.IsGenericType && vftblType is object) - { - vftblType = vftblType.MakeGenericType(helperType.GetGenericArguments()); - } - return vftblType; - } - - internal static IntPtr GetAbiToProjectionVftblPtr( + } + } + +#if NET + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicMethods | + DynamicallyAccessedMemberTypes.PublicFields)] +#endif + public static Type GetHelperType(this Type type) + { + var helperType = type.FindHelperType(); + if (helperType is object) + return helperType; + throw new InvalidOperationException($"Target type is not a projected type: {type.FullName}."); + } + +#if NET + [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] +#endif + public static Type GetGuidType( #if NET [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] -#endif - this Type helperType) - { - return (IntPtr)(helperType.FindVftblType() ?? helperType).GetField("AbiToProjectionVftablePtr", BindingFlags.Public | BindingFlags.Static).GetValue(null); - } - - public static Type GetAbiType(this Type type) - { - return type.GetHelperType().GetMethod("GetAbi", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).ReturnType; - } - - public static Type GetMarshalerType(this Type type) +#endif + this Type type) { - return type.GetHelperType().GetMethod("CreateMarshaler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static).ReturnType; + return type.IsDelegate() ? type.GetHelperType() : type; } - internal static Type GetMarshaler2Type(this Type type) +#if NET + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "The fallback path is not AOT-safe by design (to avoid annotations).")] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "The fallback path is not trim-safe by design (to avoid annotations).")] +#endif + public static Type FindVftblType(this Type helperType) + { +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return null; + } +#endif + +#if NET8_0_OR_GREATER + [RequiresDynamicCode(AttributeMessages.MarshallingOrGenericInstantiationsRequiresDynamicCode)] +#endif +#if NET + [DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.Windows.Foundation.IAsyncActionWithProgress`1+Vftbl", "Microsoft.Windows.SDK.NET")] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.Windows.Foundation.IAsyncOperationWithProgress`2+Vftbl", "Microsoft.Windows.SDK.NET")] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.Windows.Foundation.IAsyncOperation`1+Vftbl", "Microsoft.Windows.SDK.NET")] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.Windows.Foundation.Collections.IMapChangedEventArgs`1+Vftbl", "Microsoft.Windows.SDK.NET")] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.Windows.Foundation.Collections.IObservableMap`2+Vftbl", "Microsoft.Windows.SDK.NET")] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.Windows.Foundation.Collections.IObservableVector`1+Vftbl", "Microsoft.Windows.SDK.NET")] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.System.EventHandler`1+Vftbl", "WinRT.Runtime")] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.System.Collections.Generic.KeyValuePair`2+Vftbl", "WinRT.Runtime")] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.System.Collections.Generic.IEnumerable`1+Vftbl", "WinRT.Runtime")] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.System.Collections.Generic.IEnumerator`1+Vftbl", "WinRT.Runtime")] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.System.Collections.Generic.IList`1+Vftbl", "WinRT.Runtime")] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.System.Collections.Generic.IReadOnlyList`1+Vftbl", "WinRT.Runtime")] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.System.Collections.Generic.IDictionary`2+Vftbl", "WinRT.Runtime")] + [DynamicDependency(DynamicallyAccessedMemberTypes.All, "ABI.System.Collections.Generic.IReadOnlyDictionary`2+Vftbl", "WinRT.Runtime")] + [RequiresUnreferencedCode(AttributeMessages.GenericRequiresUnreferencedCodeMessage)] +#endif + static Type FindVftblTypeFallback(Type helperType) + { + Type vftblType = helperType.GetNestedType("Vftbl"); + if (vftblType is null) + { + return null; + } + if (helperType.IsGenericType) + { + vftblType = vftblType.MakeGenericType(helperType.GetGenericArguments()); + } + return vftblType; + } + + return FindVftblTypeFallback(helperType); + } + +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2075", Justification = "The path using vtable types is a fallback and is not trim-safe by design.")] +#endif + internal static IntPtr GetAbiToProjectionVftblPtr( +#if NET + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicFields)] +#endif + this Type helperType) + { + return (IntPtr)(helperType.FindVftblType() ?? helperType).GetField("AbiToProjectionVftablePtr", BindingFlags.Public | BindingFlags.Static).GetValue(null); + } + + public static Type GetAbiType(this Type type) + { + return type.GetHelperType().GetMethod("GetAbi", BindingFlags.Public | BindingFlags.Static).ReturnType; + } + + public static Type GetMarshalerType(this Type type) + { + return type.GetHelperType().GetMethod("CreateMarshaler", BindingFlags.Public | BindingFlags.Static).ReturnType; + } + + internal static Type GetMarshaler2Type(this Type type) { var helperType = type.GetHelperType(); - var createMarshaler = helperType.GetMethod("CreateMarshaler2", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static) ?? - helperType.GetMethod("CreateMarshaler", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); + var createMarshaler = + helperType.GetMethod("CreateMarshaler2", BindingFlags.Public | BindingFlags.Static) ?? + helperType.GetMethod("CreateMarshaler", BindingFlags.Public | BindingFlags.Static); return createMarshaler.ReturnType; } - - internal static Type GetMarshalerArrayType(this Type type) - { - return type.GetHelperType().GetMethod("CreateMarshalerArray", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static)?.ReturnType; - } - - public static bool IsDelegate(this Type type) - { - return typeof(Delegate).IsAssignableFrom(type); - } - - internal static bool IsTypeOfType(this Type type) - { - return typeof(Type).IsAssignableFrom(type); - } - - public static Type GetRuntimeClassCCWType(this Type type) - { - return type.IsClass && !type.IsArray ? type.GetAuthoringMetadataType() : null; - } - - private readonly static ConcurrentDictionary AuthoringMetadataTypeCache = new ConcurrentDictionary(); - internal static Type GetAuthoringMetadataType(this Type type) - { - return AuthoringMetadataTypeCache.GetOrAdd(type, (type) => + + internal static Type GetMarshalerArrayType(this Type type) + { + return type.GetHelperType().GetMethod("CreateMarshalerArray", BindingFlags.Public | BindingFlags.Static)?.ReturnType; + } + + public static bool IsDelegate(this Type type) + { + return typeof(Delegate).IsAssignableFrom(type); + } + + internal static bool IsNullableT(this Type type) + { + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(System.Nullable<>); + } + + internal static bool IsAbiNullableDelegate(this Type type) + { + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ABI.System.Nullable_Delegate<>); + } + + internal static bool IsIReferenceArray(this Type type) + { + // If support for 'IReference' is disabled, we'll never instantiate any types implementing this interface. We + // can guard this check behind the feature switch to avoid making 'IReferenceArray' reflectable, which will + // otherwise root some unnecessary code and metadata from the ABI implementation type. + if (!FeatureSwitches.EnableIReferenceSupport) { - var ccwTypeName = $"ABI.Impl.{type.FullName}"; - return type.Assembly.GetType(ccwTypeName, false); - }); - } - } -} + return false; + } + + return type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Windows.Foundation.IReferenceArray<>); + } + + internal static bool ShouldProvideIReference(this Type type) + { + if (!FeatureSwitches.EnableIReferenceSupport) + { + return false; + } + + return type.IsPrimitive || + type == typeof(string) || + type == typeof(Guid) || + type == typeof(DateTimeOffset) || + type == typeof(TimeSpan) || + type.IsTypeOfType() || + type.IsTypeOfException() || + ((type.IsValueType || type.IsDelegate()) && Projections.IsTypeWindowsRuntimeType(type)); + } + + internal static bool IsTypeOfType(this Type type) + { + return typeof(Type).IsAssignableFrom(type); + } + + internal static bool IsTypeOfException(this Type type) + { + return typeof(Exception).IsAssignableFrom(type); + } + + public static Type GetRuntimeClassCCWType(this Type type) + { + return type.IsClass && !type.IsArray ? type.GetAuthoringMetadataType() : null; + } + + internal static Type GetCCWType(this Type type) + { + return !type.IsArray ? type.GetAuthoringMetadataType() : null; + } + + private static readonly ConcurrentDictionary AuthoringMetadataTypeCache = new(); + private static readonly List> AuthoringMetadaTypeLookup = new(); + + internal static void RegisterAuthoringMetadataTypeLookup(Func authoringMetadataTypeLookup) + { + AuthoringMetadaTypeLookup.Add(authoringMetadataTypeLookup); + } + + internal static Type GetAuthoringMetadataType(this Type type) + { + return AuthoringMetadataTypeCache.GetOrAdd(type, + static (type) => + { + // Using for loop to avoid exception from list changing when using for each. + // List is only added to and if any are added while looping, we can ignore those. + int count = AuthoringMetadaTypeLookup.Count; + for (int i = 0; i < count; i++) + { + Type metadataType = AuthoringMetadaTypeLookup[i](type); + if (metadataType is not null) + { + return metadataType; + } + } + +#if NET + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return null; + } +#endif + // Fallback code path for back compat with previously generated projections + // running without AOT. + return GetAuthoringMetadataTypeFallback(type); + }); + } + +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2026:RequiresUnreferencedCode", + Justification = "This is a fallback for compat purposes with existing projections. " + + "Applications making use of updated projections won't hit this code path.")] +#endif + private static Type GetAuthoringMetadataTypeFallback(Type type) + { + var ccwTypeName = $"ABI.Impl.{type.FullName}"; + return type.Assembly.GetType(ccwTypeName, false); + } + } +} diff --git a/src/WinRT.Runtime/TypeNameSupport.cs b/src/WinRT.Runtime/TypeNameSupport.cs index 9b8dc3478..400d80cde 100644 --- a/src/WinRT.Runtime/TypeNameSupport.cs +++ b/src/WinRT.Runtime/TypeNameSupport.cs @@ -5,11 +5,10 @@ using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Linq; +using System.Diagnostics.CodeAnalysis; using System.Reflection; +using System.Runtime.CompilerServices; using System.Text; -using System.Threading; namespace WinRT { @@ -17,19 +16,21 @@ namespace WinRT internal enum TypeNameGenerationFlags { None = 0, + /// /// Generate the name of the type as if it was boxed in an object. /// GenerateBoxedName = 0x1, + /// /// Don't output a type name of a custom .NET type. Generate a compatible WinRT type name if needed. /// - NoCustomTypeName = 0x2 + ForGetRuntimeClassName = 0x2, } internal static class TypeNameSupport { - private static readonly List projectionAssemblies = new List(); + private static readonly List projectionAssemblies = new List(); private static readonly List> projectionTypeNameToBaseTypeNameMappings = new List>(); private static readonly ConcurrentDictionary typeNameCache = new ConcurrentDictionary(StringComparer.Ordinal) { ["TrackerCollection"] = null }; private static readonly ConcurrentDictionary baseRcwTypeCache = new ConcurrentDictionary(StringComparer.Ordinal) { ["TrackerCollection"] = null }; @@ -37,52 +38,65 @@ internal static class TypeNameSupport public static void RegisterProjectionAssembly(Assembly assembly) { projectionAssemblies.Add(assembly); - } - - public static void RegisterProjectionTypeBaseTypeMapping(IDictionary typeNameToBaseTypeNameMapping) - { - projectionTypeNameToBaseTypeNameMappings.Add(typeNameToBaseTypeNameMapping); - } - -#if NET - [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] -#endif + } + + public static void RegisterProjectionTypeBaseTypeMapping(IDictionary typeNameToBaseTypeNameMapping) + { + projectionTypeNameToBaseTypeNameMappings.Add(typeNameToBaseTypeNameMapping); + } + + public static void RegisterBaseTypeForTypeName(string runtimeClassName, Type baseType) + { + baseRcwTypeCache.AddOrUpdate(runtimeClassName, baseType, + (runtimeClassName, existingBaseType) => + { + if (existingBaseType is null || existingBaseType.IsAssignableFrom(baseType)) + { + // Only update the entry if there is no entry or + // the new statically known base type is more derived than the current one. + return baseType; + } + return existingBaseType; + }); + } + public static Type FindRcwTypeByNameCached(string runtimeClassName) - { - // Try to get the given type name. If it is not found, the type might have been trimmed. - // Due to that, check if one of the base types exists and if so use that instead for the RCW type. - var rcwType = FindTypeByNameCached(runtimeClassName); - if (rcwType is null) - { - rcwType = baseRcwTypeCache.GetOrAdd(runtimeClassName, -#if NET - [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] -#endif - (runtimeClassName) => + { + // Try to get the given type name. If it is not found, the type might have been trimmed. + // Due to that, check if one of the base types exists and if so use that instead for the RCW type. + var rcwType = FindTypeByNameCached(runtimeClassName); + if (rcwType is null) + { + rcwType = baseRcwTypeCache.GetOrAdd(runtimeClassName, + static (runtimeClassName) => { - var resolvedBaseType = projectionTypeNameToBaseTypeNameMappings.Find((dict) => dict.ContainsKey(runtimeClassName))?[runtimeClassName]; - return resolvedBaseType is not null ? FindRcwTypeByNameCached(resolvedBaseType) : null; - }); - } - + // Using for loop to avoid exception from list changing when using for each. + // List is only added to and if any are added while looping, we can ignore those. + int count = projectionTypeNameToBaseTypeNameMappings.Count; + for (int i = 0; i < count; i++) + { + if (projectionTypeNameToBaseTypeNameMappings[i].TryGetValue(runtimeClassName, out string value)) + { + return FindRcwTypeByNameCached(value); + } + } + + return null; + }); + } + return rcwType; - } - + } + /// /// Parses and loads the given type name, if not found in the cache. /// /// The runtime class name to attempt to parse. /// The type, if found. Null otherwise -#if NET - [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] -#endif public static Type FindTypeByNameCached(string runtimeClassName) { - return typeNameCache.GetOrAdd(runtimeClassName, -#if NET - [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] -#endif - (runtimeClassName) => + return typeNameCache.GetOrAdd(runtimeClassName, + static (runtimeClassName) => { Type implementationType = null; try @@ -94,6 +108,16 @@ public static Type FindTypeByNameCached(string runtimeClassName) } return implementationType; }); + } + + // Helper to get an exception if the input type is 'IReference' when support for it is disabled + [MethodImpl(MethodImplOptions.NoInlining)] + private static Exception GetExceptionForUnsupportedIReferenceType(ReadOnlySpan runtimeClassName) + { + return new NotSupportedException( + $"The requested runtime class name is '{runtimeClassName.ToString()}', which maps to an 'IReference' projected type. " + + "This can only be used when support for 'IReference' types is enabled in the CsWinRT configuration. To enable it, " + + "make sure that the 'CsWinRTEnableIReferenceSupport' MSBuild property is not being set to 'false' anywhere."); } /// @@ -107,28 +131,56 @@ public static (Type type, int remaining) FindTypeByName(ReadOnlySpan runti // It may be necessary to detect otherwise and return System.Object. if (runtimeClassName.StartsWith("<>f__AnonymousType".AsSpan(), StringComparison.Ordinal)) { - return (typeof(System.Dynamic.ExpandoObject), 0); - } - // PropertySet and ValueSet can return IReference but Nullable is illegal - else if (runtimeClassName.CompareTo("Windows.Foundation.IReference`1".AsSpan(), StringComparison.Ordinal) == 0) - { - return (typeof(ABI.System.Nullable_string), 0); + if (FeatureSwitches.EnableDynamicObjectsSupport) + { + return (typeof(System.Dynamic.ExpandoObject), 0); + } + + throw new NotSupportedException( + $"The requested runtime class name is '{runtimeClassName.ToString()}', which maps to a dynamic projected type. " + + "This can only be used when support for dynamic objects is enabled in the CsWinRT configuration. To enable it, " + + "make sure that the 'CsWinRTEnableDynamicObjectsSupport' MSBuild property is not being set to 'false' anywhere."); } - else if (runtimeClassName.CompareTo("Windows.Foundation.IReference`1".AsSpan(), StringComparison.Ordinal) == 0) + + // PropertySet and ValueSet can return IReference but Nullable is illegal + if (runtimeClassName.CompareTo("Windows.Foundation.IReference`1".AsSpan(), StringComparison.Ordinal) == 0) { - return (typeof(ABI.System.Nullable_Type), 0); + if (FeatureSwitches.EnableIReferenceSupport) + { + return (typeof(ABI.System.Nullable_string), 0); + } + + throw GetExceptionForUnsupportedIReferenceType(runtimeClassName); } - else - { - var (genericTypeName, genericTypes, remaining) = ParseGenericTypeName(runtimeClassName); - if (genericTypeName == null) + + if (runtimeClassName.CompareTo("Windows.Foundation.IReference`1".AsSpan(), StringComparison.Ordinal) == 0) + { + if (FeatureSwitches.EnableIReferenceSupport) { - return (null, -1); - } - return (FindTypeByNameCore(genericTypeName, genericTypes), remaining); + return (typeof(ABI.System.Nullable_Type), 0); + } + + throw GetExceptionForUnsupportedIReferenceType(runtimeClassName); + } + + if (runtimeClassName.CompareTo("Windows.Foundation.IReference`1".AsSpan(), StringComparison.Ordinal) == 0) + { + if (FeatureSwitches.EnableIReferenceSupport) + { + return (typeof(ABI.System.Nullable_Exception), 0); + } + + throw GetExceptionForUnsupportedIReferenceType(runtimeClassName); } - } - + + var (genericTypeName, genericTypes, remaining) = ParseGenericTypeName(runtimeClassName); + if (genericTypeName == null) + { + return (null, -1); + } + return (FindTypeByNameCore(genericTypeName, genericTypes), remaining); + } + /// /// Resolve a type from the given simple type name and the provided generic parameters. /// @@ -140,9 +192,7 @@ public static (Type type, int remaining) FindTypeByName(ReadOnlySpan runti /// the full type closure of the application. /// #if NET - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2026:RequiresUnreferencedCode", - Justification = "Any types which are trimmed are not used by user code and there is fallback logic to handle that.")] - [return: DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.NonPublicConstructors)] + [UnconditionalSuppressMessage("Trimming", "IL2026", Justification = "Any types which are trimmed are not used by user code and there is fallback logic to handle that.")] #endif private static Type FindTypeByNameCore(string runtimeClassName, Type[] genericTypes) { @@ -159,9 +209,12 @@ private static Type FindTypeByNameCore(string runtimeClassName, Type[] genericTy } } - foreach (var assembly in projectionAssemblies) + // Using for loop to avoid exception from list changing when using for each. + // List is only added to and if any are added while looping, we can ignore those. + int count = projectionAssemblies.Count; + for (int i = 0; i < count; i++) { - Type type = assembly.GetType(runtimeClassName); + Type type = projectionAssemblies[i].GetType(runtimeClassName); if (type is not null) { resolvedType = type; @@ -176,28 +229,76 @@ private static Type FindTypeByNameCore(string runtimeClassName, Type[] genericTy { Type type = assembly.GetType(runtimeClassName); if (type is not null) - { + { resolvedType = type; break; } } - } + } if (resolvedType is not null) { if (genericTypes != null) - { - if(resolvedType == typeof(global::System.Nullable<>) && genericTypes[0].IsDelegate()) - { - return typeof(ABI.System.Nullable_Delegate<>).MakeGenericType(genericTypes); - } - resolvedType = resolvedType.MakeGenericType(genericTypes); + { + return ResolveGenericType(resolvedType, genericTypes, runtimeClassName); } return resolvedType; - } - + } + Debug.WriteLine($"FindTypeByNameCore: Unable to find a type named '{runtimeClassName}'"); - return null; + return null; + +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2055", Justification = "The 'MakeGenericType' call is guarded by explicit checks in our code.")] + [UnconditionalSuppressMessage("AOT", "IL3050", Justification = "Calls to MakeGenericType are done with reference types.")] +#endif + static Type ResolveGenericType(Type resolvedType, Type[] genericTypes, string runtimeClassName) + { + if (resolvedType == typeof(global::System.Nullable<>)) + { + if (genericTypes[0].IsDelegate()) + { + if (FeatureSwitches.EnableIReferenceSupport) + { + return typeof(ABI.System.Nullable_Delegate<>).MakeGenericType(genericTypes); + } + + throw GetExceptionForUnsupportedIReferenceType(runtimeClassName.AsSpan()); + } + else + { + var nullableType = ABI.System.NullableType.GetTypeAsNullableType(genericTypes[0]); + if (nullableType is not null) + { + return nullableType; + } + } + } + +#if NET + if (resolvedType == typeof(Windows.Foundation.IReferenceArray<>)) + { + var referenceArrayType = ABI.Windows.Foundation.IReferenceArrayType.GetTypeAsArrayType(genericTypes[0]); + if (referenceArrayType is not null) + { + return referenceArrayType; + } + } + + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + foreach (var type in genericTypes) + { + if (type.IsValueType) + { + return null; + } + } + } +#endif + + return resolvedType.MakeGenericType(genericTypes); + } } public static Type ResolvePrimitiveType(string primitiveTypeName) @@ -214,7 +315,7 @@ public static Type ResolvePrimitiveType(string primitiveTypeName) "Int64" => typeof(long), "Boolean" => typeof(bool), "String" => typeof(string), - "Char" => typeof(char), + "Char" => typeof(char), "Char16" => typeof(char), "Single" => typeof(float), "Double" => typeof(double), @@ -256,9 +357,9 @@ private static (string genericTypeName, Type[] genericTypes, int remaining) Pars { // Resolve the generic type argument at this point in the parameter list. var (genericType, endOfGenericArgument) = FindTypeByName(remainingTypeName); - if (genericType == null) - { - return (null, null, -1); + if (genericType == null) + { + return (null, null, -1); } remainingIndex += endOfGenericArgument; @@ -291,42 +392,44 @@ struct VisitedType { public Type Type { get; set; } public bool Covariant { get; set; } - } - + } + +#nullable enable /// /// Tracker for visited types when determining a WinRT interface to use as the type name. - /// Only used when GetNameForType is called with . /// - private static readonly ThreadLocal> VisitedTypes = new ThreadLocal>(() => new Stack()); + /// + /// Only used when is called with . + /// + [ThreadStatic] + private static Stack? visitedTypesInstance; + + [ThreadStatic] + private static StringBuilder? nameForTypeBuilderInstance; - public static string GetNameForType(Type type, TypeNameGenerationFlags flags) + public static string GetNameForType(Type? type, TypeNameGenerationFlags flags) { if (type is null) { return string.Empty; } - StringBuilder nameBuilder = new StringBuilder(); + + // Get instance for this thread + StringBuilder? nameBuilder = nameForTypeBuilderInstance ??= new StringBuilder(); + nameBuilder.Clear(); if (TryAppendTypeName(type, nameBuilder, flags)) { return nameBuilder.ToString(); - } - return null; - } + } + return string.Empty; + } +#nullable restore + private static bool TryAppendSimpleTypeName(Type type, StringBuilder builder, TypeNameGenerationFlags flags) { if (type.IsPrimitive || type == typeof(string) || type == typeof(Guid) || type == typeof(TimeSpan)) { - if ((flags & TypeNameGenerationFlags.GenerateBoxedName) != 0) - { - builder.Append("Windows.Foundation.IReference`1<"); - if (!TryAppendSimpleTypeName(type, builder, flags & ~TypeNameGenerationFlags.GenerateBoxedName)) - { - return false; - } - builder.Append('>'); - return true; - } if (type == typeof(byte)) { builder.Append("UInt8"); @@ -343,11 +446,15 @@ private static bool TryAppendSimpleTypeName(Type type, StringBuilder builder, Ty else if (type == typeof(object)) { builder.Append("Object"); + } + else if ((flags & TypeNameGenerationFlags.ForGetRuntimeClassName) != 0 && type.IsTypeOfType()) + { + builder.Append("Windows.UI.Xaml.Interop.TypeName"); } else { var projectedAbiTypeName = Projections.FindCustomAbiTypeNameForType(type); - if (projectedAbiTypeName is object) + if (projectedAbiTypeName is not null) { builder.Append(projectedAbiTypeName); } @@ -355,13 +462,16 @@ private static bool TryAppendSimpleTypeName(Type type, StringBuilder builder, Ty { builder.Append(type.FullName); } - else if ((flags & TypeNameGenerationFlags.NoCustomTypeName) != 0) + else { - return TryAppendWinRTInterfaceNameForType(type, builder, flags); - } - else - { - builder.Append(type.FullName); + if ((flags & TypeNameGenerationFlags.ForGetRuntimeClassName) != 0) + { + return TryAppendWinRTInterfaceNameForType(type, builder, flags); + } + else + { + builder.Append(type.FullName); + } } } return true; @@ -369,12 +479,52 @@ private static bool TryAppendSimpleTypeName(Type type, StringBuilder builder, Ty private static bool TryAppendWinRTInterfaceNameForType(Type type, StringBuilder builder, TypeNameGenerationFlags flags) { - Debug.Assert((flags & TypeNameGenerationFlags.NoCustomTypeName) != 0); - Debug.Assert(!type.IsGenericTypeDefinition); + Debug.Assert((flags & TypeNameGenerationFlags.ForGetRuntimeClassName) != 0); + Debug.Assert(!type.IsGenericTypeDefinition); + +#if NET + var runtimeClassNameAttribute = type.GetCustomAttribute(); + if (runtimeClassNameAttribute is not null) + { + builder.Append(runtimeClassNameAttribute.RuntimeClassName); + return true; + } + + var runtimeClassNameFromLookupTable = ComWrappersSupport.GetRuntimeClassNameForNonWinRTTypeFromLookupTable(type); + if (!string.IsNullOrEmpty(runtimeClassNameFromLookupTable)) + { + builder.Append(runtimeClassNameFromLookupTable); + return true; + } + + // There is no need to check for 'WinRTManagedOnlyTypeDetails' here, because this method can only be reached + // from 'GetRuntimeClassName' on some marshalled object, which is impossible to do with those type details. + + // AOT source generator should have generated the attribute with the class name. + if (!RuntimeFeature.IsDynamicCodeCompiled) + { + return false; + } +#endif - var visitedTypes = VisitedTypes.Value; - if (visitedTypes.Any(visited => visited.Type == type)) + var visitedTypes = visitedTypesInstance ??= new Stack(); + + // Manual helper to save binary size (no LINQ, no lambdas) and get better performance + static bool HasAnyVisitedTypes(Stack visitedTypes, Type type) + { + foreach (VisitedType visitedType in visitedTypes) + { + if (visitedType.Type == type) + { + return true; + } + } + + return false; + } + + if (HasAnyVisitedTypes(visitedTypes, type)) { // In this case, we've already visited the type when recursing through generic parameters. // Try to fall back to object if the parameter is covariant and the argument is compatable with object. @@ -388,29 +538,40 @@ private static bool TryAppendWinRTInterfaceNameForType(Type type, StringBuilder } else { - visitedTypes.Push(new VisitedType { Type = type }); - Type interfaceTypeToUse = null; - foreach (var iface in type.GetInterfaces()) - { - if (Projections.IsTypeWindowsRuntimeType(iface)) - { - if (interfaceTypeToUse is null || iface.IsAssignableFrom(interfaceTypeToUse)) - { - interfaceTypeToUse = iface; - } - } - } - - bool success = false; - - if (interfaceTypeToUse is object) - { - success = TryAppendTypeName(interfaceTypeToUse, builder, flags); +#if NET + [UnconditionalSuppressMessage("Trimming", "IL2070", Justification = "Updated binaries will have WinRTRuntimeClassNameAttribute which will be used instead.")] +#endif + static bool TryAppendWinRTInterfaceNameForTypeJit(Type type, StringBuilder builder, TypeNameGenerationFlags flags) + { + var visitedTypes = visitedTypesInstance; + + visitedTypes.Push(new VisitedType { Type = type }); + + Type interfaceTypeToUse = null; + foreach (var iface in type.GetInterfaces()) + { + if (Projections.IsTypeWindowsRuntimeType(iface)) + { + if (interfaceTypeToUse is null || interfaceTypeToUse.IsAssignableFrom(iface)) + { + interfaceTypeToUse = iface; + } + } + } + + bool success = false; + + if (interfaceTypeToUse is not null) + { + success = TryAppendTypeName(interfaceTypeToUse, builder, flags); + } + + visitedTypes.Pop(); + + return success; } - visitedTypes.Pop(); - - return success; + return TryAppendWinRTInterfaceNameForTypeJit(type, builder, flags); } } @@ -426,13 +587,32 @@ private static bool TryAppendTypeName(Type type, StringBuilder builder, TypeName if (type.IsSZArray) #endif { - builder.Append("Windows.Foundation.IReferenceArray`1<"); - if (TryAppendTypeName(type.GetElementType(), builder, flags & ~TypeNameGenerationFlags.GenerateBoxedName)) - { - builder.Append('>'); - return true; + var elementType = type.GetElementType(); + if (elementType.ShouldProvideIReference()) + { + builder.Append("Windows.Foundation.IReferenceArray`1<"); + if (TryAppendTypeName(elementType, builder, flags & ~TypeNameGenerationFlags.GenerateBoxedName)) + { + builder.Append('>'); + return true; + } + return false; + } + else + { + return false; } - return true; + } + + if ((flags & TypeNameGenerationFlags.GenerateBoxedName) != 0 && type.ShouldProvideIReference()) + { + builder.Append("Windows.Foundation.IReference`1<"); + if (!TryAppendSimpleTypeName(type, builder, flags & ~TypeNameGenerationFlags.GenerateBoxedName)) + { + return false; + } + builder.Append('>'); + return true; } if (!type.IsGenericType || type.IsGenericTypeDefinition) @@ -440,7 +620,7 @@ private static bool TryAppendTypeName(Type type, StringBuilder builder, TypeName return TryAppendSimpleTypeName(type, builder, flags); } - if ((flags & TypeNameGenerationFlags.NoCustomTypeName) != 0 && !Projections.IsTypeWindowsRuntimeType(type)) + if ((flags & TypeNameGenerationFlags.ForGetRuntimeClassName) != 0 && !Projections.IsTypeWindowsRuntimeType(type)) { return TryAppendWinRTInterfaceNameForType(type, builder, flags); } @@ -458,6 +638,8 @@ private static bool TryAppendTypeName(Type type, StringBuilder builder, TypeName Type[] genericTypeArguments = type.GetGenericArguments(); Type[] genericTypeParameters = definition.GetGenericArguments(); + var visitedTypes = visitedTypesInstance ??= new Stack(); + for (int i = 0; i < genericTypeArguments.Length; i++) { Type argument = genericTypeArguments[i]; @@ -473,9 +655,9 @@ private static bool TryAppendTypeName(Type type, StringBuilder builder, TypeName } first = false; - if ((flags & TypeNameGenerationFlags.NoCustomTypeName) != 0) + if ((flags & TypeNameGenerationFlags.ForGetRuntimeClassName) != 0) { - VisitedTypes.Value.Push(new VisitedType + visitedTypes.Push(new VisitedType { Type = type, Covariant = (genericTypeParameters[i].GenericParameterAttributes & GenericParameterAttributes.VarianceMask) == GenericParameterAttributes.Covariant @@ -484,9 +666,9 @@ private static bool TryAppendTypeName(Type type, StringBuilder builder, TypeName bool success = TryAppendTypeName(argument, builder, flags & ~TypeNameGenerationFlags.GenerateBoxedName); - if ((flags & TypeNameGenerationFlags.NoCustomTypeName) != 0) + if ((flags & TypeNameGenerationFlags.ForGetRuntimeClassName) != 0) { - VisitedTypes.Value.Pop(); + visitedTypes.Pop(); } if (!success) diff --git a/src/WinRT.Runtime/WeakReference.netstandard2.0.cs b/src/WinRT.Runtime/WeakReference.netstandard2.0.cs index dd40c1bca..b67c2b4bc 100644 --- a/src/WinRT.Runtime/WeakReference.netstandard2.0.cs +++ b/src/WinRT.Runtime/WeakReference.netstandard2.0.cs @@ -19,13 +19,13 @@ sealed class WeakReference where T : class { private System.WeakReference _managedWeakReference; - private IWeakReference _nativeWeakReference; + private global::WinRT.Interop.IWeakReference _nativeWeakReference; public WeakReference(T target) { _managedWeakReference = new System.WeakReference(target); - if (target is object && ComWrappersSupport.TryUnwrapObject(target, out var objRef)) + if (target is object && ComWrappersSupport.TryUnwrapObject(target, out var _)) { - _nativeWeakReference = target.As().GetWeakReference(); + _nativeWeakReference = target.As().GetWeakReference(); } } @@ -36,7 +36,7 @@ public void SetTarget(T target) _managedWeakReference.SetTarget(target); if (target is object && ComWrappersSupport.TryUnwrapObject(target, out _)) { - _nativeWeakReference = target.As().GetWeakReference(); + _nativeWeakReference = target.As().GetWeakReference(); } } } @@ -58,13 +58,13 @@ public bool TryGetTarget(out T target) } } - private static object ResolveNativeWeakReference(IWeakReference reference) + private static object ResolveNativeWeakReference(global::WinRT.Interop.IWeakReference reference) { if (reference is null) { return null; } - using var resolved = reference.Resolve(IUnknownVftbl.IID); + using var resolved = reference.Resolve(IID.IID_IUnknown); return ComWrappersSupport.CreateRcwForComObject(resolved.ThisPtr); } } diff --git a/src/WinRT.Runtime/WinRT.Runtime.csproj b/src/WinRT.Runtime/WinRT.Runtime.csproj index 9e44aa0e4..8e8922137 100644 --- a/src/WinRT.Runtime/WinRT.Runtime.csproj +++ b/src/WinRT.Runtime/WinRT.Runtime.csproj @@ -4,8 +4,8 @@ $(LibBuildTFMs) WinRT true - 9 - full + 11.0 + embedded true Microsoft Corporation Microsoft Corporation @@ -13,7 +13,6 @@ WinRT.Runtime $(VersionNumber) $(VersionNumber) - $(AssemblyVersionNumber) $(VersionNumber) en C#/WinRT Runtime v$(VersionString) @@ -23,10 +22,45 @@ key.snk true true + $(DefineConstants);CsWinRT_LANG_11_FEATURES + + false + github + + + $(NoWarn);CS9191 + + + + + $(AssemblyVersionNumber) + + + + true + + + + + + + + + + + %(Filename).g.cs + TextTemplatingFileGenerator + + + $([System.IO.Path]::GetFileNameWithoutExtension('%(Filename)')).tt + True + True + + - + @@ -40,9 +74,9 @@ - - - + + + @@ -51,35 +85,25 @@ true true true - 1.0.1 + 2.1.1 $(ApiCompatArgs) --allow-default-interface-methods $(MatchingRefApiCompatArgs) --allow-default-interface-methods - - + + + - - True - True - WinRTRuntimeErrorStrings.resx - - - - - - ResXFileCodeGenerator - WinRTRuntimeErrorStrings.Designer.cs - + - + <_ReferencePathDirectories Include="@(ReferencePath -> '%(RootDir)%(Directory)')" /> - + @(_ReferencePathDirectories->Distinct(), ',') diff --git a/src/WinRT.Runtime/WinRTRuntimeErrorStrings.Designer.cs b/src/WinRT.Runtime/WinRTRuntimeErrorStrings.Designer.cs index 69efce5db..fba59c96a 100644 --- a/src/WinRT.Runtime/WinRTRuntimeErrorStrings.Designer.cs +++ b/src/WinRT.Runtime/WinRTRuntimeErrorStrings.Designer.cs @@ -1,225 +1,83 @@ -//------------------------------------------------------------------------------ -// -// 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 { - using System; - - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Resources; +using System.Runtime.CompilerServices; + +#nullable enable + +namespace WinRT +{ /// - /// A strongly-typed resource class, for looking up localized strings, etc. + /// A strongly-typed resource class, for looking up localized strings, etc. + /// This is manually written to add support for trimming all resources when + /// the corresponding 'CsWinRTUseExceptionResourceKeys' is set, to save size. /// - // 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 WinRTRuntimeErrorStrings { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal WinRTRuntimeErrorStrings() { - } - - /// - /// 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.WinRTRuntimeErrorStrings", typeof(WinRTRuntimeErrorStrings).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 Index was outside the bounds of the array.. - /// - internal static string Arg_IndexOutOfRangeException { - get { - return ResourceManager.GetString("Arg_IndexOutOfRangeException", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The given key was not present in the dictionary.. - /// - internal static string Arg_KeyNotFound { - get { - return ResourceManager.GetString("Arg_KeyNotFound", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The given key '{0}' was not present in the dictionary.. - /// - internal static string Arg_KeyNotFoundWithKey { - get { - return ResourceManager.GetString("Arg_KeyNotFoundWithKey", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Only single dimensional arrays are supported for the requested action.. - /// - internal static string Arg_RankMultiDimNotSupported { - get { - return ResourceManager.GetString("Arg_RankMultiDimNotSupported", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to An item with the same key has already been added.. - /// - internal static string Argument_AddingDuplicate { - get { - return ResourceManager.GetString("Argument_AddingDuplicate", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to An item with the same key has already been added. Key: {0}. - /// - internal static string Argument_AddingDuplicateWithKey { - get { - return ResourceManager.GetString("Argument_AddingDuplicateWithKey", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The specified index is out of bounds of the specified array.. - /// - internal static string Argument_IndexOutOfArrayBounds { - get { - return ResourceManager.GetString("Argument_IndexOutOfArrayBounds", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The specified space is not sufficient to copy the elements from this Collection.. - /// - internal static string Argument_InsufficientSpaceToCopyCollection { - get { - return ResourceManager.GetString("Argument_InsufficientSpaceToCopyCollection", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Index was out of range. Must be non-negative and less than the size of the collection.. - /// - internal static string ArgumentOutOfRange_Index { - get { - return ResourceManager.GetString("ArgumentOutOfRange_Index", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to This collection cannot work with indices larger than Int32.MaxValue - 1 (0x7FFFFFFF - 1).. - /// - internal static string ArgumentOutOfRange_IndexLargerThanMaxValue { - get { - return ResourceManager.GetString("ArgumentOutOfRange_IndexLargerThanMaxValue", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Cannot remove the last element from an empty collection.. - /// - internal static string InvalidOperation_CannotRemoveLastFromEmptyCollection { - get { - return ResourceManager.GetString("InvalidOperation_CannotRemoveLastFromEmptyCollection", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The collection backing this Dictionary contains too many elements.. - /// - internal static string InvalidOperation_CollectionBackingDictionaryTooLarge { - get { - return ResourceManager.GetString("InvalidOperation_CollectionBackingDictionaryTooLarge", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to The collection backing this List contains too many elements.. - /// - internal static string InvalidOperation_CollectionBackingListTooLarge { - get { - return ResourceManager.GetString("InvalidOperation_CollectionBackingListTooLarge", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Enumeration already finished.. - /// - internal static string InvalidOperation_EnumEnded { - get { - return ResourceManager.GetString("InvalidOperation_EnumEnded", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Collection was modified; enumeration operation may not execute.. - /// - internal static string InvalidOperation_EnumFailedVersion { - get { - return ResourceManager.GetString("InvalidOperation_EnumFailedVersion", resourceCulture); - } - } - + internal static class WinRTRuntimeErrorStrings + { + private static ResourceManager? s_resourceManager; + /// - /// Looks up a localized string similar to Enumeration has not started. Call MoveNext.. + /// Gets the cached instance used by this class. /// - internal static string InvalidOperation_EnumNotStarted { - get { - return ResourceManager.GetString("InvalidOperation_EnumNotStarted", resourceCulture); - } - } - - /// - /// Looks up a localized string similar to Mutating a key collection derived from a dictionary is not allowed.. - /// - internal static string NotSupported_KeyCollectionSet { - get { - return ResourceManager.GetString("NotSupported_KeyCollectionSet", resourceCulture); - } - } + private static ResourceManager ResourceManager => s_resourceManager ?? (s_resourceManager = new("WinRT.WinRTRuntimeErrorStrings", typeof(WinRTRuntimeErrorStrings).Assembly)); + internal static string Arg_IndexOutOfRangeException => GetResourceString(); + + internal static string Arg_KeyNotFound => GetResourceString(); + + internal static string Arg_KeyNotFoundWithKey => GetResourceString(); + + internal static string Arg_RankMultiDimNotSupported => GetResourceString(); + + internal static string Argument_AddingDuplicate => GetResourceString(); + + internal static string Argument_AddingDuplicateWithKey => GetResourceString(); + + internal static string Argument_IndexOutOfArrayBounds => GetResourceString(); + + internal static string Argument_InsufficientSpaceToCopyCollection => GetResourceString(); + + internal static string ArgumentOutOfRange_Index => GetResourceString(); + + internal static string ArgumentOutOfRange_IndexLargerThanMaxValue => GetResourceString(); + + internal static string InvalidOperation_CannotRemoveLastFromEmptyCollection => GetResourceString(); + + internal static string InvalidOperation_CollectionBackingDictionaryTooLarge => GetResourceString(); + + internal static string InvalidOperation_CollectionBackingListTooLarge => GetResourceString(); + + internal static string InvalidOperation_EnumEnded => GetResourceString(); + + internal static string InvalidOperation_EnumFailedVersion => GetResourceString(); + + internal static string InvalidOperation_EnumNotStarted => GetResourceString(); + + internal static string NotSupported_KeyCollectionSet => GetResourceString(); + + internal static string NotSupported_ValueCollectionSet => GetResourceString(); + /// - /// Looks up a localized string similar to Mutating a value collection derived from a dictionary is not allowed.. + /// Gets a given resource string, while also respecting the feature switches in use. /// - internal static string NotSupported_ValueCollectionSet { - get { - return ResourceManager.GetString("NotSupported_ValueCollectionSet", resourceCulture); + /// The resource key to retrieve. + /// The resulting resource string (or just the key, if the feature switch is set). + private static string GetResourceString([CallerMemberName] string? resourceKey = null) + { + // If the feature switch is set, just return the resource key itself. + // This allows trimming the resource manager and embedded resources. + if (FeatureSwitches.UseExceptionResourceKeys) + { + return resourceKey!; } + + // Normal path retrieving the resource from the embedded .resx files. + // The returned string should always be not-null (unless it's missing). + return ResourceManager.GetString(resourceKey!)!; } } } + +// Restore in case this file is merged with others. +#nullable restore \ No newline at end of file diff --git a/src/benchmark.cmd b/src/benchmark.cmd index ef7ced243..49991002f 100644 --- a/src/benchmark.cmd +++ b/src/benchmark.cmd @@ -1,9 +1,13 @@ +set DOTNET_ROOT=%LocalAppData%\Microsoft\dotnet +set DOTNET_ROOT(x86)=%LocalAppData%\Microsoft\dotnet\x86 +set path=%DOTNET_ROOT%;%DOTNET_ROOT(x86)%;%path% + nuget restore TestWinRT\Test.sln nuget restore cswinrt.sln msbuild Benchmarks\Benchmarks.csproj -t:restore -t:build /p:platform=x64 /p:configuration=release /p:solutiondir=%~dp0 /p:IsDotnetBuild=false -dotnet %~dp0Benchmarks\bin\x64\Release\netcoreapp3.1\Benchmarks.dll -filter * --runtimes netcoreapp5.0 +dotnet %~dp0Benchmarks\bin\x64\Release\net8.0\Benchmarks.dll -filter * --runtimes net8.0 nuget restore Perf\ResultsComparer\ResultsComparer.sln msbuild Perf\ResultsComparer\ResultsComparer.sln -t:restore -t:build /p:platform="Any CPU" /p:configuration=release set baselinedir=%1 IF "%baselinedir%" == "" set baselinedir="Perf\BenchmarkBaseline" -Perf\ResultsComparer\bin\Release\net5.0\ResultsComparer.exe --base %baselinedir% --diff BenchmarkDotNet.Artifacts\results\ --threshold 5%% --xml BenchmarkDotNet.Artifacts\results\comparison.xml \ No newline at end of file +Perf\ResultsComparer\bin\Release\net8.0\ResultsComparer.exe --base %baselinedir% --diff BenchmarkDotNet.Artifacts\results\ --threshold 5%% --xml BenchmarkDotNet.Artifacts\results\comparison.xml \ No newline at end of file diff --git a/src/build.cmd b/src/build.cmd index 0454216b3..cbf1a4bd9 100644 --- a/src/build.cmd +++ b/src/build.cmd @@ -1,9 +1,9 @@ @echo off if /i "%cswinrt_echo%" == "on" @echo on -set CsWinRTBuildNetSDKVersion=6.0.301 -set CsWinRTBuildNet7SDKVersion=7.0.100-preview.7.22377.5 -set CsWinRTNet5SdkVersion=5.0.408 +set CsWinRTBuildNetSDKVersion=8.0.303 +set CsWinRTBuildNet9SDKVersion=9.0.201 + set this_dir=%~dp0 :dotnet @@ -24,29 +24,18 @@ powershell -NoProfile -ExecutionPolicy unrestricted -Command ^ &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) ^ -Version '%CsWinRTBuildNetSDKVersion%' -InstallDir '%DOTNET_ROOT(x86)%' -Architecture 'x86' -DownloadTimeout %DownloadTimeout% ^ -AzureFeed 'https://dotnetcli.blob.core.windows.net/dotnet' -rem Install .NET 7 used to build projection +rem Install .NET 9 used to build projection powershell -NoProfile -ExecutionPolicy unrestricted -Command ^ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; ^ &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) ^ --Version '%CsWinRTBuildNet7SDKVersion%' -InstallDir '%DOTNET_ROOT%' -Architecture 'x64' -DownloadTimeout %DownloadTimeout% ^ +-Version '%CsWinRTBuildNet9SDKVersion%' -InstallDir '%DOTNET_ROOT%' -Architecture 'x64' -DownloadTimeout %DownloadTimeout% ^ -AzureFeed 'https://dotnetcli.blob.core.windows.net/dotnet' -rem do we need a special, other link for x86 version of this net7 version? reason being we use a preview version? powershell -NoProfile -ExecutionPolicy unrestricted -Command ^ [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; ^ &([scriptblock]::Create((Invoke-WebRequest -UseBasicParsing 'https://dot.net/v1/dotnet-install.ps1'))) ^ --Version '%CsWinRTBuildNet7SDKVersion%' -InstallDir '%DOTNET_ROOT(x86)%' -Architecture 'x86' -DownloadTimeout %DownloadTimeout% ^ +-Version '%CsWinRTBuildNet9SDKVersion%' -InstallDir '%DOTNET_ROOT(x86)%' -Architecture 'x86' -DownloadTimeout %DownloadTimeout% ^ -AzureFeed 'https://dotnetcli.blob.core.windows.net/dotnet' -:globaljson -rem Create global.json for current .NET SDK, and with allowPrerelease=true -set global_json=%this_dir%global.json -echo { > %global_json% -echo "sdk": { >> %global_json% -echo "version": "%CsWinRTBuildNet7SDKVersion%", >> %global_json% -echo "allowPrerelease": true >> %global_json% -echo } >> %global_json% -echo } >> %global_json% - rem Preserve above for Visual Studio launch inheritance setlocal ENABLEDELAYEDEXPANSION @@ -89,16 +78,14 @@ if "%cswinrt_baseline_breaking_compat_errors%"=="" set cswinrt_baseline_breaking if "%cswinrt_baseline_assembly_version_compat_errors%"=="" set cswinrt_baseline_assembly_version_compat_errors=false set cswinrt_functional_tests=JsonValueFunctionCalls, ClassActivation, Structs, Events, DynamicInterfaceCasting, Collections, Async, DerivedClassActivation, DerivedClassAsBaseClass, CCW +set cswinrt_aot_functional_tests=JsonValueFunctionCalls, ClassActivation, Structs, Events, DynamicInterfaceCasting, Collections, Async, DerivedClassActivation, DerivedClassAsBaseClass, CCW -rem Generate prerelease targets file to exercise build warnings -set prerelease_targets=%this_dir%..\nuget\Microsoft.Windows.CsWinRT.Prerelease.targets -rem Create default %prerelease_targets% -echo ^ > %prerelease_targets% -echo ^> %prerelease_targets% -echo Condition="'$(NetCoreSdkVersion)' ^!= '%CsWinRTNet5SdkVersion%' and '$(Net5SdkVersion)' ^!= '%CsWinRTNet5SdkVersion%'"^> >> %prerelease_targets% -echo ^ >> %prerelease_targets% -echo ^ >> %prerelease_targets% -echo ^ >> %prerelease_targets% +if "%cswinrt_platform%" EQU "x86" set run_functional_tests=true +if "%cswinrt_platform%" EQU "x64" ( + if "%CIBuildReason%" NEQ "CI" ( + set run_functional_tests=true + ) +) goto :skip_build_tools rem VS 16.X BuildTools support (when a prerelease VS is required, until it is deployed to Azure Devops agents) @@ -135,7 +122,7 @@ if exist %nuget_dir%\nuget.exe ( ) ) if not exist %nuget_dir% md %nuget_dir% -if not exist %nuget_dir%\nuget.exe powershell -Command "Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/v5.8.0-preview.2/nuget.exe -OutFile %nuget_dir%\nuget.exe" +if not exist %nuget_dir%\nuget.exe powershell -Command "Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile %nuget_dir%\nuget.exe" %nuget_dir%\nuget update -self rem Note: packages.config-based (vcxproj) projects do not support msbuild /t:restore call %this_dir%get_testwinrt.cmd @@ -145,6 +132,13 @@ call :exec %nuget_dir%\nuget.exe restore %nuget_params% %this_dir%cswinrt.sln rem: Calling nuget restore again on ObjectLifetimeTests.Lifted.csproj to prevent .props from \microsoft.testplatform.testhost\build\netcoreapp2.1 from being included. Nuget.exe erroneously imports props files. https://github.com/NuGet/Home/issues/9672 call :exec %msbuild_path%msbuild.exe %this_dir%\Tests\ObjectLifetimeTests\ObjectLifetimeTests.Lifted.csproj /t:restore /p:platform=%cswinrt_platform%;configuration=%cswinrt_configuration% +if "%cswinrt_platform%" EQU "x64" ( + if /I "%cswinrt_configuration%" EQU "release" ( + rem We restore here as NAOT needs its own restore to pull in ILC + call :exec %msbuild_path%msbuild.exe %this_dir%\Tests\AuthoringTest\AuthoringTest.csproj /t:restore /p:platform=%cswinrt_platform%;configuration=%cswinrt_configuration%;RuntimeIdentifier=win-%cswinrt_platform% + ) +) + :build echo Building cswinrt for %cswinrt_platform% %cswinrt_configuration% call :exec %msbuild_path%msbuild.exe %cswinrt_build_params% /p:platform=%cswinrt_platform%;configuration=%cswinrt_configuration%;VersionNumber=%cswinrt_version_number%;VersionString=%cswinrt_version_string%;AssemblyVersionNumber=%cswinrt_assembly_version%;GenerateTestProjection=true;BaselineAllAPICompatError=%cswinrt_baseline_breaking_compat_errors%;BaselineAllMatchingRefApiCompatError=%cswinrt_baseline_assembly_version_compat_errors% %this_dir%cswinrt.sln @@ -156,10 +150,33 @@ if ErrorLevel 1 ( if "%cswinrt_platform%" NEQ "arm" ( if "%cswinrt_platform%" NEQ "arm64" ( - echo Publishing functional tests for %cswinrt_platform% %cswinrt_configuration% + echo Restore functional tests for %cswinrt_platform% %cswinrt_configuration% for %%a in (%cswinrt_functional_tests%) do ( + echo Restoring %%a + + rem Do restore separately to workaround issue where specifying TargetFramework causes nuget restore to propagate it to project references causing issues. + call :exec %msbuild_path%msbuild.exe /t:restore %cswinrt_build_params% /p:platform=%cswinrt_platform%;configuration=%cswinrt_configuration%;RuntimeIdentifier=win-%cswinrt_platform%;VersionNumber=%cswinrt_version_number%;VersionString=%cswinrt_version_string%;AssemblyVersionNumber=%cswinrt_assembly_version%;GenerateTestProjection=true;BaselineAllAPICompatError=%cswinrt_baseline_breaking_compat_errors%;BaselineAllMatchingRefApiCompatError=%cswinrt_baseline_assembly_version_compat_errors% /p:solutiondir=%this_dir% %this_dir%Tests\FunctionalTests\%%a\%%a.csproj + ) + ) +) + +if "%run_functional_tests%" EQU "true" ( + echo Publishing functional tests for %cswinrt_platform% %cswinrt_configuration% + for %%a in (%cswinrt_functional_tests%) do ( + echo Publishing %%a + + call :exec %msbuild_path%msbuild.exe /t:publish %cswinrt_build_params% /p:platform=%cswinrt_platform%;configuration=%cswinrt_configuration%;RuntimeIdentifier=win-%cswinrt_platform%;VersionNumber=%cswinrt_version_number%;VersionString=%cswinrt_version_string%;AssemblyVersionNumber=%cswinrt_assembly_version%;GenerateTestProjection=true;BaselineAllAPICompatError=%cswinrt_baseline_breaking_compat_errors%;BaselineAllMatchingRefApiCompatError=%cswinrt_baseline_assembly_version_compat_errors% /p:TargetFramework=net8.0 /p:solutiondir=%this_dir% %this_dir%Tests\FunctionalTests\%%a\%%a.csproj -bl:CCWBin%%a.binlog + ) +) + +if "%cswinrt_platform%" EQU "x64" ( + if /I "%cswinrt_configuration%" EQU "release" ( + echo Publishing AOT functional tests for %cswinrt_platform% %cswinrt_configuration% + for %%a in (%cswinrt_aot_functional_tests%) do ( echo Publishing %%a - call :exec %msbuild_path%msbuild.exe /t:restore /t:publish %cswinrt_build_params% /p:platform=%cswinrt_platform%;configuration=%cswinrt_configuration%;VersionNumber=%cswinrt_version_number%;VersionString=%cswinrt_version_string%;AssemblyVersionNumber=%cswinrt_assembly_version%;GenerateTestProjection=true;BaselineAllAPICompatError=%cswinrt_baseline_breaking_compat_errors%;BaselineAllMatchingRefApiCompatError=%cswinrt_baseline_assembly_version_compat_errors% /p:solutiondir=%this_dir% %this_dir%Tests\FunctionalTests\%%a\%%a.csproj + + rem No restore needed here as the previous run for .NET 6 did a restore without target framework. + call :exec %msbuild_path%msbuild.exe /t:publish %cswinrt_build_params% /p:platform=%cswinrt_platform%;configuration=%cswinrt_configuration%;RuntimeIdentifier=win-%cswinrt_platform%;VersionNumber=%cswinrt_version_number%;VersionString=%cswinrt_version_string%;AssemblyVersionNumber=%cswinrt_assembly_version%;GenerateTestProjection=true;BaselineAllAPICompatError=%cswinrt_baseline_breaking_compat_errors%;BaselineAllMatchingRefApiCompatError=%cswinrt_baseline_assembly_version_compat_errors% /p:TargetFramework=net8.0 /p:solutiondir=%this_dir% %this_dir%Tests\FunctionalTests\%%a\%%a.csproj ) ) ) @@ -209,7 +226,7 @@ if ErrorLevel 1 ( rem Running Object Lifetime Unit Tests echo Running object lifetime tests for %cswinrt_platform% %cswinrt_configuration% if '%NUGET_PACKAGES%'=='' set NUGET_PACKAGES=%USERPROFILE%\.nuget\packages -call :exec vstest.console.exe %this_dir%\Tests\ObjectLifetimeTests\bin\%cswinrt_platform%\%cswinrt_configuration%\net7.0-windows10.0.19041.0\win10-%cswinrt_platform%\ObjectLifetimeTests.Lifted.build.appxrecipe /TestAdapterPath:"%NUGET_PACKAGES%\mstest.testadapter\2.2.4-preview-20210513-02\build\_common" /framework:FrameworkUap10 /logger:trx;LogFileName=%this_dir%\VsTestResults.trx +call :exec vstest.console.exe %this_dir%\Tests\ObjectLifetimeTests\bin\%cswinrt_platform%\%cswinrt_configuration%\net8.0-windows10.0.19041.0\win-%cswinrt_platform%\ObjectLifetimeTests.Lifted.build.appxrecipe /TestAdapterPath:"%NUGET_PACKAGES%\mstest.testadapter\2.2.4-preview-20210513-02\build\_common" /framework:FrameworkUap10 /logger:trx;LogFileName=%this_dir%\VsTestResults.trx if ErrorLevel 1 ( echo. echo ERROR: Lifetime test failed, skipping NuGet pack @@ -229,6 +246,16 @@ if ErrorLevel 1 ( exit /b !ErrorLevel! ) +:sourcegeneratortest +rem Running Source Generator Unit Tests +echo Running source generator tests for %cswinrt_platform% %cswinrt_configuration% +call :exec %dotnet_exe% test --verbosity normal --no-build --logger trx;LogFilePath=%~dp0sourcegeneratortest_%cswinrt_version_string%.trx %this_dir%Tests\SourceGeneratorTest\SourceGeneratorTest.csproj /nologo /m /p:configuration=%cswinrt_configuration% -- RunConfiguration.TreatNoTestsAsError=true +if ErrorLevel 1 ( + echo. + echo ERROR: Source generator unit test failed, skipping NuGet pack + exit /b !ErrorLevel! +) + :hosttest rem Run WinRT.Host tests echo Running cswinrt host tests for %cswinrt_platform% %cswinrt_configuration% @@ -251,38 +278,60 @@ if ErrorLevel 1 ( :functionaltest rem Run functional tests -echo Running cswinrt functional tests for %cswinrt_platform% %cswinrt_configuration% +if "%run_functional_tests%" EQU "true" ( + echo Running cswinrt functional tests for %cswinrt_platform% %cswinrt_configuration% -for %%a in (%cswinrt_functional_tests%) do ( - echo Running %%a + for %%a in (%cswinrt_functional_tests%) do ( + echo Running %%a - call :exec %this_dir%Tests\FunctionalTests\%%a\bin\%cswinrt_configuration%\net6.0\win10-%cswinrt_platform%\publish\%%a.exe - if !errorlevel! NEQ 100 ( - echo. - echo ERROR: Functional test '%%a' failed with !errorlevel!, skipping NuGet pack - exit /b !ErrorLevel! + call :exec %this_dir%Tests\FunctionalTests\%%a\bin\%cswinrt_configuration%\net8.0\win-%cswinrt_platform%\publish\%%a.exe + if !errorlevel! NEQ 100 ( + echo. + echo ERROR: Functional test '%%a' failed with !errorlevel!, skipping NuGet pack + exit /b !ErrorLevel! + ) ) ) +if "%cswinrt_platform%" EQU "x64" ( + if /I "%cswinrt_configuration%" EQU "release" ( + echo Running cswinrt AOT functional tests for %cswinrt_platform% %cswinrt_configuration% + + for %%a in (%cswinrt_aot_functional_tests%) do ( + echo Running %%a + + call :exec %this_dir%Tests\FunctionalTests\%%a\bin\%cswinrt_configuration%\net8.0\win-%cswinrt_platform%\publish\%%a.exe + if !errorlevel! NEQ 100 ( + echo. + echo ERROR: AOT Functional test '%%a' failed with !errorlevel!, skipping NuGet pack + exit /b !ErrorLevel! + ) + ) + ) +) + + if "%cswinrt_label%"=="functionaltest" exit /b 0 :package rem We set the properties of the CsWinRT.nuspec here, and pass them as the -Properties option when we call `nuget pack` set cswinrt_bin_dir=%this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\cswinrt\bin\ set cswinrt_exe=%cswinrt_bin_dir%cswinrt.exe -set interop_winmd=%this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\cswinrt\obj\merged\WinRT.Interop.winmd +set interop_winmd=%cswinrt_bin_dir%WinRT.Interop.winmd set netstandard2_runtime=%this_dir%WinRT.Runtime\bin\%cswinrt_configuration%\netstandard2.0\WinRT.Runtime.dll -set net6_runtime=%this_dir%WinRT.Runtime\bin\%cswinrt_configuration%\net6.0\WinRT.Runtime.dll -set source_generator=%this_dir%Authoring\WinRT.SourceGenerator\bin\%cswinrt_configuration%\netstandard2.0\WinRT.SourceGenerator.dll +set net8_runtime=%this_dir%WinRT.Runtime\bin\%cswinrt_configuration%\net8.0\WinRT.Runtime.dll +set net9_runtime=%this_dir%WinRT.Runtime\bin\%cswinrt_configuration%\net9.0\WinRT.Runtime.dll +set source_generator_roslyn4080=%this_dir%Authoring\WinRT.SourceGenerator.Roslyn4080\bin\%cswinrt_configuration%\netstandard2.0\WinRT.SourceGenerator.dll +set source_generator_roslyn4120=%this_dir%Authoring\WinRT.SourceGenerator.Roslyn4120\bin\%cswinrt_configuration%\netstandard2.0\WinRT.SourceGenerator.dll set winrt_host_%cswinrt_platform%=%this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\WinRT.Host\bin\WinRT.Host.dll set winrt_host_resource_%cswinrt_platform%=%this_dir%_build\%cswinrt_platform%\%cswinrt_configuration%\WinRT.Host\bin\WinRT.Host.dll.mui -set winrt_shim=%this_dir%Authoring\WinRT.Host.Shim\bin\%cswinrt_configuration%\net6.0\WinRT.Host.Shim.dll -set guid_patch=%this_dir%Perf\IIDOptimizer\bin\%cswinrt_configuration%\net6.0\*.* -set cswinmd_outpath=%this_dir%Authoring\cswinmd\bin\%cswinrt_configuration%\net6.0 +set winrt_shim=%this_dir%Authoring\WinRT.Host.Shim\bin\%cswinrt_configuration%\net8.0\WinRT.Host.Shim.dll +set guid_patch=%this_dir%Perf\IIDOptimizer\bin\%cswinrt_configuration%\net8.0\*.* +set cswinmd_outpath=%this_dir%Authoring\cswinmd\bin\%cswinrt_configuration%\net8.0 rem Now call pack echo Creating nuget package -call :exec %nuget_dir%\nuget pack %this_dir%..\nuget\Microsoft.Windows.CsWinRT.nuspec -Properties cswinrt_exe=%cswinrt_exe%;interop_winmd=%interop_winmd%;netstandard2_runtime=%netstandard2_runtime%;net6_runtime=%net6_runtime%;source_generator=%source_generator%;cswinrt_nuget_version=%cswinrt_version_string%;winrt_host_x86=%winrt_host_x86%;winrt_host_x64=%winrt_host_x64%;winrt_host_arm=%winrt_host_arm%;winrt_host_arm64=%winrt_host_arm64%;winrt_host_resource_x86=%winrt_host_resource_x86%;winrt_host_resource_x64=%winrt_host_resource_x64%;winrt_host_resource_arm=%winrt_host_resource_arm%;winrt_host_resource_arm64=%winrt_host_resource_arm64%;winrt_shim=%winrt_shim%;guid_patch=%guid_patch% -OutputDirectory %cswinrt_bin_dir% -NonInteractive -Verbosity Detailed -NoPackageAnalysis -call :exec %nuget_dir%\nuget pack %this_dir%..\nuget\Microsoft.Windows.CsWinMD.nuspec -Properties cswinmd_outpath=%cswinmd_outpath%;source_generator=%source_generator%cswinmd_nuget_version=%cswinrt_version_string%; -OutputDirectory %cswinrt_bin_dir% -NonInteractive -Verbosity Detailed -NoPackageAnalysis +call :exec %nuget_dir%\nuget pack %this_dir%..\nuget\Microsoft.Windows.CsWinRT.nuspec -Properties cswinrt_exe=%cswinrt_exe%;interop_winmd=%interop_winmd%;netstandard2_runtime=%netstandard2_runtime%;net8_runtime=%net8_runtime%;net9_runtime=%net9_runtime%;source_generator_roslyn4080=%source_generator_roslyn4080%;source_generator_roslyn4120=%source_generator_roslyn4120%;cswinrt_nuget_version=%cswinrt_version_string%;winrt_host_x86=%winrt_host_x86%;winrt_host_x64=%winrt_host_x64%;winrt_host_arm=%winrt_host_arm%;winrt_host_arm64=%winrt_host_arm64%;winrt_host_resource_x86=%winrt_host_resource_x86%;winrt_host_resource_x64=%winrt_host_resource_x64%;winrt_host_resource_arm=%winrt_host_resource_arm%;winrt_host_resource_arm64=%winrt_host_resource_arm64%;winrt_shim=%winrt_shim%;guid_patch=%guid_patch% -OutputDirectory %cswinrt_bin_dir% -NonInteractive -Verbosity Detailed -NoPackageAnalysis +call :exec %nuget_dir%\nuget pack %this_dir%..\nuget\Microsoft.Windows.CsWinMD.nuspec -Properties cswinmd_outpath=%cswinmd_outpath%;source_generator_roslyn4080=%source_generator_roslyn4080%;cswinmd_nuget_version=%cswinrt_version_string%; -OutputDirectory %cswinrt_bin_dir% -NonInteractive -Verbosity Detailed -NoPackageAnalysis goto :eof :exec diff --git a/src/cswinrt.sln b/src/cswinrt.sln index f021c4c07..c9313f30c 100644 --- a/src/cswinrt.sln +++ b/src/cswinrt.sln @@ -9,14 +9,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "TestComponentCSharp", "Test EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UnitTest", "Tests\UnitTest\UnitTest.csproj", "{9A9F52CA-F624-43A4-B5EF-C50861F584C2}" ProjectSection(ProjectDependencies) = postProject - {AE3B0611-2FBB-42AB-A245-B4E79868A5F9} = {AE3B0611-2FBB-42AB-A245-B4E79868A5F9} {25244CED-966E-45F2-9711-1F51E951FF89} = {25244CED-966E-45F2-9711-1F51E951FF89} + {AE3B0611-2FBB-42AB-A245-B4E79868A5F9} = {AE3B0611-2FBB-42AB-A245-B4E79868A5F9} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cswinrt", "cswinrt\cswinrt.vcxproj", "{6ACFD2B2-E8AA-4CD4-AAD8-213CE8BB2637}" - ProjectSection(ProjectDependencies) = postProject - {25244CED-966E-45F2-9711-1F51E951FF89} = {25244CED-966E-45F2-9711-1F51E951FF89} - EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Files", "Files", "{96495AB4-2D86-47D1-A174-27D0B436DC98}" ProjectSection(SolutionItems) = preProject @@ -52,8 +49,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Projections", "Projections" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Windows", "Projections\Windows\Windows.csproj", "{FFA9A78B-F53F-43EE-AF87-24A80F4C330A}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinUI", "Projections\WinUI\WinUI.csproj", "{0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF}" -EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Nuget", "Nuget", "{967889B0-4C40-4EA2-8F11-26ECB88205FF}" ProjectSection(SolutionItems) = preProject ..\nuget\LICENSE = ..\nuget\LICENSE @@ -86,7 +81,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinRT.Host.Shim", "Authorin EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestHost.ProbeByClass", "Projections\TestHost.ProbeByClass\TestHost.ProbeByClass.csproj", "{EF3326B5-716F-41D2-AB30-4EFAB30955E2}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinRT.SourceGenerator", "Authoring\WinRT.SourceGenerator\WinRT.SourceGenerator.csproj", "{E0C26D3A-504A-4826-BAE2-DE775F865B2A}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinRT.SourceGenerator.Roslyn4080", "Authoring\WinRT.SourceGenerator.Roslyn4080\WinRT.SourceGenerator.Roslyn4080.csproj", "{E0C26D3A-504A-4826-BAE2-DE775F865B2A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthoringTest", "Tests\AuthoringTest\AuthoringTest.csproj", "{41E2A272-150F-42F5-AD40-047AAD9088A0}" EndProject @@ -117,8 +112,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Perf", "Perf", "{539DBDEF-3 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IIDOptimizer", "Perf\IIDOptimizer\IIDOptimizer.csproj", "{AE3B0611-2FBB-42AB-A245-B4E79868A5F9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reunion", "Projections\Reunion\Reunion.csproj", "{B6312AD1-A59E-4F3B-AA39-20B780FE9E15}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestSubset", "Projections\TestSubset\TestSubset.csproj", "{80F0EC48-5536-49D6-8ED1-B1F4CED88073}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_component_fast", "TestWinRT\test_component_fast\test_component_fast.vcxproj", "{0E0ACA62-A92F-44CF-BD41-AEB541946DF8}" @@ -128,16 +121,22 @@ EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CsWinMD", "Authoring\CsWinMD\CsWinMD.csproj", "{B63DC238-AF5B-4790-B038-4F5ABE09F963}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CSWinMDComponent", "Tests\CSWinMDComponent\CSWinMDComponent.vcxproj", "{D10DC47A-DB45-491C-852E-2B2165F25F86}" + ProjectSection(ProjectDependencies) = postProject + {B63DC238-AF5B-4790-B038-4F5ABE09F963} = {B63DC238-AF5B-4790-B038-4F5ABE09F963} + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JsonValueFunctionCalls", "Tests\FunctionalTests\JsonValueFunctionCalls\JsonValueFunctionCalls.csproj", "{EE5B4B23-5D4B-4DFB-8DC9-4DBB1B735CC8}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "FunctionalTests", "FunctionalTests", "{5ECC38F0-16FD-47E1-B8DC-8C474008AD55}" + ProjectSection(SolutionItems) = preProject + Tests\FunctionalTests\Directory.Build.props = Tests\FunctionalTests\Directory.Build.props + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PublishProfiles", "PublishProfiles", "{6FBE79A9-9D6D-4436-9F84-797ED1C9F676}" ProjectSection(SolutionItems) = preProject - Tests\FunctionalTests\PublishProfiles\win10-arm64.pubxml = Tests\FunctionalTests\PublishProfiles\win10-arm64.pubxml - Tests\FunctionalTests\PublishProfiles\win10-x64.pubxml = Tests\FunctionalTests\PublishProfiles\win10-x64.pubxml - Tests\FunctionalTests\PublishProfiles\win10-x86.pubxml = Tests\FunctionalTests\PublishProfiles\win10-x86.pubxml + Tests\FunctionalTests\PublishProfiles\win-arm64.pubxml = Tests\FunctionalTests\PublishProfiles\win-arm64.pubxml + Tests\FunctionalTests\PublishProfiles\win-x64.pubxml = Tests\FunctionalTests\PublishProfiles\win-x64.pubxml + Tests\FunctionalTests\PublishProfiles\win-x86.pubxml = Tests\FunctionalTests\PublishProfiles\win-x86.pubxml EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ClassActivation", "Tests\FunctionalTests\ClassActivation\ClassActivation.csproj", "{F0D6034B-5368-4B13-BBB9-4ABBC535AA0E}" @@ -158,6 +157,30 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DerivedClassAsBaseClass", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CCW", "Tests\FunctionalTests\CCW\CCW.csproj", "{C44DB047-5DF0-4732-98F4-A181D3AD8A7F}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WinAppSDK", "Projections\WinAppSDK\WinAppSDK.csproj", "{7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Test-Library", "Tests\FunctionalTests\TestLibrary\Test-Library.csproj", "{335D51AC-1DCF-4487-A2BD-34CE2F17B3C4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Windows.UI.Xaml", "Projections\Windows.UI.Xaml\Windows.UI.Xaml.csproj", "{E85F3614-79B6-4652-BDB0-64AF68874CE0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AuthoringWuxTest", "Tests\AuthoringWuxTest\AuthoringWuxTest.csproj", "{D60CFCAD-4A13-4047-91C8-9D7DF4753493}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "AuthoringWuxConsumptionTest", "Tests\AuthoringWuxConsumptionTest\AuthoringWuxConsumptionTest.vcxproj", "{A04A0416-5E35-4DD0-8226-63D941B28467}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestPublicExclusiveTo", "Projections\TestPublicExclusiveTo\TestPublicExclusiveTo.csproj", "{0B3A0096-5131-441B-935B-5E8A56343869}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestImplementExclusiveTo", "Tests\FunctionalTests\TestImplementExclusiveTo\TestImplementExclusiveTo.csproj", "{30EE902C-C1D3-40E0-B63C-59FFB4503633}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NonWinRT", "Tests\FunctionalTests\NonWinRT\NonWinRT.csproj", "{54D6C10E-19C9-4BCF-B237-682C0120FC1F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OptInMode", "Tests\FunctionalTests\OptInMode\OptInMode.csproj", "{FFC0B06E-78E7-44D5-8E67-8FE5744CE071}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SourceGeneratorTest", "Tests\SourceGeneratorTest\SourceGeneratorTest.csproj", "{CB7C338F-3850-4744-A1EA-3DC47B703C4A}" +EndProject +Project("{D954291E-2A0B-460D-934E-DC6B0785DB48}") = "WinRT.SourceGenerator", "Authoring\WinRT.SourceGenerator\WinRT.SourceGenerator.shproj", "{B0E5E9CC-5FB4-4F94-BD54-115A91A6AE9D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WinRT.SourceGenerator.Roslyn4120", "Authoring\WinRT.SourceGenerator.Roslyn4120\WinRT.SourceGenerator.Roslyn4120.csproj", "{20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|ARM = Debug|ARM @@ -247,6 +270,7 @@ Global {8E6FBCB2-B0C1-4E92-8AEB-2A11564E6E0D}.Release|ARM.ActiveCfg = Release|x86 {8E6FBCB2-B0C1-4E92-8AEB-2A11564E6E0D}.Release|ARM64.ActiveCfg = Release|x86 {8E6FBCB2-B0C1-4E92-8AEB-2A11564E6E0D}.Release|x64.ActiveCfg = Release|x64 + {8E6FBCB2-B0C1-4E92-8AEB-2A11564E6E0D}.Release|x64.Build.0 = Release|x64 {8E6FBCB2-B0C1-4E92-8AEB-2A11564E6E0D}.Release|x86.ActiveCfg = Release|x86 {8E6FBCB2-B0C1-4E92-8AEB-2A11564E6E0D}.Release|x86.Build.0 = Release|x86 {C6D580C5-7037-4733-B933-916FF400AFE2}.Debug|ARM.ActiveCfg = Debug|x86 @@ -275,18 +299,6 @@ Global {FFA9A78B-F53F-43EE-AF87-24A80F4C330A}.Release|x64.Build.0 = Release|x64 {FFA9A78B-F53F-43EE-AF87-24A80F4C330A}.Release|x86.ActiveCfg = Release|x86 {FFA9A78B-F53F-43EE-AF87-24A80F4C330A}.Release|x86.Build.0 = Release|x86 - {0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF}.Debug|ARM.ActiveCfg = Debug|x86 - {0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF}.Debug|ARM64.ActiveCfg = Debug|x86 - {0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF}.Debug|x64.ActiveCfg = Debug|x64 - {0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF}.Debug|x64.Build.0 = Debug|x64 - {0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF}.Debug|x86.ActiveCfg = Debug|x86 - {0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF}.Debug|x86.Build.0 = Debug|x86 - {0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF}.Release|ARM.ActiveCfg = Release|x86 - {0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF}.Release|ARM64.ActiveCfg = Release|x86 - {0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF}.Release|x64.ActiveCfg = Release|x64 - {0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF}.Release|x64.Build.0 = Release|x64 - {0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF}.Release|x86.ActiveCfg = Release|x86 - {0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF}.Release|x86.Build.0 = Release|x86 {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Debug|ARM.ActiveCfg = Debug|x86 {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Debug|ARM64.ActiveCfg = Debug|x86 {B34C96F4-3660-4B2D-8ABD-A4B428166DC7}.Debug|x64.ActiveCfg = Debug|x64 @@ -495,6 +507,7 @@ Global {BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Release|ARM64.ActiveCfg = Release|arm64 {BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Release|x64.ActiveCfg = Release|x64 {BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Release|x64.Build.0 = Release|x64 + {BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Release|x64.Deploy.0 = Release|x64 {BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Release|x86.ActiveCfg = Release|x86 {BA7390DC-6CD3-44BB-B8B0-32BF2D068450}.Release|x86.Build.0 = Release|x86 {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Debug|ARM.ActiveCfg = Debug|Any CPU @@ -511,18 +524,6 @@ Global {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Release|x64.Build.0 = Release|Any CPU {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Release|x86.ActiveCfg = Release|Any CPU {AE3B0611-2FBB-42AB-A245-B4E79868A5F9}.Release|x86.Build.0 = Release|Any CPU - {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|ARM.ActiveCfg = Debug|x86 - {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|ARM64.ActiveCfg = Debug|x86 - {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|x64.ActiveCfg = Debug|x64 - {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|x64.Build.0 = Debug|x64 - {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|x86.ActiveCfg = Debug|x86 - {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Debug|x86.Build.0 = Debug|x86 - {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|ARM.ActiveCfg = Release|x86 - {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|ARM64.ActiveCfg = Release|x86 - {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|x64.ActiveCfg = Release|x64 - {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|x64.Build.0 = Release|x64 - {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|x86.ActiveCfg = Release|x86 - {B6312AD1-A59E-4F3B-AA39-20B780FE9E15}.Release|x86.Build.0 = Release|x86 {80F0EC48-5536-49D6-8ED1-B1F4CED88073}.Debug|ARM.ActiveCfg = Debug|x86 {80F0EC48-5536-49D6-8ED1-B1F4CED88073}.Debug|ARM64.ActiveCfg = Debug|x86 {80F0EC48-5536-49D6-8ED1-B1F4CED88073}.Debug|x64.ActiveCfg = Debug|x64 @@ -713,6 +714,162 @@ Global {C44DB047-5DF0-4732-98F4-A181D3AD8A7F}.Release|x64.Build.0 = Release|x64 {C44DB047-5DF0-4732-98F4-A181D3AD8A7F}.Release|x86.ActiveCfg = Release|x86 {C44DB047-5DF0-4732-98F4-A181D3AD8A7F}.Release|x86.Build.0 = Release|x86 + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}.Debug|ARM.ActiveCfg = Debug|x86 + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}.Debug|ARM.Build.0 = Debug|x86 + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}.Debug|ARM64.ActiveCfg = Debug|x86 + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}.Debug|ARM64.Build.0 = Debug|x86 + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}.Debug|x64.ActiveCfg = Debug|x64 + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}.Debug|x64.Build.0 = Debug|x64 + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}.Debug|x86.ActiveCfg = Debug|x86 + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}.Debug|x86.Build.0 = Debug|x86 + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}.Release|ARM.ActiveCfg = Release|x86 + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}.Release|ARM.Build.0 = Release|x86 + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}.Release|ARM64.ActiveCfg = Release|x86 + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}.Release|ARM64.Build.0 = Release|x86 + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}.Release|x64.ActiveCfg = Release|x64 + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}.Release|x64.Build.0 = Release|x64 + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}.Release|x86.ActiveCfg = Release|x86 + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA}.Release|x86.Build.0 = Release|x86 + {335D51AC-1DCF-4487-A2BD-34CE2F17B3C4}.Debug|ARM.ActiveCfg = Debug|ARM + {335D51AC-1DCF-4487-A2BD-34CE2F17B3C4}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {335D51AC-1DCF-4487-A2BD-34CE2F17B3C4}.Debug|x64.ActiveCfg = Debug|x64 + {335D51AC-1DCF-4487-A2BD-34CE2F17B3C4}.Debug|x64.Build.0 = Debug|x64 + {335D51AC-1DCF-4487-A2BD-34CE2F17B3C4}.Debug|x86.ActiveCfg = Debug|x86 + {335D51AC-1DCF-4487-A2BD-34CE2F17B3C4}.Debug|x86.Build.0 = Debug|x86 + {335D51AC-1DCF-4487-A2BD-34CE2F17B3C4}.Release|ARM.ActiveCfg = Release|ARM + {335D51AC-1DCF-4487-A2BD-34CE2F17B3C4}.Release|ARM64.ActiveCfg = Release|ARM64 + {335D51AC-1DCF-4487-A2BD-34CE2F17B3C4}.Release|x64.ActiveCfg = Release|x64 + {335D51AC-1DCF-4487-A2BD-34CE2F17B3C4}.Release|x64.Build.0 = Release|x64 + {335D51AC-1DCF-4487-A2BD-34CE2F17B3C4}.Release|x86.ActiveCfg = Release|x86 + {335D51AC-1DCF-4487-A2BD-34CE2F17B3C4}.Release|x86.Build.0 = Release|x86 + {E85F3614-79B6-4652-BDB0-64AF68874CE0}.Debug|ARM.ActiveCfg = Debug|x86 + {E85F3614-79B6-4652-BDB0-64AF68874CE0}.Debug|ARM.Build.0 = Debug|x86 + {E85F3614-79B6-4652-BDB0-64AF68874CE0}.Debug|ARM64.ActiveCfg = Debug|x86 + {E85F3614-79B6-4652-BDB0-64AF68874CE0}.Debug|ARM64.Build.0 = Debug|x86 + {E85F3614-79B6-4652-BDB0-64AF68874CE0}.Debug|x64.ActiveCfg = Debug|x64 + {E85F3614-79B6-4652-BDB0-64AF68874CE0}.Debug|x64.Build.0 = Debug|x64 + {E85F3614-79B6-4652-BDB0-64AF68874CE0}.Debug|x86.ActiveCfg = Debug|x86 + {E85F3614-79B6-4652-BDB0-64AF68874CE0}.Debug|x86.Build.0 = Debug|x86 + {E85F3614-79B6-4652-BDB0-64AF68874CE0}.Release|ARM.ActiveCfg = Release|x86 + {E85F3614-79B6-4652-BDB0-64AF68874CE0}.Release|ARM.Build.0 = Release|x86 + {E85F3614-79B6-4652-BDB0-64AF68874CE0}.Release|ARM64.ActiveCfg = Release|x86 + {E85F3614-79B6-4652-BDB0-64AF68874CE0}.Release|ARM64.Build.0 = Release|x86 + {E85F3614-79B6-4652-BDB0-64AF68874CE0}.Release|x64.ActiveCfg = Release|x64 + {E85F3614-79B6-4652-BDB0-64AF68874CE0}.Release|x64.Build.0 = Release|x64 + {E85F3614-79B6-4652-BDB0-64AF68874CE0}.Release|x86.ActiveCfg = Release|x86 + {E85F3614-79B6-4652-BDB0-64AF68874CE0}.Release|x86.Build.0 = Release|x86 + {D60CFCAD-4A13-4047-91C8-9D7DF4753493}.Debug|ARM.ActiveCfg = Debug|x86 + {D60CFCAD-4A13-4047-91C8-9D7DF4753493}.Debug|ARM.Build.0 = Debug|x86 + {D60CFCAD-4A13-4047-91C8-9D7DF4753493}.Debug|ARM64.ActiveCfg = Debug|x86 + {D60CFCAD-4A13-4047-91C8-9D7DF4753493}.Debug|ARM64.Build.0 = Debug|x86 + {D60CFCAD-4A13-4047-91C8-9D7DF4753493}.Debug|x64.ActiveCfg = Debug|x64 + {D60CFCAD-4A13-4047-91C8-9D7DF4753493}.Debug|x64.Build.0 = Debug|x64 + {D60CFCAD-4A13-4047-91C8-9D7DF4753493}.Debug|x86.ActiveCfg = Debug|x86 + {D60CFCAD-4A13-4047-91C8-9D7DF4753493}.Debug|x86.Build.0 = Debug|x86 + {D60CFCAD-4A13-4047-91C8-9D7DF4753493}.Release|ARM.ActiveCfg = Release|x86 + {D60CFCAD-4A13-4047-91C8-9D7DF4753493}.Release|ARM.Build.0 = Release|x86 + {D60CFCAD-4A13-4047-91C8-9D7DF4753493}.Release|ARM64.ActiveCfg = Release|x86 + {D60CFCAD-4A13-4047-91C8-9D7DF4753493}.Release|ARM64.Build.0 = Release|x86 + {D60CFCAD-4A13-4047-91C8-9D7DF4753493}.Release|x64.ActiveCfg = Release|x64 + {D60CFCAD-4A13-4047-91C8-9D7DF4753493}.Release|x64.Build.0 = Release|x64 + {D60CFCAD-4A13-4047-91C8-9D7DF4753493}.Release|x86.ActiveCfg = Release|x86 + {D60CFCAD-4A13-4047-91C8-9D7DF4753493}.Release|x86.Build.0 = Release|x86 + {A04A0416-5E35-4DD0-8226-63D941B28467}.Debug|ARM.ActiveCfg = Debug|Win32 + {A04A0416-5E35-4DD0-8226-63D941B28467}.Debug|ARM.Build.0 = Debug|Win32 + {A04A0416-5E35-4DD0-8226-63D941B28467}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {A04A0416-5E35-4DD0-8226-63D941B28467}.Debug|ARM64.Build.0 = Debug|ARM64 + {A04A0416-5E35-4DD0-8226-63D941B28467}.Debug|x64.ActiveCfg = Debug|x64 + {A04A0416-5E35-4DD0-8226-63D941B28467}.Debug|x64.Build.0 = Debug|x64 + {A04A0416-5E35-4DD0-8226-63D941B28467}.Debug|x86.ActiveCfg = Debug|Win32 + {A04A0416-5E35-4DD0-8226-63D941B28467}.Debug|x86.Build.0 = Debug|Win32 + {A04A0416-5E35-4DD0-8226-63D941B28467}.Release|ARM.ActiveCfg = Release|Win32 + {A04A0416-5E35-4DD0-8226-63D941B28467}.Release|ARM.Build.0 = Release|Win32 + {A04A0416-5E35-4DD0-8226-63D941B28467}.Release|ARM64.ActiveCfg = Release|ARM64 + {A04A0416-5E35-4DD0-8226-63D941B28467}.Release|ARM64.Build.0 = Release|ARM64 + {A04A0416-5E35-4DD0-8226-63D941B28467}.Release|x64.ActiveCfg = Release|x64 + {A04A0416-5E35-4DD0-8226-63D941B28467}.Release|x64.Build.0 = Release|x64 + {A04A0416-5E35-4DD0-8226-63D941B28467}.Release|x86.ActiveCfg = Release|Win32 + {A04A0416-5E35-4DD0-8226-63D941B28467}.Release|x86.Build.0 = Release|Win32 + {0B3A0096-5131-441B-935B-5E8A56343869}.Debug|ARM.ActiveCfg = Debug|x64 + {0B3A0096-5131-441B-935B-5E8A56343869}.Debug|ARM64.ActiveCfg = Debug|x64 + {0B3A0096-5131-441B-935B-5E8A56343869}.Debug|x64.ActiveCfg = Debug|x64 + {0B3A0096-5131-441B-935B-5E8A56343869}.Debug|x64.Build.0 = Debug|x64 + {0B3A0096-5131-441B-935B-5E8A56343869}.Debug|x86.ActiveCfg = Debug|x86 + {0B3A0096-5131-441B-935B-5E8A56343869}.Debug|x86.Build.0 = Debug|x86 + {0B3A0096-5131-441B-935B-5E8A56343869}.Release|ARM.ActiveCfg = Release|x64 + {0B3A0096-5131-441B-935B-5E8A56343869}.Release|ARM64.ActiveCfg = Release|x64 + {0B3A0096-5131-441B-935B-5E8A56343869}.Release|x64.ActiveCfg = Release|x64 + {0B3A0096-5131-441B-935B-5E8A56343869}.Release|x64.Build.0 = Release|x64 + {0B3A0096-5131-441B-935B-5E8A56343869}.Release|x86.ActiveCfg = Release|x86 + {0B3A0096-5131-441B-935B-5E8A56343869}.Release|x86.Build.0 = Release|x86 + {30EE902C-C1D3-40E0-B63C-59FFB4503633}.Debug|ARM.ActiveCfg = Debug|x64 + {30EE902C-C1D3-40E0-B63C-59FFB4503633}.Debug|ARM64.ActiveCfg = Debug|x64 + {30EE902C-C1D3-40E0-B63C-59FFB4503633}.Debug|x64.ActiveCfg = Debug|x64 + {30EE902C-C1D3-40E0-B63C-59FFB4503633}.Debug|x64.Build.0 = Debug|x64 + {30EE902C-C1D3-40E0-B63C-59FFB4503633}.Debug|x86.ActiveCfg = Debug|x86 + {30EE902C-C1D3-40E0-B63C-59FFB4503633}.Debug|x86.Build.0 = Debug|x86 + {30EE902C-C1D3-40E0-B63C-59FFB4503633}.Release|ARM.ActiveCfg = Release|x64 + {30EE902C-C1D3-40E0-B63C-59FFB4503633}.Release|ARM64.ActiveCfg = Release|x64 + {30EE902C-C1D3-40E0-B63C-59FFB4503633}.Release|x64.ActiveCfg = Release|x64 + {30EE902C-C1D3-40E0-B63C-59FFB4503633}.Release|x64.Build.0 = Release|x64 + {30EE902C-C1D3-40E0-B63C-59FFB4503633}.Release|x86.ActiveCfg = Release|x86 + {30EE902C-C1D3-40E0-B63C-59FFB4503633}.Release|x86.Build.0 = Release|x86 + {54D6C10E-19C9-4BCF-B237-682C0120FC1F}.Debug|ARM.ActiveCfg = Debug|ARM + {54D6C10E-19C9-4BCF-B237-682C0120FC1F}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {54D6C10E-19C9-4BCF-B237-682C0120FC1F}.Debug|x64.ActiveCfg = Debug|x64 + {54D6C10E-19C9-4BCF-B237-682C0120FC1F}.Debug|x64.Build.0 = Debug|x64 + {54D6C10E-19C9-4BCF-B237-682C0120FC1F}.Debug|x86.ActiveCfg = Debug|x86 + {54D6C10E-19C9-4BCF-B237-682C0120FC1F}.Debug|x86.Build.0 = Debug|x86 + {54D6C10E-19C9-4BCF-B237-682C0120FC1F}.Release|ARM.ActiveCfg = Release|ARM + {54D6C10E-19C9-4BCF-B237-682C0120FC1F}.Release|ARM64.ActiveCfg = Release|ARM64 + {54D6C10E-19C9-4BCF-B237-682C0120FC1F}.Release|x64.ActiveCfg = Release|x64 + {54D6C10E-19C9-4BCF-B237-682C0120FC1F}.Release|x64.Build.0 = Release|x64 + {54D6C10E-19C9-4BCF-B237-682C0120FC1F}.Release|x86.ActiveCfg = Release|x86 + {54D6C10E-19C9-4BCF-B237-682C0120FC1F}.Release|x86.Build.0 = Release|x86 + {FFC0B06E-78E7-44D5-8E67-8FE5744CE071}.Debug|ARM.ActiveCfg = Debug|ARM + {FFC0B06E-78E7-44D5-8E67-8FE5744CE071}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {FFC0B06E-78E7-44D5-8E67-8FE5744CE071}.Debug|x64.ActiveCfg = Debug|x64 + {FFC0B06E-78E7-44D5-8E67-8FE5744CE071}.Debug|x64.Build.0 = Debug|x64 + {FFC0B06E-78E7-44D5-8E67-8FE5744CE071}.Debug|x86.ActiveCfg = Debug|x86 + {FFC0B06E-78E7-44D5-8E67-8FE5744CE071}.Debug|x86.Build.0 = Debug|x86 + {FFC0B06E-78E7-44D5-8E67-8FE5744CE071}.Release|ARM.ActiveCfg = Release|ARM + {FFC0B06E-78E7-44D5-8E67-8FE5744CE071}.Release|ARM64.ActiveCfg = Release|ARM64 + {FFC0B06E-78E7-44D5-8E67-8FE5744CE071}.Release|x64.ActiveCfg = Release|x64 + {FFC0B06E-78E7-44D5-8E67-8FE5744CE071}.Release|x64.Build.0 = Release|x64 + {FFC0B06E-78E7-44D5-8E67-8FE5744CE071}.Release|x86.ActiveCfg = Release|x86 + {FFC0B06E-78E7-44D5-8E67-8FE5744CE071}.Release|x86.Build.0 = Release|x86 + {CB7C338F-3850-4744-A1EA-3DC47B703C4A}.Debug|ARM.ActiveCfg = Debug|Any CPU + {CB7C338F-3850-4744-A1EA-3DC47B703C4A}.Debug|ARM.Build.0 = Debug|Any CPU + {CB7C338F-3850-4744-A1EA-3DC47B703C4A}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {CB7C338F-3850-4744-A1EA-3DC47B703C4A}.Debug|ARM64.Build.0 = Debug|Any CPU + {CB7C338F-3850-4744-A1EA-3DC47B703C4A}.Debug|x64.ActiveCfg = Debug|Any CPU + {CB7C338F-3850-4744-A1EA-3DC47B703C4A}.Debug|x64.Build.0 = Debug|Any CPU + {CB7C338F-3850-4744-A1EA-3DC47B703C4A}.Debug|x86.ActiveCfg = Debug|Any CPU + {CB7C338F-3850-4744-A1EA-3DC47B703C4A}.Debug|x86.Build.0 = Debug|Any CPU + {CB7C338F-3850-4744-A1EA-3DC47B703C4A}.Release|ARM.ActiveCfg = Release|Any CPU + {CB7C338F-3850-4744-A1EA-3DC47B703C4A}.Release|ARM.Build.0 = Release|Any CPU + {CB7C338F-3850-4744-A1EA-3DC47B703C4A}.Release|ARM64.ActiveCfg = Release|Any CPU + {CB7C338F-3850-4744-A1EA-3DC47B703C4A}.Release|ARM64.Build.0 = Release|Any CPU + {CB7C338F-3850-4744-A1EA-3DC47B703C4A}.Release|x64.ActiveCfg = Release|Any CPU + {CB7C338F-3850-4744-A1EA-3DC47B703C4A}.Release|x64.Build.0 = Release|Any CPU + {CB7C338F-3850-4744-A1EA-3DC47B703C4A}.Release|x86.ActiveCfg = Release|Any CPU + {CB7C338F-3850-4744-A1EA-3DC47B703C4A}.Release|x86.Build.0 = Release|Any CPU + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}.Debug|ARM.ActiveCfg = Debug|Any CPU + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}.Debug|ARM.Build.0 = Debug|Any CPU + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}.Debug|ARM64.ActiveCfg = Debug|Any CPU + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}.Debug|ARM64.Build.0 = Debug|Any CPU + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}.Debug|x64.ActiveCfg = Debug|Any CPU + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}.Debug|x64.Build.0 = Debug|Any CPU + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}.Debug|x86.ActiveCfg = Debug|Any CPU + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}.Debug|x86.Build.0 = Debug|Any CPU + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}.Release|ARM.ActiveCfg = Release|Any CPU + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}.Release|ARM.Build.0 = Release|Any CPU + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}.Release|ARM64.ActiveCfg = Release|Any CPU + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}.Release|ARM64.Build.0 = Release|Any CPU + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}.Release|x64.ActiveCfg = Release|Any CPU + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}.Release|x64.Build.0 = Release|Any CPU + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}.Release|x86.ActiveCfg = Release|Any CPU + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -724,7 +881,6 @@ Global {8E6FBCB2-B0C1-4E92-8AEB-2A11564E6E0D} = {6DA854DB-57BF-4FDD-AFF6-F70C965F48F6} {C6D580C5-7037-4733-B933-916FF400AFE2} = {6D41796B-9904-40B8-BBCB-40B2D1BAE44B} {FFA9A78B-F53F-43EE-AF87-24A80F4C330A} = {6D41796B-9904-40B8-BBCB-40B2D1BAE44B} - {0A991D5F-BFEE-4D2F-9AAD-6AD06470A5DF} = {6D41796B-9904-40B8-BBCB-40B2D1BAE44B} {03EEF460-2F10-4FBE-AFFA-53477D3FC8D5} = {6D41796B-9904-40B8-BBCB-40B2D1BAE44B} {7E33BCB7-19C5-4061-981D-BA695322708A} = {DA5AE0BA-E43D-4282-BC80-807DDE0C22C0} {B511B7C9-C8E2-47ED-A0D1-538C00747D30} = {CFB651EC-DAA4-4A11-ABCD-C77F90602EB5} @@ -739,7 +895,6 @@ Global {75B1621F-EC51-4D77-BD7E-BEE576B3ADC9} = {CFB651EC-DAA4-4A11-ABCD-C77F90602EB5} {493C7729-2F21-4198-AB09-BDF56BF501D3} = {CFB651EC-DAA4-4A11-ABCD-C77F90602EB5} {AE3B0611-2FBB-42AB-A245-B4E79868A5F9} = {539DBDEF-3B49-4503-9BD3-7EB83C2179CB} - {B6312AD1-A59E-4F3B-AA39-20B780FE9E15} = {6D41796B-9904-40B8-BBCB-40B2D1BAE44B} {80F0EC48-5536-49D6-8ED1-B1F4CED88073} = {6D41796B-9904-40B8-BBCB-40B2D1BAE44B} {0E0ACA62-A92F-44CF-BD41-AEB541946DF8} = {CFB651EC-DAA4-4A11-ABCD-C77F90602EB5} {8DB50E24-9599-4890-939D-C87C1EC5DDAD} = {CFB651EC-DAA4-4A11-ABCD-C77F90602EB5} @@ -757,8 +912,25 @@ Global {756947F1-26D7-49ED-A4E8-8C4F126182C8} = {5ECC38F0-16FD-47E1-B8DC-8C474008AD55} {E3AEC9A4-D1A3-448D-B980-F82894792AF1} = {5ECC38F0-16FD-47E1-B8DC-8C474008AD55} {C44DB047-5DF0-4732-98F4-A181D3AD8A7F} = {5ECC38F0-16FD-47E1-B8DC-8C474008AD55} + {7B803846-91AE-4B98-AC93-D3FCFB2DE5AA} = {6D41796B-9904-40B8-BBCB-40B2D1BAE44B} + {335D51AC-1DCF-4487-A2BD-34CE2F17B3C4} = {5ECC38F0-16FD-47E1-B8DC-8C474008AD55} + {E85F3614-79B6-4652-BDB0-64AF68874CE0} = {6D41796B-9904-40B8-BBCB-40B2D1BAE44B} + {D60CFCAD-4A13-4047-91C8-9D7DF4753493} = {CFB651EC-DAA4-4A11-ABCD-C77F90602EB5} + {A04A0416-5E35-4DD0-8226-63D941B28467} = {CFB651EC-DAA4-4A11-ABCD-C77F90602EB5} + {0B3A0096-5131-441B-935B-5E8A56343869} = {6D41796B-9904-40B8-BBCB-40B2D1BAE44B} + {30EE902C-C1D3-40E0-B63C-59FFB4503633} = {5ECC38F0-16FD-47E1-B8DC-8C474008AD55} + {54D6C10E-19C9-4BCF-B237-682C0120FC1F} = {5ECC38F0-16FD-47E1-B8DC-8C474008AD55} + {FFC0B06E-78E7-44D5-8E67-8FE5744CE071} = {5ECC38F0-16FD-47E1-B8DC-8C474008AD55} + {CB7C338F-3850-4744-A1EA-3DC47B703C4A} = {CFB651EC-DAA4-4A11-ABCD-C77F90602EB5} + {B0E5E9CC-5FB4-4F94-BD54-115A91A6AE9D} = {DA5AE0BA-E43D-4282-BC80-807DDE0C22C0} + {20F67B53-BDF2-4252-A0E1-6D0AB55BA2F9} = {DA5AE0BA-E43D-4282-BC80-807DDE0C22C0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {5AE8C9D7-2613-4E1A-A4F2-579BAC28D0A2} EndGlobalSection + GlobalSection(SharedMSBuildProjectFiles) = preSolution + Authoring\WinRT.SourceGenerator\WinRT.SourceGenerator.projitems*{20f67b53-bdf2-4252-a0e1-6d0ab55ba2f9}*SharedItemsImports = 5 + Authoring\WinRT.SourceGenerator\WinRT.SourceGenerator.projitems*{b0e5e9cc-5fb4-4f94-bd54-115a91a6ae9d}*SharedItemsImports = 13 + Authoring\WinRT.SourceGenerator\WinRT.SourceGenerator.projitems*{e0c26d3a-504a-4826-bae2-de775f865b2a}*SharedItemsImports = 5 + EndGlobalSection EndGlobal diff --git a/src/cswinrt/code_writers.h b/src/cswinrt/code_writers.h index 26bc84faf..9971ae62c 100644 --- a/src/cswinrt/code_writers.h +++ b/src/cswinrt/code_writers.h @@ -6,6 +6,7 @@ #include #include #include +#include #define INSPECTABLE_METHOD_COUNT 6 @@ -35,6 +36,9 @@ namespace cswinrt {"string", "String"}, }; + static concurrency::concurrent_unordered_set generic_type_instances; + generic_type_instance ConvertGenericTypeInstanceToConcreteType(writer& w, const generic_type_instance& generic_instance); + auto to_csharp_type(fundamental_type type) { return type_mappings[(int)type].csharp; @@ -163,6 +167,45 @@ namespace cswinrt }); } + // This checks for interfaces that have derived generic interfaces + // to handle the scenario where the class implementing that interface + // can be trimmed and we need to handle potential calls to the + // generic interface methods while not using IDIC. + bool has_derived_generic_interface(TypeDef const& type) + { + if (is_exclusive_to(type)) + { + return false; + } + + bool found = false; + + // Looking at derived interfaces and not the interface / type itself. + auto impls = type.InterfaceImpl(); + for (auto&& impl : impls) + { + call(get_type_semantics(impl.Interface()), + [&](type_definition const& type) + { + found |= has_derived_generic_interface(type); + }, + [&](generic_type_instance const&) + { + found = true; + }, + [](auto) + { + }); + + if (found) + { + return true; + } + } + + return false; + } + void write_fundamental_type(writer& w, fundamental_type type) { w.write(to_csharp_type(type)); @@ -175,6 +218,7 @@ namespace cswinrt void write_projection_type(writer& w, type_semantics const& semantics); void write_projection_type_for_name_type(writer& w, type_semantics const& semantics, typedef_name_type const& nameType); + void write_guid(writer& w, TypeDef const& type, bool lowerCase); void write_generic_type_name_base(writer& w, uint32_t index) { @@ -322,6 +366,19 @@ namespace cswinrt return w.write_temp(format, bind(type, nameType, false)); } + void write_static_abi_class_generic_instantiation_type(writer& w, type_semantics const& semantics) + { + writer::write_generic_type_name_guard g(w, [&](writer& w, uint32_t index) + { + write_projection_type_for_name_type(w, w.get_generic_arg_scope(index).first, typedef_name_type::Projected); + w.write(", "); + write_projection_type_for_name_type(w, w.get_generic_arg_scope(index).first, typedef_name_type::Projected); + w.write("Abi"); + }); + + w.write("%", bind(semantics, typedef_name_type::StaticAbiClass, false)); + } + void write_projection_type_for_name_type(writer& w, type_semantics const& semantics, typedef_name_type const& nameType) { call(semantics, @@ -361,6 +418,21 @@ namespace cswinrt write_projection_type_for_name_type(w, semantics, typedef_name_type::CCW); } + std::string get_generic_instantiation_class_type_name(writer& w, type_definition const& type) + { + separator s{ w }; + uint32_t index = 0; + auto generic_instantiation_class_name = escape_type_name_for_identifier(w.write_temp( + "%_%", + bind(type, typedef_name_type::NonProjected, true), + bind_each([&](writer& w, GenericParam const& /*gp*/) + { + s(); + write_projection_type_for_name_type(w, w.get_generic_arg(index++), typedef_name_type::NonProjected); + }, type.GenericParam()))); + return generic_instantiation_class_name; + } + bool is_keyword(std::string_view str) { static constexpr std::string_view keywords[] = @@ -470,22 +542,26 @@ namespace cswinrt { auto eventTypeCode = w.write_temp("%", bind(eventTypeSemantics, typedef_name_type::Projected, false)); std::string eventTypeName = "_EventSource_" + eventTypeCode; - std::regex re(R"-((\ |:|<|>|,|\.))-"); - w.write("%", std::regex_replace(eventTypeName, re, "_")); + w.write("%", escape_type_name_for_identifier(eventTypeName)); } - method_signature get_event_invoke_method(TypeDef const& eventType) + MethodDef get_event_invoke_method(TypeDef const& eventType) { for (auto&& method : eventType.MethodList()) { if (method.Name() == "Invoke") { - return method_signature(method); + return method; } } throw_invalid("Event type must have an Invoke method"); } + method_signature get_event_invoke_method_signature(TypeDef const& eventType) + { + return method_signature(get_event_invoke_method(eventType)); + } + void write_event_invoke_params(writer& w, method_signature const& methodSig) { w.write("%", bind_list(", ", methodSig.params())); @@ -707,6 +783,15 @@ namespace cswinrt write_abi_return(w, signature); } + void write_abi_parameters_without_return(writer& w, method_signature const& signature) + { + w.write("IntPtr thisPtr"); + for (auto&& param : signature.params()) + { + write_abi_parameter(w, param); + } + } + void write_abi_parameter_types(writer& w, method_signature const& signature) { w.write("IntPtr"); @@ -717,6 +802,15 @@ namespace cswinrt write_abi_return_type(w, signature); } + void write_abi_parameter_types_without_return(writer& w, method_signature const& signature) + { + w.write("IntPtr"); + for (auto&& param : signature.params()) + { + write_abi_parameter_type(w, param); + } + } + void write_abi_parameter_types_pointer(writer& w, method_signature const& signature) { w.write("IntPtr"); @@ -727,18 +821,65 @@ namespace cswinrt write_abi_return_type_pointer(w, signature); } + void write_abi_parameter_types_pointer_without_return(writer& w, method_signature const& signature) + { + w.write("IntPtr"); + for (auto&& param : signature.params()) + { + write_abi_parameter_type_pointer(w, param); + } + } + + void write_abi_delegate_parameter_types_pointer(writer& w, MethodDef const& method) + { + writer::write_generic_type_name_guard g(w, [&](writer& w, uint32_t index) + { + write_projection_type_for_name_type(w, w.get_generic_arg_scope(index).first, typedef_name_type::Projected); + w.write("Abi"); + }); + + w.write("delegate* unmanaged[Stdcall]<%, int>", + bind(method_signature(method))); + } + bool abi_signature_has_generic_parameters(writer& w, method_signature const& signature) { bool signature_has_generic_parameters{}; writer::write_generic_type_name_guard g(w, [&](writer& /*w*/, uint32_t /*index*/) { signature_has_generic_parameters = true; - }); + }); auto _ = w.write_temp("%", bind(signature)); return signature_has_generic_parameters; } + bool abi_signature_without_return_has_generic_parameters(writer& w, method_signature const& signature) + { + bool signature_has_generic_parameters{}; + + writer::write_generic_type_name_guard g(w, [&](writer& /*w*/, uint32_t /*index*/) { + signature_has_generic_parameters = true; + }); + + auto _ = w.write_temp("%", bind(signature)); + return signature_has_generic_parameters; + } + + bool projected_signature_has_generic_parameters(writer& w, method_signature const& signature) + { + bool signature_has_generic_parameters{}; + + writer::write_generic_type_name_guard g(w, [&](writer& /*w*/, uint32_t /*index*/) { + signature_has_generic_parameters = true; + }); + + auto _ = w.write_temp("%%", + bind_list(", ", signature.params()), + bind(signature)); + return signature_has_generic_parameters; + } + template void write_event_params(writer& w, row_base::value_type const& evt, write_params params) { @@ -914,7 +1055,7 @@ namespace cswinrt void write_abi_event_source_static_method_call(writer& w, type_semantics const& iface, Event const& evt, bool isSubscribeCall, std::string const& targetObjRef, bool is_static_event = false) { - w.write("%.Get_%(%, %).%(value)", + w.write("%.Get_%2(%, %).%(value)", bind(iface, typedef_name_type::StaticAbiClass, true), evt.Name(), targetObjRef, @@ -928,7 +1069,7 @@ namespace cswinrt w.write("(IWinRTObject)this"); } }), - isSubscribeCall ? "Item1" : "Item2"); + isSubscribeCall ? "Subscribe" : "Unsubscribe"); } void write_method(writer& w, method_signature signature, std::string_view method_name, @@ -950,7 +1091,7 @@ namespace cswinrt if (paramsForStaticMethodCall.has_value()) { w.write("%", bind(paramsForStaticMethodCall.value().first, paramsForStaticMethodCall.value().second, - w.write_temp("%", bind(paramsForStaticMethodCall.value().first)))); + w.write_temp("%", method_target))); } else { @@ -964,12 +1105,15 @@ namespace cswinrt void write_explicitly_implemented_method_for_abi(writer& w, MethodDef const& method, std::string_view return_type, TypeDef const& method_interface, std::string_view method_target) { + // In authoring scenarios, exclusive interfaces don't exist, so use the CCW impl type. + bool implement_ccw_interface = does_abi_interface_implement_ccw_interface(method_interface); + method_signature signature{ method }; w.write(R"( % %.%(%) => %.%(%); )", return_type, - bind(method_interface, typedef_name_type::CCW, false), + bind(method_interface, implement_ccw_interface ? typedef_name_type::CCW : typedef_name_type::Projected, false), method.Name(), bind_list(", ", signature.params()), method_target, @@ -1117,11 +1261,13 @@ namespace cswinrt auto static_method_params = call_static_method.has_value() ? std::optional(std::pair(call_static_method.value(), method)) : std::nullopt; if (!is_private) { - write_method(w, signature, method.Name(), return_type, interface_member, access_spec, method_spec, platform_attribute, - static_method_params); + write_method(w, signature, method.Name(), return_type, + static_method_params.has_value() ? w.write_temp("%", bind(static_method_params.value().first)) : interface_member, + access_spec, method_spec, platform_attribute, static_method_params); } - if (is_overridable || !is_exclusive_to(method.Parent())) + // If overridable or private, we need to generate the explcit method + if (is_overridable || is_private) { w.write(R"( %% %.%(%) => %;)", @@ -1174,7 +1320,7 @@ namespace cswinrt if (params_for_static_getter.has_value()) { w.write("%", bind(params_for_static_getter.value().first, params_for_static_getter.value().second, - w.write_temp("%", bind(params_for_static_getter.value().first)))); + w.write_temp("%", getter_target))); } else { @@ -1209,7 +1355,7 @@ namespace cswinrt if (params_for_static_getter.has_value()) { w.write("%", bind(params_for_static_getter.value().first, params_for_static_getter.value().second, - w.write_temp("%", bind(params_for_static_getter.value().first)))); + w.write_temp("%", getter_target))); } else { @@ -1221,7 +1367,7 @@ namespace cswinrt if (params_for_static_setter.has_value()) { w.write("%", bind(params_for_static_setter.value().first, params_for_static_setter.value().second, - w.write_temp("%", bind(params_for_static_setter.value().first)))); + w.write_temp("%", setter_target))); } else { @@ -1247,8 +1393,7 @@ namespace cswinrt { auto interfaceTypeCode = w.write_temp("%", bind(ifaceTypeSemantics, typedef_name_type::Projected, true)); std::string interfaceTypeName = "_lazy_" + interfaceTypeCode; - std::regex re(R"-((\ |:|<|>|,|\.))-"); - w.write("%", std::regex_replace(interfaceTypeName, re, "_")); + w.write("%", escape_type_name_for_identifier(interfaceTypeName)); } void write_lazy_interface_initialization(writer& w, TypeDef const& type) @@ -1309,7 +1454,10 @@ private % Make_%() std::string write_explicit_name(writer& w, TypeDef const& iface, std::string_view name) { - return w.write_temp("%.%", write_type_name_temp(w, iface, "%", typedef_name_type::CCW), name); + // In authoring scenarios, exclusive interfaces don't exist, so use the CCW impl type. + bool implement_ccw_interface = does_abi_interface_implement_ccw_interface(iface); + + return w.write_temp("%.%", write_type_name_temp(w, iface, "%", implement_ccw_interface ? typedef_name_type::CCW : typedef_name_type::Projected), name); } std::string write_prop_type(writer& w, Property const& prop) @@ -1338,7 +1486,7 @@ private % Make_%() if (event.Name() == "CanExecuteChanged" && event_type == "global::System.EventHandler") { auto parent_type = w.write_temp("%", bind(event.Parent(), typedef_name_type::NonProjected, true)); - if (parent_type == "Microsoft.UI.Xaml.Input.ICommand") + if (parent_type == "Microsoft.UI.Xaml.Input.ICommand" || parent_type == "Windows.UI.Xaml.Input.ICommand") { event_type = "global::System.EventHandler"; } @@ -1360,7 +1508,7 @@ remove => %; { auto&& [iface_type_semantics, _, is_static] = paramsForStaticMethodCall.value(); w.write("%", bind(iface_type_semantics, event, true, - w.write_temp("%", bind(iface_type_semantics)), is_static)); + w.write_temp("%", event_target), is_static)); } else { @@ -1372,7 +1520,7 @@ remove => %; { auto&& [iface_type_semantics, _, is_static] = paramsForStaticMethodCall.value(); w.write("%", bind(iface_type_semantics, event, false, - w.write_temp("%", bind(iface_type_semantics)), is_static)); + w.write_temp("%", event_target), is_static)); } else { @@ -1404,10 +1552,13 @@ remove => %; bool is_private = is_implemented_as_private_method(w, class_type, add); if (!is_private) { - write_event(w, event.Name(), event, interface_member, visibility, ""sv, platform_attribute, call_static_method.has_value() ? std::optional(std::tuple(call_static_method.value(), event, false)) : std::nullopt); + write_event(w, event.Name(), event, + call_static_method.has_value() ? w.write_temp("%", bind(call_static_method.value())) : interface_member, + visibility, ""sv, platform_attribute, call_static_method.has_value() ? std::optional(std::tuple(call_static_method.value(), event, false)) : std::nullopt); } - if (is_overridable || !is_exclusive_to(event.Parent())) + // If overridable or private, we need to generate the explicit event + if (is_overridable || is_private) { write_event( w, @@ -1806,105 +1957,135 @@ remove => %; return result; } - void write_composing_factory_method(writer& w, MethodDef const& method); - - void write_abi_method_with_raw_return_type(writer& w, MethodDef const& method); + void write_class_static_cache_definition(writer& w, TypeDef const& staticsType, TypeDef const& classType) + { + if (staticsType) + { + auto factory_class_name = settings.netstandard_compat ? + w.write_temp("BaseFactory<%.Vftbl>", bind(staticsType, typedef_name_type::ABI, true)) : + w.write_temp("BaseFactory"); - template - std::string write_factory_cache_object(writer& w, TypeDef const& factory_type, TypeDef const& class_type); + auto statics_type_name = staticsType.TypeName(); + w.write(R"( +private static % _% = new %("%.%", %.IID); +)", + factory_class_name, + statics_type_name, + factory_class_name, + classType.TypeNamespace(), + classType.TypeName(), + bind(staticsType, typedef_name_type::StaticAbiClass, true)); + } + } - std::string write_static_cache_object(writer& w, std::string_view cache_type_name, TypeDef const& class_type) + void write_activation_factory_objref_definition(writer& w, TypeDef const& classType) { - auto instance = - w.write_temp( - "private static readonly _% _instance = new _%();", - cache_type_name, - cache_type_name); - auto factoryAs = w.write_temp("%", - bind([&](writer& w) - { - if (is_static(class_type)) - { - w.write("_factory._As"); - } - else - { - w.write("ActivationFactory<%>.As", class_type.TypeName()); - } - }) - ); + auto objrefname = w.write_temp("%", bind(classType)); + w.write(R"( +private static volatile IObjectReference __%; +private static IObjectReference % +{ + get + { + var factory = __%; + if (factory != null && factory.IsInCurrentContext) + { + return factory; + } + else + { + return __% = ActivationFactory.Get("%.%"); + } + } +} +)", + objrefname, + objrefname, + objrefname, + objrefname, + classType.TypeNamespace(), + classType.TypeName()); + } + void write_static_objref_definition(writer& w, TypeDef const& staticsType, TypeDef const& classType) + { if (settings.netstandard_compat) - { - auto cache_vftbl_type = w.write_temp("ABI.%.%.Vftbl", - class_type.TypeNamespace(), - cache_type_name); - auto cache_interface = - w.write_temp( - R"(%<%>)", - factoryAs, - cache_vftbl_type); + { + auto vftblType = w.write_temp("%.Vftbl", bind(staticsType, typedef_name_type::ABI, true)); + auto objrefname = w.write_temp("%", bind(staticsType)); w.write(R"( -internal sealed class _% : ABI.%.% +private static volatile ObjectReference<%> __%; +private static ObjectReference<%> Make__%() { -public _%() : base(%()) { } -% -internal static % Instance => _instance; + global::System.Threading.Interlocked.CompareExchange(ref __%, ActivationFactory.Get<%>("%.%", %.IID), null); + return __%; } +private static ObjectReference<%> % => __% ?? Make__%(); )", - cache_type_name, - class_type.TypeNamespace(), - cache_type_name, - cache_type_name, - cache_interface, - instance, - cache_type_name); + vftblType, + objrefname, + vftblType, + objrefname, + objrefname, + vftblType, + classType.TypeNamespace(), + classType.TypeName(), + bind(staticsType, typedef_name_type::StaticAbiClass, true), + objrefname, + vftblType, + objrefname, + objrefname, + objrefname); } else { + auto objrefname = w.write_temp("%", bind(staticsType)); w.write(R"( -internal sealed class _% : IWinRTObject -{ -private IObjectReference _obj; -public _%() -{ -_obj = %(GuidGenerator.GetIID(typeof(%.%).GetHelperType())); -} - -% -internal static % Instance => (%)_instance; - -IObjectReference IWinRTObject.NativeObject => _obj; -bool IWinRTObject.HasUnwrappableNativeObject => false; -private volatile global::System.Collections.Concurrent.ConcurrentDictionary _queryInterfaceCache; -private global::System.Collections.Concurrent.ConcurrentDictionary MakeQueryInterfaceCache() -{ - global::System.Threading.Interlocked.CompareExchange(ref _queryInterfaceCache, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); - return _queryInterfaceCache; -} -global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.QueryInterfaceCache => _queryInterfaceCache ?? MakeQueryInterfaceCache(); -private volatile global::System.Collections.Concurrent.ConcurrentDictionary _additionalTypeData; -private global::System.Collections.Concurrent.ConcurrentDictionary MakeAdditionalTypeData() +private static volatile IObjectReference __%; +private static IObjectReference % { - global::System.Threading.Interlocked.CompareExchange(ref _additionalTypeData, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); - return _additionalTypeData; -} -global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.AdditionalTypeData => _additionalTypeData ?? MakeAdditionalTypeData(); + get + { + var factory = __%; + if (factory != null && factory.IsInCurrentContext) + { + return factory; + } + else + { + return __% = ActivationFactory.Get("%.%", %.IID); + } + } } )", - cache_type_name, - cache_type_name, - factoryAs, - class_type.TypeNamespace(), - cache_type_name, - instance, - cache_type_name, - cache_type_name); + objrefname, + objrefname, + objrefname, + objrefname, + classType.TypeNamespace(), + classType.TypeName(), + bind(staticsType, typedef_name_type::StaticAbiClass, true)); } + } - return w.write_temp("_%.Instance", cache_type_name); + template + void write_static_abi_class_raw(writer& w, TypeDef const& factory_type) + { + w.write(R"( +private static class _% +{%} +)", + bind(factory_type, typedef_name_type::StaticAbiClass, false), + bind_each([&](writer& w, MethodDef const& method) + { + method_writer(w, factory_type, method); + }, factory_type.MethodList())); } + void write_static_composing_factory_method(writer& w, TypeDef const& iface, MethodDef const& method); + + void write_static_abi_method_with_raw_return_type(writer& w, TypeDef const& iface, MethodDef const& method); + static std::string get_default_interface_name(writer& w, TypeDef const& type, bool abiNamespace = true, bool forceCCW = false) { return w.write_temp("%", bind(get_type_semantics(get_default_interface(type)), abiNamespace ? typedef_name_type::ABI : forceCCW ? typedef_name_type::CCW : typedef_name_type::Projected, false)); @@ -1915,9 +2096,12 @@ global::System.Collections.Concurrent.ConcurrentDictionary(w, factory_type, class_type); - auto platform_attribute = write_platform_attribute_temp(w, factory_type); + write_static_abi_class_raw(w, factory_type); + write_static_objref_definition(w, factory_type, class_type); + auto cache_object = w.write_temp("%", bind(factory_type)); + auto gc_pressure_amount = get_gc_pressure_amount(class_type); + auto platform_attribute = write_platform_attribute_temp(w, factory_type); for (auto&& method : factory_type.MethodList()) { method_signature signature{ method }; @@ -1925,7 +2109,7 @@ global::System.Collections.Concurrent.ConcurrentDictionary)(() => { -IntPtr ptr = (%.%(%)); +IntPtr ptr = (_%.%(%%%)); try { return %(ComWrappersSupport.GetObjectReferenceForInterface(ptr)); @@ -1941,15 +2125,18 @@ MarshalInspectable.DisposeAbi(ptr); class_type.TypeName(), bind_list(", ", signature.params()), default_interface_name, - cache_object, + bind(factory_type, typedef_name_type::StaticAbiClass, false), method.Name(), + cache_object, + signature.has_params() ? ", " : "", bind_list(", ", signature.params()), "new " + default_interface_name ); } else { - auto default_interface_typedef = for_typedef(w, get_type_semantics(get_default_interface(class_type)), [&](auto&& iface) { return iface; }); + auto default_type_semantics = get_type_semantics(get_default_interface(class_type)); + auto default_interface_typedef = for_typedef(w, default_type_semantics, [&](auto&& iface) { return iface; }); auto is_manually_gen_default_interface = is_manually_generated_iface(default_interface_typedef); bool has_base_type = !std::holds_alternative(get_type_semantics(class_type.Extends())); @@ -1957,10 +2144,10 @@ MarshalInspectable.DisposeAbi(ptr); w.write(R"( %public %(%) % { -IntPtr ptr = (%.%(%)); +IntPtr ptr = (_%.%(%%%)); try { -_inner = ComWrappersSupport.GetObjectReferenceForInterface(ptr); +_inner = ComWrappersSupport.GetObjectReferenceForInterface(ptr, %.IID, false); % } finally @@ -1972,9 +2159,12 @@ MarshalInspectable.DisposeAbi(ptr); class_type.TypeName(), bind_list(", ", signature.params()), has_base_type ? ":base(global::WinRT.DerivedComposed.Instance)" : "", - cache_object, + bind(factory_type, typedef_name_type::StaticAbiClass, false), method.Name(), + cache_object, + signature.has_params() ? ", " : "", bind_list(", ", signature.params()), + bind(default_type_semantics, typedef_name_type::StaticAbiClass, true), bind([&](writer& w) { if (is_manually_gen_default_interface) @@ -1987,42 +2177,72 @@ MarshalInspectable.DisposeAbi(ptr); w.write(R"( ComWrappersSupport.RegisterObjectForInterface(this, ThisPtr); % -} +%} )", - settings.netstandard_compat ? "" : "ComWrappersHelper.Init(_inner, false);"); + settings.netstandard_compat ? "" : "ComWrappersHelper.Init(_inner, false);", + [&](writer& w) + { + if (!gc_pressure_amount || settings.netstandard_compat) return; + w.write("GC.AddMemoryPressure(%);\n", gc_pressure_amount); + }); } } else { - w.write(R"( -public %() : this(%(ActivationFactory<%>.ActivateInstance())) + write_activation_factory_objref_definition(w, class_type); + auto objrefname = w.write_temp("%", bind(class_type)); + + if (settings.netstandard_compat) + { + w.write(R"( +public %() : this(new %(global::ABI.WinRT.Interop.IActivationFactoryMethods.ActivateInstanceUnsafe(%))) +{ +ComWrappersSupport.RegisterObjectForInterface(this, ThisPtr); +} +)", + class_type.TypeName(), + default_interface_name, + objrefname); + } + else + { + bool has_base_type = !std::holds_alternative(get_type_semantics(class_type.Extends())); + auto default_type_semantics = get_type_semantics(get_default_interface(class_type)); + auto default_interface_typedef = for_typedef(w, default_type_semantics, [&](auto&& iface) { return iface; }); + auto is_manually_gen_default_interface = is_manually_generated_iface(default_interface_typedef); + + w.write(R"( +public %() % { +_inner = global::ABI.WinRT.Interop.IActivationFactoryMethods.ActivateInstanceUnsafe(%, %.IID); ComWrappersSupport.RegisterObjectForInterface(this, ThisPtr); +ComWrappersHelper.Init(_inner, false); % } )", - class_type.TypeName(), - settings.netstandard_compat ? "new " + default_interface_name : "", - class_type.TypeName(), - settings.netstandard_compat ? "" : "ComWrappersHelper.Init(_inner, false);"); + class_type.TypeName(), + has_base_type ? ":base(global::WinRT.DerivedComposed.Instance)" : "", + objrefname, + bind(default_type_semantics, typedef_name_type::StaticAbiClass, true), + bind([&](writer& w) + { + if (is_manually_gen_default_interface) + { + w.write("_defaultLazy = new Lazy<%>(() => (%)new SingleInterfaceOptimizedObject(typeof(%), _inner));", default_interface_name, default_interface_name, default_interface_name); + } + })); + } } } bool is_manually_generated_iface(TypeDef const& ifaceType) { - if (ifaceType.TypeNamespace() == "Windows.Foundation.Collections" && - (ifaceType.TypeName() == "IVector`1" - || ifaceType.TypeName() == "IMap`2") - || ifaceType.TypeName() == "IIterable`1" - || ifaceType.TypeName() == "IMapView`2" - || ifaceType.TypeName() == "IVectorView`1") - { - return false; - } - if (auto mapping = get_mapped_type(ifaceType.TypeNamespace(), ifaceType.TypeName())) + if (ifaceType.TypeNamespace() == "Microsoft.UI.Xaml.Interop" && + (ifaceType.TypeName() == "IBindableVector" || ifaceType.TypeName() == "IBindableIterable")) { return true; } + return false; } @@ -2030,8 +2250,7 @@ ComWrappersSupport.RegisterObjectForInterface(this, ThisPtr); { auto objRefTypeCode = w.write_temp("%", bind(ifaceTypeSemantics, typedef_name_type::Projected, true)); std::string objRefTypeName = "_objRef_" + objRefTypeCode; - std::regex re(R"-((\ |:|<|>|,|\.))-"); - w.write("%", std::regex_replace(objRefTypeName, re, "_")); + w.write("%", escape_type_name_for_identifier(objRefTypeName)); } void write_class_objrefs_definition(writer& w, TypeDef const& classType, bool replaceDefaultByInner) @@ -2067,20 +2286,34 @@ private IObjectReference Make__%() { w.write(R"(global::System.Threading.Interlocked.CompareExchange(ref __%, GetDefaultInterfaceObjRef(%), null);)", objrefname, get_class_hierarchy_index(classType)); } - else if (distance(ifaceType.GenericParam()) == 0) + else { + if (distance(ifaceType.GenericParam()) != 0) + { + auto generic_instantiation_class_name = get_generic_instantiation_class_type_name(w, ifaceType); + + generic_type_instance generic_instantiation; + generic_instantiation.generic_type = ifaceType; + for (int idx = 0; idx < distance(ifaceType.GenericParam()); idx++) + { + generic_instantiation.generic_args.push_back(w.get_generic_arg(idx)); + } + + generic_type_instances.insert( + generic_type_instantiation + { + generic_instantiation, + generic_instantiation_class_name + }); - w.write(R"(global::System.Threading.Interlocked.CompareExchange(ref __%, ((IWinRTObject)this).NativeObject.As(GuidGenerator.GetIID(typeof(%).GetHelperType())), null);)", + w.write("_ = global::WinRT.GenericTypeInstantiations.%.EnsureInitialized();\n", generic_instantiation_class_name); + } + + w.write(R"(global::System.Threading.Interlocked.CompareExchange(ref __%, ((IWinRTObject)this).NativeObject.As(%.IID), null);)", objrefname, - bind(semantics, typedef_name_type::Projected, false) + bind(semantics, typedef_name_type::StaticAbiClass, true) ); } - else - { - w.write(R"(global::System.Threading.Interlocked.CompareExchange(ref __%, (IObjectReference)typeof(IObjectReference).GetMethod("As", Type.EmptyTypes).MakeGenericMethod(typeof(%).GetHelperType().FindVftblType()).Invoke(((IWinRTObject)this).NativeObject, null), null);)", - objrefname, - bind(semantics, typedef_name_type::Projected, false)); - } w.write(R"( return __%; @@ -2100,42 +2333,17 @@ private IObjectReference % => __% ?? Make__%(); } } - void write_class_static_objrefs_definition(writer& w, std::string const& target, TypeDef const& classType) + void write_composable_constructors(writer& w, TypeDef const& composable_type, TypeDef const& class_type, std::string_view visibility) { - for (auto&& [interface_name, factory] : get_attributed_types(w, classType)) - { - if (factory.statics) - { - auto objrefname = bind(factory.type); - w.write(R"( -private static volatile IObjectReference __%; -private static IObjectReference Make__%() -{ - global::System.Threading.Interlocked.CompareExchange(ref __%, %As(GuidGenerator.GetIID(typeof(%).GetHelperType())), null); - return __%; -} -private static IObjectReference % => __% ?? Make__%(); + write_static_abi_class_raw(w, composable_type); -)", - objrefname, - objrefname, - objrefname, - target, - bind(factory.type, typedef_name_type::Projected, false), - objrefname, - objrefname, - objrefname, - objrefname); - } - } - } + write_static_objref_definition(w, composable_type, class_type); + auto cache_object = bind(composable_type); - void write_composable_constructors(writer& w, TypeDef const& composable_type, TypeDef const& class_type, std::string_view visibility) - { - auto cache_object = write_factory_cache_object(w, composable_type, class_type); auto default_interface_name = get_default_interface_name(w, class_type, false); auto default_interface_abi_name = get_default_interface_name(w, class_type); - auto default_interface_typedef = for_typedef(w, get_type_semantics(get_default_interface(class_type)), [&](auto&& iface) { return iface; }); + auto default_type_semantics = get_type_semantics(get_default_interface(class_type)); + auto default_interface_typedef = for_typedef(w, default_type_semantics, [&](auto&& iface) { return iface; }); auto is_manually_gen_default_interface = is_manually_generated_iface(default_interface_typedef); for (auto&& method : composable_type.MethodList()) @@ -2152,7 +2360,7 @@ private static IObjectReference % => __% ?? Make__%(); % %(%)% { object baseInspectable = this.GetType() != typeof(%) ? this : null; -IntPtr composed = %.%(%%baseInspectable, out IntPtr ptr); +IntPtr composed = _%.%(%, %%baseInspectable, out IntPtr ptr); using IObjectReference composedRef = ObjectReference.Attach(ref composed); try { @@ -2172,8 +2380,9 @@ MarshalInspectable.DisposeAbi(ptr); bind_list(", ", params_without_objects), has_base_type ? ":base(global::WinRT.DerivedComposed.Instance)" : "", bind(class_type, typedef_name_type::Projected, false), - cache_object, + bind(composable_type, typedef_name_type::StaticAbiClass, false), method.Name(), + cache_object, bind_list(", ", params_without_objects), [&](writer& w) {w.write("%", params_without_objects.empty() ? " " : ", "); }, default_interface_abi_name, @@ -2188,10 +2397,10 @@ MarshalInspectable.DisposeAbi(ptr); { bool isAggregation = this.GetType() != typeof(%); object baseInspectable = isAggregation ? this : null; -IntPtr composed = %.%(%%baseInspectable, out IntPtr inner); +IntPtr composed = _%.%(%, %%baseInspectable, out IntPtr inner); try { -ComWrappersHelper.Init(isAggregation, this, composed, inner, out _inner); +ComWrappersHelper.Init(isAggregation, this, composed, inner, %.IID, out _inner); % } finally @@ -2206,10 +2415,12 @@ Marshal.Release(inner); bind_list(", ", params_without_objects), has_base_type ? ":base(global::WinRT.DerivedComposed.Instance)" : "", bind(class_type, typedef_name_type::Projected, false), - cache_object, + bind(composable_type, typedef_name_type::StaticAbiClass, false), method.Name(), + cache_object, bind_list(", ", params_without_objects), [&](writer& w) {w.write("%", params_without_objects.empty() ? " " : ", "); }, + bind(default_type_semantics, typedef_name_type::StaticAbiClass, true), bind([&](writer& w) { if (is_manually_gen_default_interface) @@ -2221,7 +2432,20 @@ Marshal.Release(inner); } } - void write_static_method(writer& w, MethodDef const& method, std::string_view method_target, bool factory_class = false, std::string_view platform_attribute = ""sv) + void write_static_factory_method(writer& w, MethodDef const& method, std::string_view method_target, std::string_view platform_attribute = ""sv) + { + if (method.SpecialName()) + { + return; + } + method_signature signature{ method }; + auto return_type = w.write_temp("%", [&](writer& w) { + write_projection_return_type(w, signature); + }); + write_method(w, signature, method.Name(), return_type, method_target, "public "sv, ""sv, platform_attribute, std::nullopt); + } + + void write_static_method(writer& w, MethodDef const& method, std::string_view method_target, std::string_view platform_attribute = ""sv) { if (method.SpecialName()) { @@ -2231,38 +2455,99 @@ Marshal.Release(inner); auto return_type = w.write_temp("%", [&](writer& w) { write_projection_return_type(w, signature); }); - write_method(w, signature, method.Name(), return_type, method_target, "public "sv, factory_class ? ""sv : "static "sv, platform_attribute, settings.netstandard_compat || factory_class ? std::nullopt : std::optional(std::pair(method.Parent(), method))); + write_method(w, signature, method.Name(), return_type, method_target, "public "sv, "static "sv, platform_attribute, std::optional(std::pair(method.Parent(), method))); } - void write_static_property(writer& w, Property const& prop, std::string_view prop_target, bool factory_class = false, std::string_view platform_attribute = ""sv) + void write_static_factory_property(writer& w, Property const& prop, std::string_view prop_target, std::string_view platform_attribute = ""sv) { auto [getter, setter] = get_property_methods(prop); auto getter_target = getter ? prop_target : ""; auto setter_target = setter ? prop_target : ""; write_property(w, prop.Name(), prop.Name(), write_prop_type(w, prop), - getter_target, setter_target, "public "sv, factory_class ? ""sv : "static "sv, platform_attribute, platform_attribute, - settings.netstandard_compat || factory_class || !getter ? std::nullopt : std::optional(std::pair(prop.Parent(), prop)), - settings.netstandard_compat || factory_class || !setter ? std::nullopt : std::optional(std::pair(prop.Parent(), prop))); + getter_target, setter_target, "public "sv, ""sv, platform_attribute, platform_attribute, + std::nullopt, + std::nullopt); } - void write_static_event(writer& w, Event const& event, std::string_view event_target, bool factory_class = false, std::string_view platform_attribute = ""sv) + void write_static_factory_event(writer& w, Event const& event, std::string_view event_target, std::string_view platform_attribute = ""sv) { - write_event(w, event.Name(), event, event_target, "public "sv, factory_class ? ""sv : "static "sv, platform_attribute, - settings.netstandard_compat || factory_class ? std::nullopt : std::optional(std::tuple(event.Parent(), event, true))); + write_event(w, event.Name(), event, event_target, "public "sv, ""sv, platform_attribute, std::nullopt); } - void write_static_members(writer& w, TypeDef const& static_type, TypeDef const& class_type) + void write_static_event(writer& w, Event const& event, std::string_view event_target, std::string_view platform_attribute = ""sv) { - auto cache_object = settings.netstandard_compat ? write_static_cache_object(w, static_type.TypeName(), class_type) : ""; - auto platform_attribute = write_platform_attribute_temp(w, static_type); - w.write_each(static_type.MethodList(), cache_object, false, platform_attribute); - w.write_each(static_type.PropertyList(), cache_object, false, platform_attribute); - w.write_each(static_type.EventList(), cache_object, false, platform_attribute); + write_event(w, event.Name(), event, event_target, "public "sv, "static "sv, platform_attribute, std::optional(std::tuple(event.Parent(), event, true))); } - void write_attributed_types(writer& w, TypeDef const& type) + void write_static_members(writer& w, TypeDef const& class_type) + { + std::map>, std::optional>>> properties; + + for (auto&& [interface_name, factory] : get_attributed_types(w, class_type)) + { + if (factory.statics) + { + write_static_objref_definition(w, factory.type, class_type); + auto cache_object = w.write_temp("%", bind(factory.type)); + + auto platform_attribute = write_platform_attribute_temp(w, factory.type); + w.write_each(factory.type.MethodList(), cache_object, platform_attribute); + w.write_each(factory.type.EventList(), cache_object, platform_attribute); + + // Merge property getters/setters, since such may be defined across interfaces + for (auto&& prop : factory.type.PropertyList()) + { + auto [getter, setter] = get_property_methods(prop); + auto prop_type = write_prop_type(w, prop); + + auto [prop_targets, inserted] = properties.try_emplace(std::string(prop.Name()), + prop_type, + getter ? cache_object : "", + getter ? platform_attribute : "", + setter ? cache_object : "", + setter ? platform_attribute : "", + !getter ? std::nullopt : std::optional(std::pair(prop.Parent(), prop)), + !setter ? std::nullopt : std::optional(std::pair(prop.Parent(), prop)) + ); + if (!inserted) + { + auto& [property_type, getter_target, getter_platform, setter_target, setter_platform, getter_prop, setter_prop] = prop_targets->second; + XLANG_ASSERT(property_type == prop_type); + if (getter) + { + XLANG_ASSERT(getter_target.empty()); + getter_target = cache_object; + getter_platform = platform_attribute; + getter_prop = std::optional(std::pair(prop.Parent(), prop)); + } + if (setter) + { + XLANG_ASSERT(setter_target.empty()); + setter_target = cache_object; + setter_platform = platform_attribute; + setter_prop = std::optional(std::pair(prop.Parent(), prop)); + } + XLANG_ASSERT(!getter_target.empty() || !setter_target.empty()); + } + } + } + } + + // Write properties with merged accessors + for (auto& [prop_name, prop_data] : properties) + { + auto& [prop_type, getter_target, getter_platform, setter_target, setter_platform, getter_prop, setter_prop] = prop_data; + write_property(w, prop_name, prop_name, prop_type, + getter_target, setter_target, "public "sv, "static "sv, getter_platform, setter_platform, + getter_prop, + setter_prop); + } + } + + void write_attributed_types(writer& w, TypeDef const& type) { bool factory_written{}; + for (auto&& [interface_name, factory] : get_attributed_types(w, type)) { if (factory.activatable) @@ -2302,49 +2587,17 @@ Marshal.Release(inner); }); } - if (is_static(type)) - { - w.write(R"( -internal static %BaseActivationFactory _factory = new BaseActivationFactory("%", "%.%"); -public static %I As() => _factory.AsInterface(); -% -)", - has_base_factory ? "new " : "", - type.TypeNamespace(), - type.TypeNamespace(), - type.TypeName(), - has_base_factory ? "new " : "", - bind([&](writer& w) - { - if (!settings.netstandard_compat) - { - write_class_static_objrefs_definition(w, "_factory._", type); - } - })); - } - else - { - w.write(R"( -public static %I As() => ActivationFactory<%>.AsInterface(); -% + w.write(R"( +public static %I As() => ActivationFactory.Get("%.%").AsInterface(); )", - has_base_factory ? "new " : "", - type.TypeName(), - bind([&](writer& w) - { - if (!settings.netstandard_compat) - { - write_class_static_objrefs_definition(w, - w.write_temp("ActivationFactory<%>.", type.TypeName()), - type); - } - })); - } + has_base_factory ? "new " : "", + type.TypeNamespace(), + type.TypeName()); } - - write_static_members(w, factory.type, type); } } + + write_static_members(w, type); } void write_nongeneric_enumerable_members(writer& w, std::string_view target) @@ -2428,6 +2681,26 @@ object IEnumerator.Current => Current; visibility, element, self, target); } + void write_enumerator_members_using_static_abi_methods(writer& w, bool emit_explicit, std::string const& objref_name) + { + auto element = w.write_temp("%", bind(0)); + auto self = emit_explicit ? w.write_temp("global::System.Collections.Generic.IEnumerator<%>.", element) : ""; + auto visibility = emit_explicit ? "" : "public "; + auto abiClass = w.write_temp("global::ABI.System.Collections.Generic.IEnumeratorMethods<%>", element); + + w.write(R"( +%bool %MoveNext() => %.MoveNext(%); +%void %Reset() => %.Reset(%); +%void %Dispose() => %.Dispose(%); +%% %Current => %.get_Current(%); +object IEnumerator.Current => Current; +)", +visibility, self, abiClass, objref_name, +visibility, self, abiClass, objref_name, +visibility, self, abiClass, objref_name, +visibility, element, self, abiClass, objref_name); + } + void write_readonlydictionary_members_using_static_abi_methods(writer& w, bool emit_explicit, std::string const& objref_name) { auto key = w.write_temp("%", bind(0)); @@ -2496,28 +2769,25 @@ IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); auto enumerableObjRefName = std::regex_replace(objref_name, std::regex("IDictionary"), "IEnumerable_global__System_Collections_Generic_KeyValuePair") + "_"; w.write(R"( -private Dictionary<%, (IntPtr, %)> _lookupCache = new Dictionary<%, (IntPtr, %)>(); - %ICollection<%> %Keys => %.get_Keys(%); %ICollection<%> %Values => %.get_Values(%); %int %Count => %.get_Count(%); %bool %IsReadOnly => %.get_IsReadOnly(%); %% %this[% key] { -get => %.Indexer_Get(%, _lookupCache, key); +get => %.Indexer_Get(%, null, key); set => %.Indexer_Set(%, key, value); } %void %Add(% key, % value) => %.Add(%, key, value); %bool %ContainsKey(% key) => %.ContainsKey(%, key); %bool %Remove(% key) => %.Remove(%, key); -%bool %TryGetValue(% key, out % value) => %.TryGetValue(%, _lookupCache, key, out value); +%bool %TryGetValue(% key, out % value) => %.TryGetValue(%, null, key, out value); %void %Add(KeyValuePair<%, %> item) => %.Add(%, item); %void %Clear() => %.Clear(%); -%bool %Contains(KeyValuePair<%, %> item) => %.Contains(%, _lookupCache, item); +%bool %Contains(KeyValuePair<%, %> item) => %.Contains(%, null, item); %void %CopyTo(KeyValuePair<%, %>[] array, int arrayIndex) => %.CopyTo(%, %, array, arrayIndex); bool ICollection>.Remove(KeyValuePair<%, %> item) => %.Remove(%, item); )", -key, value, key, value, visibility, key, self, abiClass, objref_name, //Keys visibility, value, self, abiClass, objref_name, // Values visibility, icollection, abiClass, objref_name, // Count @@ -2777,7 +3047,17 @@ set => %.Indexer_Set(%, index, value); visibility, self, target); } - void write_notify_data_error_info_members(writer& w, std::string_view target, bool emit_explicit) + void write_idisposable_members_using_static_abi_methods(writer& w, bool emit_explicit, std::string objref_name) + { + auto self = emit_explicit ? "global::System.IDisposable." : ""; + auto visibility = emit_explicit ? "" : "public "; + w.write(R"( +%void %Dispose() => global::ABI.System.IDisposableMethods.Dispose(%); +)", +visibility, self, objref_name); + } + + void write_notify_data_error_info_members_using_idic(writer& w, std::string_view target, bool emit_explicit) { auto self = emit_explicit ? "global::System.ComponentModel.INotifyDataErrorInfo." : ""; auto visibility = emit_explicit ? "" : "public "; @@ -2797,6 +3077,26 @@ remove => %.ErrorsChanged -= value; visibility, self, target); } + void write_notify_data_error_info_members_using_static_abi_methods(writer& w, bool emit_explicit, std::string objref_name) + { + auto self = emit_explicit ? "global::System.ComponentModel.INotifyDataErrorInfo." : ""; + auto visibility = emit_explicit ? "" : "public "; + + w.write(R"( +%global::System.Collections.IEnumerable %GetErrors(string propertyName) => global::ABI.System.ComponentModel.INotifyDataErrorInfoMethods.GetErrors(%, propertyName); + +%event global::System.EventHandler %ErrorsChanged +{ +add => global::ABI.System.ComponentModel.INotifyDataErrorInfoMethods.Get_ErrorsChanged2(%, this).Subscribe(value); +remove => global::ABI.System.ComponentModel.INotifyDataErrorInfoMethods.Get_ErrorsChanged2(%, this).Unsubscribe(value); +} +%bool %HasErrors {get => global::ABI.System.ComponentModel.INotifyDataErrorInfoMethods.get_HasErrors(%); } +)", +visibility, self, objref_name, +visibility, self, objref_name, objref_name, +visibility, self, objref_name); + } + void write_custom_mapped_type_members(writer& w, std::string_view target, mapped_type const& mapping, bool is_private, bool call_static_abi_methods, std::string objref_name) { if (mapping.abi_name == "IIterable`1") @@ -2812,7 +3112,14 @@ remove => %.ErrorsChanged -= value; } else if (mapping.abi_name == "IIterator`1") { - write_enumerator_members(w, target, is_private); + if (call_static_abi_methods) + { + write_enumerator_members_using_static_abi_methods(w, is_private, objref_name); + } + else + { + write_enumerator_members(w, target, is_private); + } } else if (mapping.abi_name == "IMapView`2") { @@ -2869,11 +3176,25 @@ remove => %.ErrorsChanged -= value; } else if (mapping.mapped_namespace == "System" && mapping.mapped_name == "IDisposable") { - write_idisposable_members(w, target, is_private); + if (call_static_abi_methods) + { + write_idisposable_members_using_static_abi_methods(w, is_private, objref_name); + } + else + { + write_idisposable_members(w, target, is_private); + } } else if (mapping.mapped_namespace == "System.ComponentModel" && mapping.mapped_name == "INotifyDataErrorInfo") { - write_notify_data_error_info_members(w, target, is_private); + if (call_static_abi_methods) + { + write_notify_data_error_info_members_using_static_abi_methods(w, is_private, objref_name); + } + else + { + write_notify_data_error_info_members_using_idic(w, target, is_private); + } } } @@ -2909,6 +3230,20 @@ remove => %.ErrorsChanged -= value; return false; }; + std::function search_interfaces_from_attributes = [&](TypeDef const& type) + { + for (auto&& [interface_name, factory] : get_attributed_types(w, type)) + { + if (factory.statics && factory.type && (search_interface(factory.type) || search_interfaces(factory.type))) + { + return true; + } + } + + return false; + }; + + // first search base interfaces for property getter if (search_interfaces(setter_iface)) { @@ -2927,6 +3262,11 @@ remove => %.ErrorsChanged -= value; { return { getter_iface, false }; } + + if (search_interfaces_from_attributes(exclusive_to_type)) + { + return { getter_iface, false }; + } } throw_invalid("Could not find property getter interface"); @@ -2934,164 +3274,190 @@ remove => %.ErrorsChanged -= value; - void write_class_members(writer& w, TypeDef const& type, bool wrapper_type) + void write_class_members(writer& w, TypeDef const& type, bool wrapper_type, bool is_interface_impl_type) { + std::set writtenInterfaces; std::map>, std::optional>>> properties; auto fast_abi_class_val = get_fast_abi_class_for_class(type); - for (auto&& ii : type.InterfaceImpl()) + auto write_class_interface = [&](TypeDef const& interface_type, bool is_default_interface, bool is_overridable_interface, bool is_protected_interface, type_semantics semantics) { - auto semantics = get_type_semantics(ii.Interface()); - - auto write_class_interface = [&](TypeDef const& interface_type) + // When writing derived interfaces of interfaces, we can sometimes encounter duplicate interfaces. + // To prevent writing them multiple times, we catch them here. + if (writtenInterfaces.find(interface_type) != writtenInterfaces.end()) { - auto interface_name = write_type_name_temp(w, interface_type); - auto interface_abi_name = write_type_name_temp(w, interface_type, "%", typedef_name_type::ABI); + return; + } + writtenInterfaces.insert(interface_type); - auto is_default_interface = has_attribute(ii, "Windows.Foundation.Metadata", "DefaultAttribute"); - auto static_iface_target = w.write_temp("%", bind(semantics, typedef_name_type::StaticAbiClass, true)); - auto target = wrapper_type ? write_type_name_temp(w, interface_type, "((%) _comp)") : - (is_default_interface ? "_default" : write_type_name_temp(w, interface_type, "AsInternal(new InterfaceTag<%>())")); + auto interface_name = write_type_name_temp(w, interface_type); + auto interface_abi_name = write_type_name_temp(w, interface_type, "%", typedef_name_type::ABI); - auto is_fast_abi_iface = fast_abi_class_val.has_value() && is_exclusive_to(interface_type) && !settings.netstandard_compat; - auto semantics_for_abi_call = is_fast_abi_iface ? get_default_iface_as_type_sem(type) : semantics; + auto static_iface_target = w.write_temp("%", bind(semantics, typedef_name_type::StaticAbiClass, true)); + auto target = wrapper_type ? write_type_name_temp(w, interface_type, "((%) _comp)") : + (is_default_interface ? "_default" : write_type_name_temp(w, interface_type, "AsInternal(new InterfaceTag<%>())")); - if (!is_default_interface && !wrapper_type) + auto is_fast_abi_iface = fast_abi_class_val.has_value() && is_exclusive_to(interface_type) && !settings.netstandard_compat; + auto semantics_for_abi_call = is_fast_abi_iface ? get_default_iface_as_type_sem(type) : semantics; + + if (!is_default_interface && !wrapper_type) + { + if (settings.netstandard_compat || is_manually_generated_iface(interface_type)) { - if (settings.netstandard_compat || is_manually_generated_iface(interface_type)) - { - w.write(R"( + w.write(R"( private % AsInternal(InterfaceTag<%> _) => % ?? Make_%(); )", - interface_name, - interface_name, - bind(interface_type), - bind(interface_type)); - } + interface_name, + interface_name, + bind(interface_type), + bind(interface_type)); } + } + + bool call_static_method = !(settings.netstandard_compat || wrapper_type || is_manually_generated_iface(interface_type)); - bool call_static_method = !(settings.netstandard_compat || wrapper_type || is_manually_generated_iface(interface_type)); + if (auto mapping = get_mapped_type(interface_type.TypeNamespace(), interface_type.TypeName()); mapping && mapping->has_custom_members_output) + { + bool is_private = is_implemented_as_private_mapped_interface(w, type, interface_type); + auto objref_name = w.write_temp("%", bind(semantics)); + write_custom_mapped_type_members(w, target, *mapping, is_private, call_static_method, objref_name); + return; + } - if (auto mapping = get_mapped_type(interface_type.TypeNamespace(), interface_type.TypeName()); mapping && mapping->has_custom_members_output) + auto platform_attribute = write_platform_attribute_temp(w, interface_type); + + w.write_each(interface_type.MethodList(), type, is_overridable_interface, is_protected_interface, target, platform_attribute, call_static_method ? std::optional(semantics_for_abi_call) : std::nullopt); + w.write_each(interface_type.EventList(), type, is_overridable_interface, is_protected_interface, target, platform_attribute, call_static_method ? std::optional(semantics_for_abi_call) : std::nullopt); + + // Merge property getters/setters, since such may be defined across interfaces + for (auto&& prop : interface_type.PropertyList()) + { + MethodDef getter, setter; + std::tie(getter, setter) = get_property_methods(prop); + auto prop_type = write_prop_type(w, prop); + auto is_private = getter && is_implemented_as_private_method(w, type, getter); // for explicitly implemented interfaces, assume there is always a get. + auto property_name = is_private ? w.write_temp("%.%", interface_name, prop.Name()) : std::string(prop.Name()); + auto [prop_targets, inserted] = properties.try_emplace(property_name, + prop_type, + getter ? target : "", + getter ? platform_attribute : "", + setter ? target : "", + setter ? platform_attribute : "", + is_overridable_interface, + !is_protected_interface && !is_overridable_interface, // By default, an overridable member is protected. + is_private, + call_static_method && getter ? std::optional(std::pair(semantics_for_abi_call, prop)) : std::nullopt, + call_static_method && setter ? std::optional(std::pair(semantics_for_abi_call, prop)) : std::nullopt + ); + if (!inserted) { - bool is_private = is_implemented_as_private_mapped_interface(w, type, interface_type); - auto objref_name = w.write_temp("%", bind(semantics)); - write_custom_mapped_type_members(w, target, *mapping, is_private, call_static_method, objref_name); - return; + auto& [property_type, getter_target, getter_platform, setter_target, setter_platform, is_overridable, is_public, _, getter_prop, setter_prop] = prop_targets->second; + XLANG_ASSERT(property_type == prop_type); + if (getter) + { + XLANG_ASSERT(getter_target.empty()); + getter_target = target; + getter_platform = platform_attribute; + getter_prop = call_static_method ? std::optional(std::pair(semantics_for_abi_call, prop)) : std::nullopt; + } + if (setter) + { + XLANG_ASSERT(setter_target.empty()); + setter_target = target; + setter_platform = platform_attribute; + setter_prop = call_static_method ? std::optional(std::pair(semantics_for_abi_call, prop)) : std::nullopt; + } + is_overridable |= is_overridable_interface; + is_public |= !is_overridable_interface && !is_protected_interface; + XLANG_ASSERT(!getter_target.empty() || !setter_target.empty()); } - - auto is_overridable_interface = has_attribute(ii, "Windows.Foundation.Metadata", "OverridableAttribute"); - auto is_protected_interface = has_attribute(ii, "Windows.Foundation.Metadata", "ProtectedAttribute"); - - auto platform_attribute = write_platform_attribute_temp(w, interface_type); - - w.write_each(interface_type.MethodList(), type, is_overridable_interface, is_protected_interface, target, platform_attribute, call_static_method ? std::optional(semantics_for_abi_call) : std::nullopt); - w.write_each(interface_type.EventList(), type, is_overridable_interface, is_protected_interface, target, platform_attribute, call_static_method ? std::optional(semantics_for_abi_call) : std::nullopt); - - // Merge property getters/setters, since such may be defined across interfaces - for (auto&& prop : interface_type.PropertyList()) + // If this interface is overridable or private then we need to emit an explicit implementation of the property for that interface. + if (is_overridable_interface || is_private) { - MethodDef getter, setter; - std::tie(getter, setter) = get_property_methods(prop); - auto prop_type = write_prop_type(w, prop); - auto is_private = getter && is_implemented_as_private_method(w, type, getter); // for explicitly implemented interfaces, assume there is always a get. - auto property_name = is_private ? w.write_temp("%.%", interface_name, prop.Name()) : std::string(prop.Name()); - auto [prop_targets, inserted] = properties.try_emplace(property_name, + w.write("\n%% %.% {%%}", + platform_attribute, prop_type, - getter ? target : "", - getter ? platform_attribute : "", - setter ? target : "", - setter ? platform_attribute : "", - is_overridable_interface, - !is_protected_interface && !is_overridable_interface, // By default, an overridable member is protected. - is_private, - call_static_method && getter ? std::optional(std::pair(semantics_for_abi_call, prop)) : std::nullopt, - call_static_method && setter ? std::optional(std::pair(semantics_for_abi_call, prop)) : std::nullopt - ); - if (!inserted) - { - auto& [property_type, getter_target, getter_platform, setter_target, setter_platform, is_overridable, is_public, _, getter_prop, setter_prop] = prop_targets->second; - XLANG_ASSERT(property_type == prop_type); - if (getter) - { - XLANG_ASSERT(getter_target.empty()); - getter_target = target; - getter_platform = platform_attribute; - getter_prop = call_static_method ? std::optional(std::pair(semantics_for_abi_call, prop)) : std::nullopt; - } - if (setter) + bind(interface_type, typedef_name_type::CCW, false), + prop.Name(), + bind([&](writer& w) { - XLANG_ASSERT(setter_target.empty()); - setter_target = target; - setter_platform = platform_attribute; - setter_prop = call_static_method ? std::optional(std::pair(semantics_for_abi_call, prop)) : std::nullopt; - } - is_overridable |= is_overridable_interface; - is_public |= !is_overridable_interface && !is_protected_interface; - XLANG_ASSERT(!getter_target.empty() || !setter_target.empty()); - } - // If this interface is overridable then we need to emit an explicit implementation of the property for that interface. - if (is_overridable_interface || !is_exclusive_to(interface_type)) - { - w.write("\n%% %.% {%%}", - platform_attribute, - prop_type, - bind(interface_type, typedef_name_type::CCW, false), - prop.Name(), - bind([&](writer& w) - { - bool base_getter{}; - std::string base_getter_platform_attribute{}; - TypeDef getter_property_iface; - if (!getter) - { - auto property_interface = find_property_interface(w, interface_type, prop.Name()); - base_getter = property_interface.second; - getter_property_iface = property_interface.first; - base_getter_platform_attribute = write_platform_attribute_temp(w, property_interface.first); + bool base_getter{}; + std::string base_getter_platform_attribute{}; + TypeDef getter_property_iface; + if (!getter) + { + auto property_interface = find_property_interface(w, interface_type, prop.Name()); + base_getter = property_interface.second; + getter_property_iface = property_interface.first; + base_getter_platform_attribute = write_platform_attribute_temp(w, property_interface.first); + } + if (getter || base_getter) + { + w.write("%get => %; ", base_getter_platform_attribute, bind([&](writer& w) { + if (call_static_method) + { + auto iface = base_getter ? getter_property_iface : prop.Parent(); + w.write("%", bind(iface, prop, + w.write_temp("%", bind(iface)))); } - if (getter || base_getter) + else { - w.write("%get => %; ", base_getter_platform_attribute, bind([&](writer& w) { - if (call_static_method) - { - auto iface = base_getter ? getter_property_iface : prop.Parent(); - w.write("%", bind(iface, prop, - w.write_temp("%", bind(iface)))); - } - else - { - w.write("%%", is_private ? target + "." : "", prop.Name()); - } - })); + w.write("%%", is_private ? target + "." : "", prop.Name()); } - }), - bind([&](writer& w) - { - if (setter) + })); + } + }), + bind([&](writer& w) + { + if (setter) + { + w.write("set => %;", bind([&](writer& w) { + if (call_static_method) { - w.write("set => %;", bind([&](writer& w) { - if (call_static_method) - { - w.write("%", bind(prop.Parent(), prop, - w.write_temp("%", bind(prop.Parent())))); - } - else - { - w.write("%% = value", is_private ? target + "." : "", prop.Name()); - } - })); + w.write("%", bind(prop.Parent(), prop, + w.write_temp("%", bind(prop.Parent())))); } - })); - } + else + { + w.write("%% = value", is_private ? target + "." : "", prop.Name()); + } + })); + } + })); } - }; - for_typedef(w, semantics, [&](auto type) + } + }; + + if (is_interface_impl_type) + { + write_class_interface(type, false, false, false, type); + } + + std::function write_class_interfaces = [&](TypeDef const& type) + { + for (auto&& ii : type.InterfaceImpl()) + { + auto is_default_interface = has_attribute(ii, "Windows.Foundation.Metadata", "DefaultAttribute"); + auto is_overridable_interface = has_attribute(ii, "Windows.Foundation.Metadata", "OverridableAttribute"); + auto is_protected_interface = has_attribute(ii, "Windows.Foundation.Metadata", "ProtectedAttribute"); + auto semantics = get_type_semantics(ii.Interface()); + + for_typedef(w, semantics, [&](auto&& type) { - write_class_interface(type); + write_class_interface(type, is_default_interface, is_overridable_interface, is_protected_interface, is_interface_impl_type ? type : semantics); + if (is_interface_impl_type) + { + write_class_interfaces(type); + } }); - } + } + }; + + for_typedef(w, type, [&](auto type) + { + write_class_interfaces(type); + }); // Write properties with merged accessors for (auto& [prop_name, prop_data] : properties) @@ -3100,63 +3466,296 @@ private % AsInternal(InterfaceTag<%> _) => % ?? Make_%(); if (is_private) continue; std::string_view access_spec = is_public ? "public "sv : "protected "sv; std::string_view method_spec = is_overridable ? "virtual "sv : ""sv; - write_property(w, prop_name, prop_name, prop_type, getter_target, setter_target, access_spec, method_spec, getter_platform, setter_platform, getter_prop, setter_prop); + write_property(w, prop_name, prop_name, prop_type, + getter_prop.has_value() ? w.write_temp("%", bind(getter_prop.value().first)) : getter_target, + setter_prop.has_value() ? w.write_temp("%", bind(setter_prop.value().first)) : setter_target, + access_spec, method_spec, getter_platform, setter_platform, getter_prop, setter_prop); } } - void write_winrt_attribute(writer& w, TypeDef const& type) - { - std::filesystem::path db_path(type.get_database().path()); - w.write(R"([global::WinRT.WindowsRuntimeType("%")])", -db_path.stem().string()); - } - - void write_winrt_helper_type_attribute(writer& w, TypeDef const& type) + void write_guid_signature(writer& w, type_semantics const& semantics) { - if (get_category(type) == category::struct_type && is_type_blittable(type)) - { - w.write(R"([global::WinRT.WindowsRuntimeHelperType])"); - return; - } - - w.write(R"([global::WinRT.WindowsRuntimeHelperType(typeof(%%))])", - bind(type, typedef_name_type::ABI, false), - bind([&](writer& w) + call(semantics, + [&](guid_type) { - if (distance(type.GenericParam()) == 0) - { - return; - } - - // Writes out the generic definition without the types. + w.write("g16"); + }, + [&](object_type) + { + w.write("cinterface(IInspectable)"); + }, + [&](type_definition const& type) + { + switch (get_category(type)) + { + case category::enum_type: + { + w.write("enum(%;%)", + bind(type, typedef_name_type::NonProjected, true), + is_flags_enum(type) ? "u4" : "i4"); + break; + } + case category::struct_type: + { + w.write("struct(%;%)", + bind(type, typedef_name_type::NonProjected, true), + bind_list([](writer& w, Field const& field) + { + write_guid_signature(w, get_type_semantics(field.Signature().Type())); + }, ";", type.FieldList()) + ); + break; + } + case category::delegate_type: + { + w.write("delegate({%})", bind(type, true)); + break; + } + case category::interface_type: + { + w.write("{%}", bind(type, true)); + break; + } + case category::class_type: + { + if (auto default_interface = get_default_interface(type)) + { + w.write("rc(%;%)", + bind(type, typedef_name_type::NonProjected, true), + bind(get_type_semantics(default_interface))); + } + else + { + w.write("{%}", bind(type, true)); + } + break; + } + } + }, + [&](generic_type_instance const& type) + { + w.write("pinterface({%};%)", + bind(type.generic_type, true), + bind_list([](writer& w, type_semantics const& genericType) + { + write_guid_signature(w, genericType); + }, ";", type.generic_args) + ); + }, + [&](fundamental_type const& type) + { + w.write("%", get_fundamental_type_guid_signature(type)); + }, + [&](auto const&) {}); + } + + void write_winrt_attribute(writer& w, TypeDef const& type) + { + std::filesystem::path db_path(type.get_database().path()); + if (get_category(type) == category::struct_type) + { + w.write(R"([global::WinRT.WindowsRuntimeType("%", "%")])", + db_path.stem().string(), + bind(type)); + } + else + { + w.write(R"([global::WinRT.WindowsRuntimeType("%")])", + db_path.stem().string()); + } + } + + void write_winrt_helper_type_attribute(writer& w, TypeDef const& type) + { + if (get_category(type) == category::struct_type && is_type_blittable(type)) + { + w.write(R"([global::WinRT.WindowsRuntimeHelperType])"); + return; + } + + w.write(R"([global::WinRT.WindowsRuntimeHelperType(typeof(%%))])", + bind(type, typedef_name_type::ABI, false), + bind([&](writer& w) + { + if (distance(type.GenericParam()) == 0) + { + return; + } + + // Writes out the generic definition without the types. separator s{ w }; w.write("<%>", bind_each([&](writer& /*w*/, GenericParam const& /*gp*/){ s(); }, type.GenericParam())); })); } - auto get_invoke_info(writer& w, MethodDef const& method, uint32_t const& abi_methods_start_index = INSPECTABLE_METHOD_COUNT) + // The WinRTExposedType attribute decides the interfaces that are + // placed on the vtable during CCW generation. + // + // Given structs and delegates can be boxed and passed as an object, + // we write this attribute to represent the interfaces they implement. + // + // For enums, they can also be boxed, but there are only two types of + // enums (int and uint) allowing us to handle this directly during CCW + // generation. + // + // For projection classes, they are typically just unwrapped to get the + // native object when passed across the ABI. The exception to this is + // unsealed classes which are extended by consumers in C# code. + // For these classes, we generate the attribute using the source generator + // and put it on the class extending it and also includes the + // override interfaces from the unsealed base class. + // + // For classes authored using our component authoring support, we + // generate the attribute on the Impl namespace metadata classes using the + // source generator rather than here as it allows us to better handle + // covariant interfaces. But for factory classes, we generate them here. + bool should_write_winrt_exposed_type_attribute(TypeDef const& type, bool isFactory) + { + if (settings.netstandard_compat) + { + return false; + } + + return isFactory || + get_category(type) == category::struct_type || + get_category(type) == category::enum_type || + (get_category(type) == category::delegate_type && distance(type.GenericParam()) == 0); + } + + void write_winrt_exposed_type_attribute(writer& w, TypeDef const& type, bool isFactory) + { + if (should_write_winrt_exposed_type_attribute(type, isFactory)) + { + if (get_category(type) == category::struct_type) + { + w.write(R"([global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails<%, %>))])", + bind(type, typedef_name_type::Projected, false), + bind(type, is_type_blittable(type) ? typedef_name_type::Projected : typedef_name_type::ABI, false)); + } + else if (get_category(type) == category::enum_type) + { + w.write(R"([global::WinRT.WinRTExposedType(typeof(global::WinRT.EnumTypeDetails<%>))])", + bind(type, typedef_name_type::Projected, false)); + } + else + { + w.write(R"([global::WinRT.WinRTExposedType(typeof(%%WinRTTypeDetails))])", + bind(type, typedef_name_type::ABI, false), + isFactory ? "ServerActivationFactory" : ""); + } + } + } + + void write_winrt_exposed_type_class(writer& w, TypeDef const& type, bool isFactory) + { + if (should_write_winrt_exposed_type_attribute(type, isFactory)) + { + if (get_category(type) == category::class_type && isFactory) + { + w.write(R"( +internal sealed class %ServerActivationFactoryWinRTTypeDetails : global::WinRT.IWinRTExposedTypeDetails +{ +public global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() +{ +return new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry[] +{ +% +}; +} +} +)", + bind(type, typedef_name_type::ABI, false), + bind([&](writer& w) + { + w.write(R"( +new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry +{ +IID = global::ABI.WinRT.Interop.IActivationFactoryMethods.IID, +Vtable = global::ABI.WinRT.Interop.IActivationFactoryMethods.AbiToProjectionVftablePtr +}, +)"); + + for (auto&& [interface_name, factory] : get_attributed_types(w, type)) + { + if ((factory.activatable || factory.statics) && factory.type) + { + w.write(R"( +new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry +{ +IID = %.IID, +Vtable = %.AbiToProjectionVftablePtr +}, +)", + bind(factory.type, typedef_name_type::StaticAbiClass, false), + // These are exclusive internal interfaces, so just use the ABI class to get the ptr. + bind(factory.type, typedef_name_type::ABI, false) + ); + } + } + }) + ); + } + else if (get_category(type) == category::delegate_type) + { + w.write(R"( +internal sealed class %WinRTTypeDetails : global::WinRT.DelegateTypeDetails<%> +{ +public override ComWrappers.ComInterfaceEntry GetDelegateInterface() +{ +return new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry +{ +IID = %.IID, +Vtable = %.AbiToProjectionVftablePtr +}; +} +} +)", + bind(type, typedef_name_type::ABI, false), + bind(type, typedef_name_type::Projected, false), + bind(type, typedef_name_type::ABI, false), + bind(type, typedef_name_type::ABI, false) + ); + } + } + } + + auto get_invoke_info(writer& w, MethodDef const& method, uint32_t const& abi_methods_start_index = INSPECTABLE_METHOD_COUNT, bool use_void_pointer_return = false, bool is_generic_instantiation_class = false) { TypeDef const& type = method.Parent(); - if (!settings.netstandard_compat && distance(type.GenericParam()) == 0) + bool signature_has_generic_parameters = abi_signature_has_generic_parameters(w, method_signature{ method }); + if (!settings.netstandard_compat) { - return std::pair{ - w.write_temp("(*(delegate* unmanaged[Stdcall]<%, int>**)ThisPtr)[%]", - bind(method_signature { method }), - get_vmethod_index(type, method) + abi_methods_start_index /* number of methods in IInspectable + previous methods if fastabi*/), - false - }; + if (!signature_has_generic_parameters || is_generic_instantiation_class) + { + return std::pair{ + w.write_temp("(*(delegate* unmanaged[Stdcall]<%, int>**)ThisPtr)[%]", + bind(method_signature { method }), + get_vmethod_index(type, method) + abi_methods_start_index /* number of methods in IInspectable + previous methods if fastabi*/), + false + }; + } + else if (use_void_pointer_return) + { + return std::pair{ + w.write_temp("(*(delegate* unmanaged[Stdcall]<%, void*, int>**)ThisPtr)[%]", + bind(method_signature { method }), + get_vmethod_index(type, method) + abi_methods_start_index /* number of methods in IInspectable + previous methods if fastabi*/), + false + }; + } } auto vmethod_name = get_vmethod_name(w, type, method); return std::pair{ "_obj.Vftbl." + vmethod_name, - abi_signature_has_generic_parameters(w, method_signature { method })}; + signature_has_generic_parameters }; }; void write_static_class(writer& w, TypeDef const& type) { w.write(R"(%%% static class % { -%})", +%} +)", bind(type), bind(type, true), internal_accessibility(), @@ -3165,23 +3764,76 @@ db_path.stem().string()); ); } + // Checks if any of the generic args in the concrete type is an non instantiated type. + // i.e. It is in an generic interface and uses the generic to define the type. + bool has_generic_param_in_concrete_type(generic_type_instance const& type) + { + for (size_t idx = 0; idx < type.generic_args.size(); idx++) + { + auto& generic_arg_semantic = type.generic_args[idx]; + if (auto gtp = std::get_if(&generic_arg_semantic)) + { + return true; + } + } + + return false; + } + + void write_ensure_generic_type_initialized_for_instance(writer& w, generic_type_instance const& instance, bool assign_to_variable = false) + { + if (settings.netstandard_compat) + { + return; + } + + auto concrete_generic_type = ConvertGenericTypeInstanceToConcreteType(w, instance); + if (has_generic_param_in_concrete_type(concrete_generic_type)) + { + return; + } + + auto guard{ w.push_generic_args(concrete_generic_type) }; + auto generic_instantiation_class_name = get_generic_instantiation_class_type_name(w, concrete_generic_type.generic_type); + generic_type_instances.insert( + generic_type_instantiation + { + concrete_generic_type, + generic_instantiation_class_name + }); + + if (assign_to_variable) + { + w.write("private static readonly bool initialized = global::WinRT.GenericTypeInstantiations.%.EnsureInitialized();", generic_instantiation_class_name); + } + else + { + w.write("_ = global::WinRT.GenericTypeInstantiations.%.EnsureInitialized();", generic_instantiation_class_name); + } + } + + void write_ensure_generic_type_initialized(writer& w, cswinrt::type_semantics const& semantics, bool assign_to_variable = false) + { + call(semantics, + [&](generic_type_instance const& instance) + { + write_ensure_generic_type_initialized_for_instance(w, instance, assign_to_variable); + }, + [&](auto const&) {}); + } + void write_event_source_generic_args(writer& w, cswinrt::type_semantics eventTypeSemantics); - void write_event_source_ctor(writer& w, Event const& evt, int index, uint32_t const& abi_methods_start_index) + void write_event_source_ctor(writer& w, Event const& evt, uint32_t const& abi_methods_start_index = 6) { if (for_typedef(w, get_type_semantics(evt.EventType()), [&](TypeDef const& eventType) { if ((eventType.TypeNamespace() == "Windows.Foundation" || eventType.TypeNamespace() == "System") && eventType.TypeName() == "EventHandler`1") { auto [add, remove] = get_event_methods(evt); - w.write(R"( new EventSource__EventHandler%(_obj, -%, -%, -%))", + w.write(R"( new global::ABI.WinRT.Interop.EventHandlerEventSource%(_obj, %))", bind(eventType), -get_invoke_info(w, add, abi_methods_start_index).first, -get_invoke_info(w, remove, abi_methods_start_index).first, -index); +get_vmethod_index(add.Parent(), add) + abi_methods_start_index); return true; } return false; @@ -3192,15 +3844,10 @@ index); auto [add, remove] = get_event_methods(evt); w.write(R"( -new %%(_obj, -%, -%, -%))", +new %%(_obj, %))", bind(get_type_semantics(evt.EventType())), bind(get_type_semantics(evt.EventType())), - get_invoke_info(w, add, abi_methods_start_index).first, - get_invoke_info(w, remove, abi_methods_start_index).first, - index); + get_vmethod_index(add.Parent(), add) + abi_methods_start_index); } void write_event_sources(writer& w, TypeDef const& type) @@ -3208,7 +3855,7 @@ new %%(_obj, for (auto&& evt : type.EventList()) { w.write(R"( -private EventSource<%> _%;)", +private global::ABI.WinRT.Interop.EventSource<%> _%;)", bind(get_type_semantics(evt.EventType()), typedef_name_type::Projected, false), evt.Name()); } @@ -3217,18 +3864,35 @@ evt.Name()); void write_event_source_table(writer& w, Event const& evt) { w.write(R"( -private volatile static global::System.Runtime.CompilerServices.ConditionalWeakTable> _%_; -private static global::System.Runtime.CompilerServices.ConditionalWeakTable> Make%Table() +private volatile static global::System.Runtime.CompilerServices.ConditionalWeakTable> _%_; +private static global::System.Runtime.CompilerServices.ConditionalWeakTable> Make%Table() { + % global::System.Threading.Interlocked.CompareExchange(ref _%_, new(), null); return _%_; } -private static global::System.Runtime.CompilerServices.ConditionalWeakTable> _% => _%_ ?? Make%Table(); +private static global::System.Runtime.CompilerServices.ConditionalWeakTable> _% => _%_ ?? Make%Table(); )", bind(get_type_semantics(evt.EventType()), typedef_name_type::Projected, false), evt.Name(), bind(get_type_semantics(evt.EventType()), typedef_name_type::Projected, false), evt.Name(), + bind([&](writer& w) + { + call(get_type_semantics(evt.EventType()), + [&](generic_type_instance const& instance) + { + // EventHandler`1 types don't get their unique EventSource class as they share them based on generic. + // Due to this, we perform the initialization of the instantiation class here. + // Others will perform them in their event source class. + if ((instance.generic_type.TypeNamespace() == "Windows.Foundation" || instance.generic_type.TypeNamespace() == "System") && + instance.generic_type.TypeName() == "EventHandler`1") + { + write_ensure_generic_type_initialized_for_instance(w, instance, false); + } + }, + [&](auto const&) {}); + }), evt.Name(), evt.Name(), bind(get_type_semantics(evt.EventType()), typedef_name_type::Projected, false), @@ -3292,6 +3956,10 @@ event % %;)", bool is_value_type; bool is_pinnable; bool marshal_by_object_reference_value; + bool has_generic_instantiation; + std::vector generic_instantiations; + std::string interface_guid; + std::string interface_init_rcw_helper; bool is_out() const { @@ -3320,6 +3988,15 @@ event % %;)", marshaler_type.empty() && local_type == "IntPtr"; } + // We pass using in for .NET Standard. Outside of .NET Standard, + // we want our function pointers to be blittable and be able to disable + // runtime marshaling, so we use ptrs with the managed function calling the + // function pointer marking the parameter as in. + bool is_const_ref() const + { + return !settings.netstandard_compat && category == param_category::ref; + } + bool is_marshal_by_object_reference_value() const { return marshal_by_object_reference_value; @@ -3395,16 +4072,40 @@ event % %;)", if (is_pinnable || is_object_in() || is_out() || local_type.empty()) return; - w.write("% = %.CreateMarshaler%%(%);\n", + if (!settings.netstandard_compat && + has_generic_instantiation) + { + for (auto&& generic_instantiation : generic_instantiations) + { + auto guard{ w.push_generic_args(generic_instantiation) }; + auto generic_instantiation_class_name = get_generic_instantiation_class_type_name(w, generic_instantiation.generic_type); + if (get_category(generic_instantiation.generic_type) == category::delegate_type) + { + generic_type_instances.insert( + generic_type_instantiation + { + generic_instantiation, + generic_instantiation_class_name + }); + + w.write("_ = global::WinRT.GenericTypeInstantiations.%.EnsureInitialized();\n", generic_instantiation_class_name); + } + } + } + + w.write("% = %.CreateMarshaler%%(%%%);\n", get_marshaler_local(w), marshaler_type, is_array() ? "Array" : "", is_marshal_by_object_reference_value() ? "2" : "", - bind(param_name)); + bind(param_name), + interface_guid != "" ? ", " : "", + interface_guid); - if (is_generic() || is_array()) + if (is_generic() || is_array() || (is_const_ref() && !marshaler_type.empty())) { - w.write("% = %.GetAbi%(%);\n", + w.write("%% = %.GetAbi%(%);\n", + is_const_ref() && !marshaler_type.empty() ? "var __" : "", get_param_local(w), is_marshal_by_object_reference_value() ? "MarshalInspectable" : marshaler_type, is_array() ? "Array" : "", @@ -3440,14 +4141,14 @@ event % %;)", if (is_array()) { w.write("%__%_length, %__%_data", - is_out() ? "out " : "", param_name, - is_out() ? "out " : "", param_name); + is_out() ? (settings.netstandard_compat ? "out " : "&") : "", param_name, + is_out() ? (settings.netstandard_compat ? "out " : "&") : "", param_name); return; } if (is_out()) { - w.write("%__%", "out ", param_name); + w.write("%__%", (settings.netstandard_compat ? "out " : "&"), param_name); return; } @@ -3471,7 +4172,8 @@ event % %;)", source, bind(param_name)); return; } - w.write("%%", + w.write("%%%", + !settings.netstandard_compat && category == param_category::ref ? "_" : "", source, bind(param_name)); return; } @@ -3491,6 +4193,12 @@ event % %;)", return; } + if (is_const_ref()) + { + w.write("&____%", param_name); + return; + } + w.write("%.GetAbi%(%%)", is_marshal_by_object_reference_value() ? "MarshalInspectable" : marshaler_type, is_array() ? "Array" : "", @@ -3578,6 +4286,37 @@ event % %;)", } return; } + + if (!settings.netstandard_compat && + has_generic_instantiation) + { + // If we have an InitRcwHelper call for the RCW impl class, we leave it to that to instantiate the generic interfaces + // instead of doing them here. + if (interface_init_rcw_helper != "") + { + w.write(interface_init_rcw_helper); + } + else + { + for (auto&& generic_instantiation : generic_instantiations) + { + auto guard{ w.push_generic_args(generic_instantiation) }; + auto generic_instantiation_class_name = get_generic_instantiation_class_type_name(w, generic_instantiation.generic_type); + if (!starts_with(generic_instantiation_class_name, "Windows_Foundation_IReference")) + { + generic_type_instances.insert( + generic_type_instantiation + { + generic_instantiation, + generic_instantiation_class_name + }); + + w.write("_ = global::WinRT.GenericTypeInstantiations.%.EnsureInitialized();\n", generic_instantiation_class_name); + } + } + } + } + is_return ? w.write("return ") : w.write("% = ", bind(param_name)); @@ -3616,7 +4355,7 @@ event % %;)", } }; - void set_abi_marshaler(writer& w, TypeSig const& type_sig, abi_marshaler& m, std::string_view prop_name = "") + void set_abi_marshaler(writer& w, TypeSig const& type_sig, abi_marshaler& m, std::string_view prop_name = "", bool is_generic_instantiation_class = false) { auto semantics = get_type_semantics(type_sig); m.param_type = w.write_temp("%", bind(semantics)); @@ -3673,6 +4412,40 @@ event % %;)", { m.marshal_by_object_reference_value = true; m.local_type = m.is_out() ? "IntPtr" : "ObjectReferenceValue"; + if (settings.netstandard_compat) + { + m.interface_guid = w.write_temp("GuidGenerator.GetIID(typeof(%).GetHelperType())", bind(semantics, typedef_name_type::Projected, false)); + } + else if (type.TypeNamespace() == "Windows.Foundation" && type.TypeName() == "IReference`1") + { + m.interface_guid = w.write_temp("%.PIID", bind(semantics, typedef_name_type::ABI, false)); + } + else + { + m.interface_guid = w.write_temp("%.IID", bind(type, typedef_name_type::StaticAbiClass, true)); + } + } + + // Make sure this isn't being called for a generic instance + // that was already processed. + if (!m.has_generic_instantiation) + { + for (auto&& iface : type.InterfaceImpl()) + { + auto ifaceSemantics = get_type_semantics(iface.Interface()); + call(ifaceSemantics, + [&](generic_type_instance const& generic) + { + m.has_generic_instantiation = true; + m.generic_instantiations.emplace_back(generic); + }, + [&](auto) { }); + } + + if (has_derived_generic_interface(type)) + { + m.interface_init_rcw_helper = w.write_temp("%.InitRcwHelper();\n", bind(type, typedef_name_type::StaticAbiClass, true)); + } } break; case category::class_type: @@ -3702,53 +4475,111 @@ event % %;)", } }; - call(semantics, - [&](object_type) - { - m.marshaler_type = "MarshalInspectable"; - if (m.is_array()) - { - m.local_type = "MarshalInterfaceHelper.MarshalerArray"; - } - else - { - m.marshal_by_object_reference_value = true; - m.local_type = m.is_out() ? "IntPtr" : "ObjectReferenceValue"; - } - }, - [&](type_definition const& type) - { - set_typedef_marshaler(m, type); - }, - [&](generic_type_index const& /*var*/) - { - m.param_type = w.write_temp("%", bind(semantics)); - m.marshaler_type = w.write_temp("Marshaler<%>", m.param_type); - m.local_type = "object"; - }, - [&](generic_type_instance const& type) - { - auto guard{ w.push_generic_args(type) }; - set_typedef_marshaler(m, type.generic_type); - }, - [&](fundamental_type type) - { - if (type == fundamental_type::String) + std::function set_type_semantics_marshaler = [&]() + { + call(semantics, + [&](object_type) { + m.marshaler_type = "MarshalInspectable"; if (m.is_array()) { - m.marshaler_type = "MarshalString"; - m.local_type = "MarshalString.MarshalerArray"; + m.local_type = "MarshalInterfaceHelper.MarshalerArray"; } else { - m.marshaler_type = "MarshalString"; - m.local_type = m.is_out() ? "IntPtr" : "MarshalString"; - m.is_pinnable = (m.category == param_category::in); + m.marshal_by_object_reference_value = true; + m.local_type = m.is_out() ? "IntPtr" : "ObjectReferenceValue"; } - } - }, - [&](auto const&) {}); + }, + [&](type_definition const& type) + { + set_typedef_marshaler(m, type); + }, + [&](generic_type_index const& var) + { + if (is_generic_instantiation_class) + { + semantics = w.get_generic_arg(var.index); + set_type_semantics_marshaler(); + } + else + { + m.param_type = w.write_temp("%", bind(semantics)); + m.marshaler_type = w.write_temp("Marshaler<%>", m.param_type); + // In our non netstandard projection, this should only occur in our generic instantiated + // static method classes which takes the ABI type as a generic. + m.local_type = !settings.netstandard_compat && m.is_out() ? w.write_temp("%Abi", m.param_type) : "object"; + } + }, + [&](generic_type_instance const& type) + { + auto type_name = w.write_temp("%", bind(semantics)); + + bool is_generic_type_param = false; + auto generic_instantiation = type; + // If this is a generic instantiation class for which we are writing the marshaler for, then + // the generics we have in type aren't going to be the actual generic types but index references + // to them in a separate vector in the writer. Due to that, we replace the generic indexes + // with the actual types so that we can use them later outside of this context. + if (is_generic_instantiation_class) + { + for (size_t idx = 0; idx < type.generic_args.size(); idx++) + { + auto& generic_arg_semantic = type.generic_args[idx]; + if (auto gti = std::get_if(&generic_arg_semantic)) + { + generic_instantiation.generic_args[idx] = w.get_generic_arg_scope(gti->index).first; + } + } + } + else + { + // Make sure we are not including declarations of generic interfaces themselves + // but rather when they are instantiated. + for (size_t idx = 0; idx < type.generic_args.size(); idx++) + { + auto& generic_arg_semantic = type.generic_args[idx]; + if (auto gti = std::get_if(&generic_arg_semantic)) + { + auto scope_semantics = w.get_generic_arg_scope(gti->index).first; + if (auto gtp = std::get_if(&scope_semantics)) + { + is_generic_type_param = true; + break; + } + } + } + } + + if (!is_generic_type_param) + { + m.generic_instantiations.emplace_back(generic_instantiation); + m.has_generic_instantiation = true; + } + + auto guard{ w.push_generic_args(type) }; + set_typedef_marshaler(m, type.generic_type); + }, + [&](fundamental_type type) + { + if (type == fundamental_type::String) + { + if (m.is_array()) + { + m.marshaler_type = "MarshalString"; + m.local_type = "MarshalString.MarshalerArray"; + } + else + { + m.marshaler_type = "MarshalString"; + m.local_type = m.is_out() ? "IntPtr" : "MarshalString"; + m.is_pinnable = (m.category == param_category::in); + } + } + }, + [&](auto const&) {}); + }; + set_type_semantics_marshaler(); if (m.is_out() && m.local_type.empty()) { @@ -3771,7 +4602,7 @@ event % %;)", } } - auto get_abi_marshalers(writer& w, method_signature const& signature, bool is_generic, std::string_view prop_name = "", bool raw_return_type = false) + auto get_abi_marshalers(writer& w, method_signature const& signature, bool is_generic, std::string_view prop_name = "", bool raw_return_type = false, bool is_generic_instantiation_class = false) { std::vector marshalers; int param_index = 1; @@ -3784,22 +4615,22 @@ event % %;)", get_param_category(param) }; param_index += m.is_array() ? 2 : 1; - set_abi_marshaler(w, param.second->Type(), m, prop_name); + set_abi_marshaler(w, param.second->Type(), m, prop_name, is_generic_instantiation_class); marshalers.push_back(std::move(m)); } if (auto ret = signature.return_signature()) { abi_marshaler m{ - "retval", - is_generic ? param_index : -1, - ret.Type().is_szarray() && !raw_return_type ? param_category::receive_array : param_category::out, - true + "retval", + is_generic ? param_index : -1, + ret.Type().is_szarray() && !raw_return_type ? param_category::receive_array : param_category::out, + true }; param_index += m.is_array() ? 2 : 1; if (!raw_return_type) { - set_abi_marshaler(w, ret.Type(), m, prop_name); + set_abi_marshaler(w, ret.Type(), m, prop_name, is_generic_instantiation_class); } else { @@ -3813,7 +4644,7 @@ event % %;)", return marshalers; } - void write_abi_method_call_marshalers(writer& w, std::string_view invoke_target, bool is_generic, std::vector const& marshalers, bool has_noexcept_attr = false) + void write_abi_method_call_marshalers(writer& w, std::string_view invoke_target, std::string_view invoke_objref, bool is_generic, std::vector const& marshalers, bool has_noexcept_attr = false) { auto write_abi_invoke = [&](writer& w) { @@ -3823,6 +4654,21 @@ event % %;)", { have_pinnables |= m.write_pinnable(w); }, marshalers)); + + if (!settings.netstandard_compat) + { + w.write("%", + bind_each([&](writer& w, abi_marshaler const& m) + { + if (m.is_const_ref() && m.marshaler_type.empty()) + { + w.write("fixed(%* _% = &%)\n", + m.param_type, + m.param_name, + m.param_name); + } + }, marshalers)); + } if (have_pinnables) { bool write_delimiter{}; @@ -3834,7 +4680,9 @@ event % %;)", } if (is_generic) { - w.write("%.DynamicInvokeAbi(__params);\n", invoke_target); + w.write("global::System.Runtime.InteropServices.Marshal.ThrowExceptionForHR((int)%.DynamicInvoke(__params));\n", invoke_target); + w.write("global::System.GC.KeepAlive(__params);\n"); + w.write("global::System.GC.KeepAlive(%);\n", invoke_objref); } else if (!has_noexcept_attr) { @@ -3845,6 +4693,7 @@ event % %;)", w.write(", "); m.write_marshal_to_abi(w); }, marshalers)); + w.write("global::System.GC.KeepAlive(%);\n", invoke_objref); } else { w.write("%(ThisPtr%);\n", @@ -3854,6 +4703,7 @@ event % %;)", w.write(", "); m.write_marshal_to_abi(w); }, marshalers)); + w.write("global::System.GC.KeepAlive(%);\n", invoke_objref); } for (auto&& m : marshalers) { @@ -3913,18 +4763,27 @@ finally ); } - void write_abi_method_call(writer& w, method_signature signature, std::string_view invoke_target, bool is_generic, bool raw_return_type = false, bool has_noexcept_attr = false) + void write_abi_method_call(writer& w, method_signature signature, std::string_view invoke_target, std::string_view invoke_objref, bool is_generic, bool raw_return_type = false, bool has_noexcept_attr = false, bool is_generic_instantiation_class = false) { - write_abi_method_call_marshalers(w, invoke_target, is_generic, get_abi_marshalers(w, signature, is_generic, "", raw_return_type), has_noexcept_attr); + write_abi_method_call_marshalers(w, invoke_target, invoke_objref, is_generic, get_abi_marshalers(w, signature, is_generic, "", raw_return_type, is_generic_instantiation_class), has_noexcept_attr); } - void write_abi_method_with_raw_return_type(writer& w, MethodDef const& method) + void write_static_abi_method_with_raw_return_type(writer& w, TypeDef const& iface, MethodDef const& method) { if (is_special(method)) { return; } + bool generic_type = distance(iface.GenericParam()) > 0; + auto init_call_variables = [&](writer& w) + { + if (generic_type) + { + w.write("\nvar _obj = (ObjectReference<%.Vftbl>)_genericObj;", bind(iface, typedef_name_type::ABI, false)); + } + w.write("\nvar ThisPtr = _obj.ThisPtr;\n"); + }; auto write_raw_return_type = [](writer& w, method_signature const& sig) { if (auto return_sig = sig.return_signature()) @@ -3939,26 +4798,38 @@ finally method_signature signature{ method }; auto [invoke_target, is_generic] = get_invoke_info(w, method); + auto objRef = generic_type ? "_genericObj" : "_obj"; w.write(R"( -public unsafe %% %(%) -{%} +public static unsafe % %(% %%%) +{%%} )", - // In the .NET Standard 2.0 code-gen, the fully-projected signature will be available in the base class, so we need to specify new to hide it - settings.netstandard_compat ? "new " : "", bind(write_raw_return_type, signature), method.Name(), + settings.netstandard_compat ? w.write_temp("ObjectReference<%.Vftbl>", bind(iface, typedef_name_type::ABI, true)) : "IObjectReference", + objRef, + signature.has_params() ? ", " : "", bind_list(", ", signature.params()), - bind(signature, invoke_target, is_generic, true, is_noexcept(method))); + bind(init_call_variables), + bind(signature, invoke_target, objRef, is_generic, true, is_noexcept(method), false)); } - void write_composing_factory_method(writer& w, MethodDef const& method) + void write_static_composing_factory_method(writer& w, TypeDef const& iface, MethodDef const& method) { if (is_special(method)) { return; } + bool generic_type = distance(iface.GenericParam()) > 0; + auto init_call_variables = [&](writer& w) + { + if (generic_type) + { + w.write("\nvar _obj = (ObjectReference<%.Vftbl>)_genericObj;", bind(iface, typedef_name_type::ABI, false)); + } + w.write("\nvar ThisPtr = _obj.ThisPtr;\n"); + }; auto write_composable_constructor_params = [&](writer& w, method_signature const& method_sig) { auto const& params = method_sig.params(); @@ -4009,96 +4880,19 @@ public unsafe %% %(%) true }; + auto objRef = generic_type ? "_genericObj" : "_obj"; w.write(R"( -public unsafe % %(%) -{%} +public static unsafe % %(% %%%) +{%%} )", bind(write_raw_return_type, signature), method.Name(), + settings.netstandard_compat ? w.write_temp("ObjectReference<%.Vftbl>", bind(iface, typedef_name_type::ABI, true)) : "IObjectReference", + objRef, + signature.has_params() ? ", " : "", bind(write_composable_constructor_params, signature), - bind(invoke_target, is_generic, abi_marshalers, is_noexcept(method))); - } - - template - std::string write_factory_cache_object(writer& w, TypeDef const& factory_type, TypeDef const& class_type) - { - std::string_view cache_type_name = factory_type.TypeName(); - if (settings.netstandard_compat) - { - auto cache_vftbl_type = w.write_temp("ABI.%.%.Vftbl", class_type.TypeNamespace(), cache_type_name); - auto cache_interface = - w.write_temp( - R"(ActivationFactory<%>.As<%>)", - class_type.TypeName(), - cache_vftbl_type); - - w.write(R"( -internal sealed class _% : ABI.%.% -{ -public _%() : base(%()) { } -private static _% _instance = new _%(); -internal static _% Instance => _instance; -% -} -)", - cache_type_name, - class_type.TypeNamespace(), - cache_type_name, - cache_type_name, - cache_interface, - cache_type_name, - cache_type_name, - cache_type_name, - bind_each(factory_type.MethodList()) - ); - } - else - { - w.write(R"( -internal sealed class _% : IWinRTObject -{ -private IObjectReference _obj; -private IntPtr ThisPtr => _obj.ThisPtr; -public _%() -{ -_obj = ActivationFactory<%>.As(GuidGenerator.GetIID(typeof(%.%).GetHelperType())); -} - -private static _% _instance = new _%(); -internal static _% Instance => _instance; - -IObjectReference IWinRTObject.NativeObject => _obj; -bool IWinRTObject.HasUnwrappableNativeObject => false; -private volatile global::System.Collections.Concurrent.ConcurrentDictionary _queryInterfaceCache; -private global::System.Collections.Concurrent.ConcurrentDictionary MakeQueryInterfaceCache() -{ - global::System.Threading.Interlocked.CompareExchange(ref _queryInterfaceCache, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); - return _queryInterfaceCache; -} -global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.QueryInterfaceCache => _queryInterfaceCache ?? MakeQueryInterfaceCache(); -private volatile global::System.Collections.Concurrent.ConcurrentDictionary _additionalTypeData; -private global::System.Collections.Concurrent.ConcurrentDictionary MakeAdditionalTypeData() -{ - global::System.Threading.Interlocked.CompareExchange(ref _additionalTypeData, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); - return _additionalTypeData; -} -global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.AdditionalTypeData => _additionalTypeData ?? MakeAdditionalTypeData(); - -% -} -)", - cache_type_name, - cache_type_name, - class_type.TypeName(), - class_type.TypeNamespace(), - cache_type_name, - cache_type_name, - cache_type_name, - cache_type_name, - bind_each(factory_type.MethodList())); - } - - return w.write_temp("_%.Instance", cache_type_name); + bind(init_call_variables), + bind(invoke_target, objRef, is_generic, abi_marshalers, is_noexcept(method))); } void write_interface_members_netstandard(writer& w, TypeDef const& type) @@ -4116,11 +4910,11 @@ public unsafe %% %(%) { %} )", - method.Name() == "ToString"sv ? "override " : "", + method.Name() == "ToString"sv && signature.params().empty() ? "override " : "", bind(signature), method.Name(), bind_list(", ", signature.params()), - bind(signature, invoke_target, is_generic, false, is_noexcept(method))); + bind(signature, invoke_target, "_obj", is_generic, false, is_noexcept(method), false)); } for (auto&& prop : type.PropertyList()) @@ -4149,7 +4943,7 @@ bind([&](writer& w) w.write(R"(get {%} )", -bind(invoke_target, is_generic, marshalers, is_noexcept(prop))); +bind(invoke_target, "_obj", is_generic, marshalers, is_noexcept(prop))); } if (setter) { @@ -4169,7 +4963,7 @@ bind(invoke_target, is_generic, marshalers, is w.write(R"(set {%} )", -bind(invoke_target, is_generic, marshalers, is_noexcept(prop))); +bind(invoke_target, "_obj", is_generic, marshalers, is_noexcept(prop))); } w.write("}\n"); } @@ -4178,7 +4972,7 @@ bind(invoke_target, is_generic, marshalers, is for (auto&& evt : type.EventList()) { auto semantics = get_type_semantics(evt.EventType()); - auto event_source = w.write_temp(settings.netstandard_compat ? "_%" : "Get_%()", evt.Name()); + auto event_source = w.write_temp(settings.netstandard_compat ? "_%" : "Get_%2()", evt.Name()); w.write(R"( %event % %% { @@ -4205,19 +4999,19 @@ remove => %.Unsubscribe(value); void write_interface_members(writer& w, TypeDef const& type) { - if (is_exclusive_to(type)) + if (is_exclusive_to(type) && !settings.idic_exclusiveto) { return; } - bool generic_type = distance(type.GenericParam()) > 0; + // In authoring scenarios, exclusive interfaces don't exist, so use the CCW impl type. + bool implement_ccw_interface = does_abi_interface_implement_ccw_interface(type); auto init_call_variables = [&](writer& w) { if (!settings.netstandard_compat) { - w.write("\nvar _obj = ((%)((IWinRTObject)this).GetObjectReferenceForType(typeof(%).TypeHandle));", - generic_type ? "ObjectReference" : "IObjectReference", + w.write("\nvar _obj = ((IObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(%).TypeHandle));", bind(type, typedef_name_type::CCW, false)); } }; @@ -4240,12 +5034,12 @@ remove => %.Unsubscribe(value); (settings.netstandard_compat && method.Name() == "ToString"sv) ? "override " : "", bind(signature), bind([&](writer& w) + { + if (!settings.netstandard_compat) { - if (!settings.netstandard_compat) - { - w.write("%.", bind(type, typedef_name_type::CCW, false)); - } - }), + w.write("%.", bind(type, implement_ccw_interface ? typedef_name_type::CCW : typedef_name_type::Projected, false)); + } + }), method.Name(), bind_list(", ", signature.params()), bind(init_call_variables), @@ -4267,7 +5061,7 @@ bind([&](writer& w) { if (!settings.netstandard_compat) { - w.write("%.", bind(type, typedef_name_type::CCW, false)); + w.write("%.", bind(type, implement_ccw_interface ? typedef_name_type::CCW : typedef_name_type::Projected, false)); } }), prop.Name()); @@ -4317,7 +5111,7 @@ return %; for (auto&& evt : type.EventList()) { auto semantics = get_type_semantics(evt.EventType()); - auto event_source = w.write_temp(settings.netstandard_compat ? "_%" : "Get_%()", evt.Name()); + auto event_source = w.write_temp(settings.netstandard_compat ? "_%" : "Get_%2()", evt.Name()); w.write(R"( %event % %% { @@ -4338,7 +5132,7 @@ remove { if (!settings.netstandard_compat) { - w.write("%.", bind(type, typedef_name_type::CCW, false)); + w.write("%.", bind(type, implement_ccw_interface ? typedef_name_type::CCW : typedef_name_type::Projected, false)); } }), evt.Name(), @@ -4350,38 +5144,180 @@ remove } } - void write_static_abi_class_members(writer& w, TypeDef const& iface, uint32_t const& abi_methods_start_index) + void write_generic_method_delegate_variable(writer& w, MethodDef const& method, method_signature const& signature, bool is_parameter_variable = false) + { + w.write("%delegate* %%%", + is_parameter_variable ? "" : "internal unsafe volatile static ", + bind_list(", ", signature.params()), + signature.has_params() ? ", " : "", + bind(signature), + is_parameter_variable ? "" : "_", + method.Name(), + is_parameter_variable ? "" : ";"); + } + + void write_static_abi_class_members(writer& w, TypeDef const& iface, uint32_t const& abi_methods_start_index = 6, bool is_generic_method_instantiation_class = false) { bool generic_type = distance(iface.GenericParam()) > 0; auto init_call_variables = [&](writer& w) { if (generic_type) { - w.write("\nvar _obj = (ObjectReference<%.Vftbl>)_genericObj;", bind(iface, typedef_name_type::ABI, false)); + if (settings.netstandard_compat) + { + w.write("\nvar _obj = (ObjectReference<%.Vftbl>)_genericObj;", bind(iface, typedef_name_type::ABI, false)); + } + else + { + w.write("\nvar _obj = _genericObj;"); + } } w.write("\nvar ThisPtr = _obj.ThisPtr;\n"); }; + auto write_method_delegate_variable = [&](writer& w, MethodDef const& method, method_signature const& signature) + { + w.write(R"( +internal unsafe volatile static delegate* _%; +)", + bind_list(", ", signature.params()), + signature.has_params() ? ", " : "", + bind(signature), + method.Name()); + }; + + // We make the static ABI classes public for override interfaces to expose + // the vftbl ptr. But we don't need the actual RCW implementation for it + // to be public and in the API surface. So we use this to determine whether + // to make them internal. + bool isExclusiveInterface = is_exclusive_to(iface); + + bool writeEnsureRcwMethodsInitialized = false; + for (auto&& method : iface.MethodList()) { if (is_special(method)) { continue; } + method_signature signature{ method }; - auto [invoke_target, is_generic] = get_invoke_info(w, method, abi_methods_start_index); + + // For methods with only generic returns, we generate them + // in the static ABI class which has abi types are generics + // and the main static ABI class just calls them as function pointers. + // This allows to use the ABI type as part of the method implementation + // and make the delegate signature blittable. Note we only handle + // generic returns as those are the only scenario we encounter + // with the code generated generic interfaces. + bool signature_has_only_generic_return = + !settings.netstandard_compat && + abi_signature_has_generic_parameters(w, signature) && + !abi_signature_without_return_has_generic_parameters(w, signature); + bool projected_signature_has_generic = + !settings.netstandard_compat && + projected_signature_has_generic_parameters(w, signature); + + if (is_generic_method_instantiation_class && !signature_has_only_generic_return) + { + // These methods are handled by the main static methods class. + continue; + } + + if (projected_signature_has_generic) + { + if (!is_generic_method_instantiation_class) + { + write_method_delegate_variable(w, method, signature); + } + } + + auto [invoke_target, is_generic] = get_invoke_info(w, method, abi_methods_start_index, signature_has_only_generic_return && is_generic_method_instantiation_class); w.write(R"( -public static unsafe %% %(IObjectReference %%%) -{%%} +% static unsafe % %(% %%%) +{%} )", - (settings.netstandard_compat && method.Name() == "ToString"sv) ? "override " : "", + isExclusiveInterface ? "internal" : "public", bind(signature), method.Name(), + settings.netstandard_compat ? w.write_temp("ObjectReference<%.Vftbl>", bind(iface, typedef_name_type::ABI, true)) : "IObjectReference", generic_type ? "_genericObj" : "_obj", signature.has_params() ? ", " : "", bind_list(", ", signature.params()), - bind(init_call_variables), - bind(signature, invoke_target, is_generic, false, is_noexcept(method))); + bind([&](writer& w) { + if (signature_has_only_generic_return && !is_generic_method_instantiation_class) + { + writeEnsureRcwMethodsInitialized = true; + w.write("\n_EnsureRcwMethodsInitialized();"); + w.write("\nreturn _%(_genericObj%%);\n", + method.Name(), + signature.has_params() ? ", " : "", + bind_list(", ", signature.params())); + } + else if (projected_signature_has_generic && !is_generic_method_instantiation_class) + { + writeEnsureRcwMethodsInitialized = true; + w.write(R"( +if (%) +{ + _EnsureRcwMethodsInitialized(); + % _%(_genericObj%%); +} +else +{ + % %Fallback(_genericObj%%); +} + +[MethodImpl(MethodImplOptions.NoInlining)] +static % %Fallback(% %%%) +{ + if (_% != null) + { + % _%(_genericObj%%); + } + else + { + % + } +} +)", + // Early return to ensure things are trimmed correctly on NAOT. + // See https://github.com/dotnet/runtime/blob/main/docs/design/tools/illink/feature-checks.md. + settings.netstandard_compat ? "false" : "!RuntimeFeature.IsDynamicCodeCompiled", + signature.return_signature() ? "return " : "", + method.Name(), + signature.has_params() ? ", " : "", + bind_list(", ", signature.params()), + + // CoreCLR paths + signature.return_signature() ? "return " : "", + method.Name(), + signature.has_params() ? ", " : "", + bind_list(", ", signature.params()), + + // Fallback method + bind(signature), + method.Name(), + settings.netstandard_compat ? w.write_temp("ObjectReference<%.Vftbl>", bind(iface, typedef_name_type::ABI, true)) : "IObjectReference", + generic_type ? "_genericObj" : "_obj", + signature.has_params() ? ", " : "", + bind_list(", ", signature.params()), + method.Name(), + signature.return_signature() ? "return " : "", + method.Name(), + signature.has_params() ? ", " : "", + bind_list(", ", signature.params()), + bind([&](writer& w) { + init_call_variables(w); + write_abi_method_call(w, signature, invoke_target, "_obj", is_generic, false, is_noexcept(method)); + })); + } + else + { + init_call_variables(w); + write_abi_method_call(w, signature, invoke_target, "_obj", is_generic, false, is_noexcept(method)); + } + })); } for (auto&& prop : iface.PropertyList()) @@ -4390,60 +5326,236 @@ public static unsafe %% %(IObjectReference %%%) if (getter) { - auto [invoke_target, is_generic] = get_invoke_info(w, getter, abi_methods_start_index); auto signature = method_signature(getter); + bool signature_has_only_generic_return = + !settings.netstandard_compat && + abi_signature_has_generic_parameters(w, signature) && + !abi_signature_without_return_has_generic_parameters(w, signature); + + if (is_generic_method_instantiation_class && !signature_has_only_generic_return) + { + // These methods are handled by the main static methods class. + continue; + } + + bool projected_signature_has_generic = + !settings.netstandard_compat && + projected_signature_has_generic_parameters(w, signature); + + if (projected_signature_has_generic) + { + if (!is_generic_method_instantiation_class) + { + write_method_delegate_variable(w, getter, signature); + } + } + + auto [invoke_target, is_generic] = get_invoke_info(w, getter, abi_methods_start_index, signature_has_only_generic_return && is_generic_method_instantiation_class); auto marshalers = get_abi_marshalers(w, signature, is_generic, prop.Name()); - w.write(R"(public static unsafe % get_%(IObjectReference %) -{%%} + w.write(R"(% static unsafe % get_%(% %) +{%} )", + isExclusiveInterface ? "internal" : "public", write_prop_type(w, prop), prop.Name(), + settings.netstandard_compat ? w.write_temp("ObjectReference<%.Vftbl>", bind(iface, typedef_name_type::ABI, true)) : "IObjectReference", generic_type ? "_genericObj" : "_obj", - bind(init_call_variables), - bind(invoke_target, is_generic, marshalers, is_noexcept(prop))); + bind([&](writer& w) { + if (signature_has_only_generic_return && !is_generic_method_instantiation_class) + { + w.write("\nreturn _get_%(_genericObj);\n", prop.Name()); + } + else if (projected_signature_has_generic && !is_generic_method_instantiation_class) + { + writeEnsureRcwMethodsInitialized = true; + w.write(R"( +if (!RuntimeFeature.IsDynamicCodeCompiled) +{ + _EnsureRcwMethodsInitialized(); + return _%(_genericObj); +} + +return get_%Fallback(_genericObj); + +[MethodImpl(MethodImplOptions.NoInlining)] +static % get_%Fallback(IObjectReference _genericObj) +{ + if (_% != null) + { + return _%(_genericObj); + } + else + { + % + } +} +)", + getter.Name(), + prop.Name(), + write_prop_type(w, prop), + prop.Name(), + getter.Name(), + getter.Name(), + bind([&](writer& w) { + init_call_variables(w); + write_abi_method_call_marshalers(w, invoke_target, "_obj", is_generic, marshalers, is_noexcept(prop)); + })); + } + else + { + init_call_variables(w); + write_abi_method_call_marshalers(w, invoke_target, "_obj", is_generic, marshalers, is_noexcept(prop)); + } + })); } - if (setter) + if (setter && !is_generic_method_instantiation_class) { auto [invoke_target, is_generic] = get_invoke_info(w, setter, abi_methods_start_index); auto signature = method_signature(setter); auto marshalers = get_abi_marshalers(w, signature, is_generic, prop.Name()); marshalers[0].param_name = "value"; - w.write(R"(public static unsafe void set_%(IObjectReference %, % value) -{%%} + + bool projected_signature_has_generic = + !settings.netstandard_compat && + projected_signature_has_generic_parameters(w, signature); + if (projected_signature_has_generic && !is_generic_method_instantiation_class) + { + write_method_delegate_variable(w, setter, signature); + } + + w.write(R"(% static unsafe void set_%(% %, % value) +{%} )", + isExclusiveInterface ? "internal" : "public", prop.Name(), + settings.netstandard_compat ? w.write_temp("ObjectReference<%.Vftbl>", bind(iface, typedef_name_type::ABI, true)) : "IObjectReference", generic_type ? "_genericObj" : "_obj", write_prop_type(w, prop), - bind(init_call_variables), - bind(invoke_target, is_generic, marshalers, is_noexcept(prop))); - } - w.write("\n"); - } + bind([&](writer& w) { + if (projected_signature_has_generic && !is_generic_method_instantiation_class) + { + writeEnsureRcwMethodsInitialized = true; + w.write(R"( +if (!RuntimeFeature.IsDynamicCodeCompiled) +{ + _EnsureRcwMethodsInitialized(); + _%(_genericObj, value); +} +else +{ + set_%Fallback(_genericObj, value); +} + +[MethodImpl(MethodImplOptions.NoInlining)] +static void set_%Fallback(IObjectReference _genericObj, % value) +{ + if (_% != null) + { + _%(_genericObj, value); + } + else + { + % + } +} +)", + setter.Name(), + prop.Name(), + prop.Name(), + write_prop_type(w, prop), + setter.Name(), + setter.Name(), + bind([&](writer& w) { + init_call_variables(w); + write_abi_method_call_marshalers(w, invoke_target, "_obj", is_generic, marshalers, is_noexcept(prop)); + })); + } + else + { + init_call_variables(w); + write_abi_method_call_marshalers(w, invoke_target, "_obj", is_generic, marshalers, is_noexcept(prop)); + } + })); + } + w.write("\n"); + } + + if (writeEnsureRcwMethodsInitialized) + { + w.write(R"( +[MethodImpl(MethodImplOptions.AggressiveInlining)] +internal static unsafe void _EnsureRcwMethodsInitialized() +{ +if (RuntimeFeature.IsDynamicCodeCompiled) +{ +return; +} +if (!_RcwHelperInitialized) +{ +static void ThrowNotInitialized() +{ +throw new NotImplementedException( +$"Type '{typeof(%)}' was called without initializing the RCW methods using '@Methods.InitRcwHelper'. " + +$"If using 'IDynamicInterfaceCastable' support to do a dynamic cast to this interface, ensure the 'InitRcwHelper' method is called."); +} + +ThrowNotInitialized(); +} +} +)", + bind(iface, typedef_name_type::Projected, true), + iface.TypeName()); + } + + if (is_generic_method_instantiation_class) + { + // Events are handled by the main static methods class as they won't + // have generic return. + return; + } - int index = 0; for (auto&& evt : iface.EventList()) { w.write(R"(% -public static unsafe (Action<%>, Action<%>) Get_%(IObjectReference %, object _thisObj) +% + +% static unsafe global::ABI.WinRT.Interop.EventSource<%> Get_%2(% %, object _thisObj) { -var eventSource = _%.GetValue(_thisObj, (key) => +return _%.GetValue(_thisObj, (key) => { % return %; }); -return eventSource.EventActions; } )", bind(evt), - bind(get_type_semantics(evt.EventType()), typedef_name_type::Projected, false), + isExclusiveInterface ? "" : w.write_temp(R"(public static unsafe (Action<%>, Action<%>) Get_%(% %, object _thisObj) +{ +var eventSource = Get_%2(%, _thisObj); +return (eventSource.Subscribe, eventSource.Unsubscribe); +})", bind(get_type_semantics(evt.EventType()), typedef_name_type::Projected, false), + bind(get_type_semantics(evt.EventType()), typedef_name_type::Projected, false), + evt.Name(), + settings.netstandard_compat ? w.write_temp("ObjectReference<%.Vftbl>", bind(iface, typedef_name_type::ABI, true)) : "IObjectReference", + generic_type ? "_genericObj" : "_obj", + evt.Name(), + generic_type ? "_genericObj" : "_obj"), + isExclusiveInterface ? "internal" : "public", bind(get_type_semantics(evt.EventType()), typedef_name_type::Projected, false), evt.Name(), + settings.netstandard_compat ? w.write_temp("ObjectReference<%.Vftbl>", bind(iface, typedef_name_type::ABI, true)) : "IObjectReference", generic_type ? "_genericObj" : "_obj", + // return evt.Name(), - bind(init_call_variables), - bind(evt, index, abi_methods_start_index) + bind([&](writer& w) + { + if(generic_type) + { + init_call_variables(w); + } + }), + bind(evt, abi_methods_start_index) ); - index++; } } @@ -4475,6 +5587,7 @@ return eventSource.EventActions; required_interfaces[std::move(generic_enumerable)] = {}; }; + bool mapping_written = true; if (mapping->abi_name == "IIterable`1") // IEnumerable`1 { auto element = w.write_temp("%", bind(0)); @@ -4600,7 +5713,23 @@ return eventSource.EventActions; !emit_mapped_type_helpers)) }; } - return; + else if (mapping->mapped_name == "INotifyDataErrorInfo") + { + required_interfaces[std::move(interface_name)] = + { + w.write_temp("%", bind(emit_mapped_type_helpers ? "As()" : "((global::System.ComponentModel.INotifyDataErrorInfo)(IWinRTObject)this)", + !emit_mapped_type_helpers)) + }; + } + else + { + mapping_written = false; + } + + if (mapping_written) + { + return; + } } auto methods = w.write_temp("%", @@ -4642,10 +5771,8 @@ return eventSource.EventActions; } } - void write_guid_attribute(writer& w, TypeDef const& type) + void write_guid(writer& w, TypeDef const& type, bool lowerCase) { - auto fully_qualify_guid = (type.TypeNamespace() == "Windows.Foundation.Metadata"); - auto attribute = get_attribute(type, "Windows.Foundation.Metadata", "GuidAttribute"); if (!attribute) { @@ -4658,8 +5785,10 @@ return eventSource.EventActions; auto get_arg = [&](decltype(args)::size_type index) { return get(args[index].value).value; }; - w.write_printf(R"([%s("%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X")])", - fully_qualify_guid ? "global::System.Runtime.InteropServices.Guid" : "Guid", + w.write_printf( + lowerCase ? + R"(%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x)" : + R"(%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X)", get(get_arg(0)), get(get_arg(1)), get(get_arg(2)), @@ -4673,6 +5802,48 @@ return eventSource.EventActions; get(get_arg(10))); } + void write_guid_attribute(writer& w, TypeDef const& type) + { + auto fully_qualify_guid = (type.TypeNamespace() == "Windows.Foundation.Metadata"); + + w.write(R"([%("%")])", + fully_qualify_guid ? "global::System.Runtime.InteropServices.Guid" : "Guid", + bind(type, false)); + } + + void write_guid_bytes(writer& w, TypeDef const& type) + { + auto attribute = get_attribute(type, "Windows.Foundation.Metadata", "GuidAttribute"); + if (!attribute) + { + throw_invalid("'Windows.Foundation.Metadata.GuidAttribute' attribute for type '", type.TypeNamespace(), ".", type.TypeName(), "' not found"); + } + + auto args = attribute.Value().FixedArgs(); + + using std::get; + + auto get_arg = [&](decltype(args)::size_type index) { return get(args[index].value).value; }; + + w.write_printf(R"(0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X, 0x%X)", + (get(get_arg(0)) >> 0) & 0xFF, + (get(get_arg(0)) >> 8) & 0xFF, + (get(get_arg(0)) >> 16) & 0xFF, + (get(get_arg(0)) >> 24) & 0xFF, + (get(get_arg(1)) >> 0) & 0xFF, + (get(get_arg(1)) >> 8) & 0xFF, + (get(get_arg(2)) >> 0) & 0xFF, + (get(get_arg(2)) >> 8) & 0xFF, + get(get_arg(3)), + get(get_arg(4)), + get(get_arg(5)), + get(get_arg(6)), + get(get_arg(7)), + get(get_arg(8)), + get(get_arg(9)), + get(get_arg(10))); + } + void write_type_inheritance(writer& w, TypeDef const& type, type_semantics base_semantics, bool add_custom_qi, bool include_exclusive_interface) { auto delimiter{ " : " }; @@ -4707,7 +5878,7 @@ return eventSource.EventActions; } } - std::string get_vmethod_delegate_type(writer& w, MethodDef const& method, std::string vmethod_name) + std::string get_vmethod_delegate_type(writer& w, MethodDef const& method, std::string) { method_signature signature{ method }; if (is_special(method)) @@ -4774,7 +5945,7 @@ return eventSource.EventActions; { auto const [generic_abi_type, generic_param] = get_generic_abi_type(w, get_type_semantics(sig)); generic_abi_types.push_back({w.write_temp(!generic_param.empty() ? "%%" : "typeof(%)%", - generic_abi_type, byref ? ".MakeByRefType()" : ""), generic_param }); + generic_abi_type, byref ? (settings.netstandard_compat ? ".MakeByRefType()" : ".MakePointerType()") : ""), generic_param }); }; auto add_array_param = [&](param_category category) @@ -4836,7 +6007,7 @@ return eventSource.EventActions; w.write("(%)", bind(signature)); return; } - if (have_generic_params) + if (settings.netstandard_compat && have_generic_params) { w.write("<"); int count = 0; @@ -4899,7 +6070,7 @@ return eventSource.EventActions; auto generic_type = generic_abi_types[index++].second; if (!return_sig.Type().is_szarray() && !generic_type.empty()) { - if (settings.netstandard_compat || have_generic_params) + if (settings.netstandard_compat) { w.write(", out % %", generic_type, bind(signature.return_param_name())); @@ -5122,11 +6293,13 @@ return eventSource.EventActions; } }; - auto get_managed_marshalers(writer& w, method_signature const& signature, bool is_generic) + auto get_managed_marshalers(writer& w, method_signature const& signature, bool /*is_generic*/, bool is_generic_instantiation_class) { std::vector marshalers; + concurrency::concurrent_unordered_set generic_instantiations; - auto set_marshaler = [is_generic](writer& w, type_semantics const& semantics, managed_marshaler& m) + std::function set_marshaler = + [&](writer& w, type_semantics const& semantics, managed_marshaler& m) { m.param_type = w.write_temp("%", bind(semantics)); @@ -5176,17 +6349,54 @@ return eventSource.EventActions; { set_typedef_marshaler(type); }, - [&](generic_type_index const& /*var*/) + [&](generic_type_index const& var) { - m.param_type = get_generic_abi_type(w, semantics).second; - m.local_type = w.write_temp("%", bind(semantics)); - m.marshaler_type = w.write_temp("Marshaler<%>", m.local_type); - m.abi_boxed = true; + if (is_generic_instantiation_class) + { + set_marshaler(w, w.get_generic_arg(var.index), m); + } + else + { + m.param_type = get_generic_abi_type(w, semantics).second; + m.local_type = w.write_temp("%", bind(semantics)); + m.marshaler_type = w.write_temp("Marshaler<%>", m.local_type); + m.abi_boxed = true; + } }, [&](generic_type_instance const& type) { auto guard{ w.push_generic_args(type) }; set_typedef_marshaler(type.generic_type); + + if (!settings.netstandard_compat) + { + auto generic_instantiation_class_name = get_generic_instantiation_class_type_name(w, type.generic_type); + if (!starts_with(generic_instantiation_class_name, "Windows_Foundation_IReference")) + { + auto concrete_type = ConvertGenericTypeInstanceToConcreteType(w, type); + + bool has_generic_type_param = false; + for (size_t idx = 0; idx < concrete_type.generic_args.size(); idx++) + { + auto& generic_arg_semantic = concrete_type.generic_args[idx]; + if (auto gtp = std::get_if(&generic_arg_semantic)) + { + has_generic_type_param = true; + break; + } + } + + if (!has_generic_type_param) + { + generic_instantiations.insert( + generic_type_instantiation + { + concrete_type, + generic_instantiation_class_name + }); + } + } + } }, [&](fundamental_type type) { @@ -5211,7 +6421,7 @@ return eventSource.EventActions; } m.local_type = (m.local_type.empty() ? m.param_type : m.local_type) + "[]"; } - m.use_pointers = !settings.netstandard_compat && !is_generic; + m.use_pointers = !settings.netstandard_compat; }; for (auto&& param : signature.params()) @@ -5231,25 +6441,27 @@ return eventSource.EventActions; ret.Type().is_szarray() ? param_category::receive_array : param_category::out }; set_marshaler(w, get_type_semantics(ret.Type()), m); - return std::pair{ marshalers, m }; + return std::tuple{ marshalers, m, generic_instantiations }; } - return std::pair{ marshalers, managed_marshaler{} }; + return std::tuple{ marshalers, managed_marshaler{}, generic_instantiations }; } - void write_managed_method_call(writer& w, method_signature signature, std::string invoke_expression_format) + void write_managed_method_call(writer& w, method_signature signature, std::string invoke_expression_format, bool is_generic_instantiation_class = false) { auto generic_abi_types = get_generic_abi_types(w, signature); bool have_generic_params = std::find_if(generic_abi_types.begin(), generic_abi_types.end(), [](auto&& pair) { return !pair.second.empty(); }) != generic_abi_types.end(); - auto managed_marshalers = get_managed_marshalers(w, signature, have_generic_params); - auto marshalers = managed_marshalers.first; - auto return_marshaler = managed_marshalers.second; + auto managed_marshalers = get_managed_marshalers(w, signature, have_generic_params, is_generic_instantiation_class); + auto marshalers = std::get<0>(managed_marshalers); + auto return_marshaler = std::get<1>(managed_marshalers); + auto generic_instantiations = std::get<2>(managed_marshalers); auto return_sig = signature.return_signature(); w.write( R"(% % +% try { % @@ -5261,6 +6473,11 @@ global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); } return 0;)", + bind_each([](writer& w, generic_type_instantiation const& i) + { + generic_type_instances.insert(i); + w.write("_ = global::WinRT.GenericTypeInstantiations.%.EnsureInitialized();\n", i.instantiation_class_name); + }, generic_instantiations), [&](writer& w) { if (!return_sig) return; return_marshaler.write_local(w); @@ -5323,23 +6540,47 @@ return 0;)", bool have_generic_params = std::find_if(generic_abi_types.begin(), generic_abi_types.end(), [](auto&& pair) { return !pair.second.empty(); }) != generic_abi_types.end(); - w.write( - R"( + w.write(R"( % private static unsafe int Do_Abi_%% { % })", - !settings.netstandard_compat && !generic_type ? "[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]" : "", - vmethod_name, - bind(method), - bind( - signature, - w.write_temp("global::WinRT.ComWrappersSupport.FindObject<%>(%).%%", - type_name, - have_generic_params ? "new IntPtr(thisPtr)" : "thisPtr", - method.Name(), - "(%)"))); +!settings.netstandard_compat && !generic_type ? "[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]" : "", +vmethod_name, +bind(method), +bind( + signature, + w.write_temp("global::WinRT.ComWrappersSupport.FindObject<%>(%).%%", + type_name, + have_generic_params ? "new IntPtr(thisPtr)" : "thisPtr", + method.Name(), + "(%)"), + false)); + } + + void write_method_abi_invoke_helper(writer& w, MethodDef const& method) + { + if (method.SpecialName()) return; + + method_signature signature{ method }; + auto return_sig = signature.return_signature(); + auto type_name = write_type_name_temp(w, method.Parent()); + auto vmethod_name = get_vmethod_name(w, method.Parent(), method); + + w.write(R"( +public static % Do_Abi_%(IntPtr thisPtr%%) +{ +%global::WinRT.ComWrappersSupport.FindObject<%>(thisPtr).%(%); +})", +bind(signature), +vmethod_name, +signature.has_params() ? ", " : "", +bind_list(", ", signature.params()), +return_sig ? "return " : "", +type_name, +method.Name(), +bind_list(", ", signature.params())); } void write_property_abi_invoke(writer& w, Property const& prop) @@ -5375,7 +6616,8 @@ private static unsafe int Do_Abi_%% type_name, have_generic_params ? "new IntPtr(thisPtr)" : "thisPtr", prop.Name(), - "%"))); + "%"), + false)); } if (getter) @@ -5405,9 +6647,52 @@ private static unsafe int Do_Abi_%% type_name, have_generic_params ? "new IntPtr(thisPtr)" : "thisPtr", prop.Name(), - "%"))); + "%"), + false)); + } + } + + void write_property_abi_invoke_helper(writer& w, Property const& prop) + { + auto [getter, setter] = get_property_methods(prop); + auto type_name = write_type_name_temp(w, prop.Parent()); + if (setter) + { + method_signature setter_sig{ setter }; + auto vmethod_name = get_vmethod_name(w, setter.Parent(), setter); + + // WinRT properties can't be indexers. + XLANG_ASSERT(setter_sig.params().size() == 1); + + w.write(R"( +public static void Do_Abi_%(IntPtr thisPtr, %) +{ +global::WinRT.ComWrappersSupport.FindObject<%>(thisPtr).% = %; +})", +vmethod_name, +bind_list(", ", setter_sig.params()), +type_name, +prop.Name(), +bind_list(", ", setter_sig.params())); } + if (getter) + { + method_signature getter_sig{ getter }; + auto vmethod_name = get_vmethod_name(w, getter.Parent(), getter); + + // WinRT properties can't be indexers. + XLANG_ASSERT(getter_sig.params().size() == 0); + w.write(R"( +public static % Do_Abi_%(IntPtr thisPtr) +{ +return global::WinRT.ComWrappersSupport.FindObject<%>(thisPtr).%; +})", +bind(getter_sig), +vmethod_name, +type_name, +prop.Name()); + } } void write_event_abi_invoke(writer& w, Event const& evt) @@ -5450,6 +6735,7 @@ private static global::System.Runtime.CompilerServices.ConditionalWeakTable<%, g % private static unsafe int Do_Abi_%% { +% %% = default; try { @@ -5467,6 +6753,12 @@ return __ex.HResult; !settings.netstandard_compat && !generic_type ? "[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]" : "", get_vmethod_name(w, add_method.Parent(), add_method), bind(add_method), + bind([&](writer& w) { + if (!settings.netstandard_compat && !generic_type) + { + write_ensure_generic_type_initialized(w, semantics, false); + } + }), settings.netstandard_compat ? "" : "*", add_handler_event_token_name, type_name, @@ -5504,36 +6796,257 @@ return __ex.HResult; evt.Name()); } - void write_vtable(writer& w, TypeDef const& type, std::string const& type_name, - std::string const& nongenerics_class, - std::vector& nongeneric_delegates) + void write_event_abi_invoke_helper(writer& w, Event const& evt) + { + auto type_name = write_type_name_temp(w, evt.Parent()); + auto semantics = get_type_semantics(evt.EventType()); + auto [add_method, remove_method] = get_event_methods(evt); + auto add_signature = method_signature{ add_method }; + + w.write(R"( +private volatile static global::System.Runtime.CompilerServices.ConditionalWeakTable<%, global::WinRT.EventRegistrationTokenTable<%>> _%_tokenTables; +private static global::System.Runtime.CompilerServices.ConditionalWeakTable<%, global::WinRT.EventRegistrationTokenTable<%>> Make%TokenTable() +{ + global::System.Threading.Interlocked.CompareExchange(ref _%_tokenTables, new(), null); + return _%_tokenTables; +} +private static global::System.Runtime.CompilerServices.ConditionalWeakTable<%, global::WinRT.EventRegistrationTokenTable<%>> _%_TokenTables => _%_tokenTables ?? Make%TokenTable(); +)", + type_name, + bind(semantics, typedef_name_type::Projected, false), + evt.Name(), + type_name, + bind(semantics, typedef_name_type::Projected, false), + evt.Name(), + evt.Name(), + evt.Name(), + type_name, + bind(semantics, typedef_name_type::Projected, false), + evt.Name(), + evt.Name(), + evt.Name()); + + w.write(R"( +public static global::WinRT.EventRegistrationToken Do_Abi_%(IntPtr thisPtr, % handler) +{ +var __this = global::WinRT.ComWrappersSupport.FindObject<%>(thisPtr); +var token = _%_TokenTables.GetOrCreateValue(__this).AddEventHandler(handler); +__this.% += handler; +return token; +} +)", + get_vmethod_name(w, add_method.Parent(), add_method), + bind(add_signature.params().back()), + type_name, + evt.Name(), + evt.Name()); + w.write(R"( +public static void Do_Abi_%(IntPtr thisPtr, global::WinRT.EventRegistrationToken token) +{ +var __this = global::WinRT.ComWrappersSupport.FindObject<%>(thisPtr); +if(__this != null && _%_TokenTables.TryGetValue(__this, out var __table) && __table.RemoveEventHandler(token, out var __handler)) +{ +__this.% -= __handler; +} +} +)", + get_vmethod_name(w, remove_method.Parent(), remove_method), + type_name, + evt.Name(), + evt.Name()); + } + + void get_vtable_members( + writer& w, + TypeDef const& type, + std::vector& nongeneric_delegates, + std::vector& method_marshals_to_abi, + std::vector& method_marshals_to_projection, + std::vector& vtable_delegates, + std::vector& method_create_function_pointers_to_projection, + std::vector& type_declarations, + std::vector& vtable_members) { + auto nongenerics_class = w.write_temp("%_Delegates", bind(type, typedef_name_type::ABI, false)); auto methods = type.MethodList(); auto is_generic = distance(type.GenericParam()) > 0; - std::vector method_marshals_to_abi; - std::vector method_marshals_to_projection; - std::vector method_create_delegates_to_projection; + for (auto& method : methods) + { + bool signature_has_generic_parameters{}; - w.write(R"(% -public struct Vftbl -{ -internal IInspectable.Vftbl IInspectableVftbl; -%%%%%% -})", - bind(type), - bind_each([&](writer& w, MethodDef const& method) + auto generic_abi_types = get_generic_abi_types(w, method_signature{ method }); + bool have_generic_type_parameters = std::find_if(generic_abi_types.begin(), generic_abi_types.end(), + [](auto&& pair) { return !pair.second.empty(); }) != generic_abi_types.end(); + + auto vmethod_name = get_vmethod_name(w, type, method); + auto delegate_type = get_vmethod_delegate_type(w, method, vmethod_name); + std::string vtable_field_type; + bool function_pointer = false; + if (vtable_field_type == "") { - bool signature_has_generic_parameters{}; + delegate_type = nongenerics_class + "." + vmethod_name; + writer::write_generic_type_name_guard g(w, [&](writer& /*w*/, uint32_t /*index*/) { + signature_has_generic_parameters = true; + }); - auto generic_abi_types = get_generic_abi_types(w, method_signature{ method }); - bool have_generic_type_parameters = std::find_if(generic_abi_types.begin(), generic_abi_types.end(), - [](auto&& pair) { return !pair.second.empty(); }) != generic_abi_types.end(); + auto delegate_definition = w.write_temp("public unsafe delegate int %(%);\n", + vmethod_name, + bind(method_signature{ method })); - auto vmethod_name = get_vmethod_name(w, type, method); - auto delegate_type = get_vmethod_delegate_type(w, method, vmethod_name); - std::string vtable_field_type; - bool function_pointer = false; - if(vtable_field_type == "") + if (signature_has_generic_parameters) + { + delegate_type = vtable_field_type = "global::System.Delegate"; + } + else + { + if (settings.netstandard_compat || is_generic) + { + nongeneric_delegates.push_back(delegate_definition); + } + + vtable_field_type = w.write_temp("delegate* unmanaged[Stdcall]<%, int>", bind(method_signature{ method })); + function_pointer = true; + } + } + else + { + // We're a well-known delegate type, but we still need to get the function pointer type. + vtable_field_type = w.write_temp("delegate* unmanaged[Stdcall]<%, int>", bind(method_signature{ method })); + function_pointer = true; + } + if (!function_pointer) + { + vtable_members.emplace_back(w.write_temp("public % %;", vtable_field_type, vmethod_name)); + } + else if (settings.netstandard_compat || is_generic) + { + // Work around https://github.com/dotnet/runtime/issues/37295 + vtable_members.emplace_back(w.write_temp(R"( +private void* _%; +public % % { get => (%)_%; set => _%=(void*)value; } +)", + vmethod_name, vtable_field_type, vmethod_name, vtable_field_type, vmethod_name, vmethod_name)); + } + else + { + // Work around C# compiler's lack of support for UnmanagedCallersOnly + vtable_members.emplace_back(w.write_temp(R"( +private delegate* unmanaged[Stdcall]<%, int> _%; +public % % { get => (%)_%; set => _%=(delegate* unmanaged[Stdcall]<%, int>)value; } +)", + bind(method_signature{ method }), vmethod_name, + vtable_field_type, vmethod_name, vtable_field_type, vmethod_name, vmethod_name, + bind(method_signature{ method }))); + } + uint32_t const delegate_cache_index = method.index() - methods.first.index(); + uint32_t const vtable_index = delegate_cache_index + 6; + if (is_generic) + { + method_marshals_to_abi.emplace_back(signature_has_generic_parameters ? + w.write_temp("% = Marshal.GetDelegateForFunctionPointer(vftbl[%], %_Type);\n", + vmethod_name, vtable_index, vmethod_name) : + w.write_temp("% = (%)(vftbl[%]);\n", + vmethod_name, vtable_field_type, vtable_index) + ); + + method_marshals_to_projection.emplace_back(signature_has_generic_parameters ? + w.write_temp("nativeVftbl[%] = Marshal.GetFunctionPointerForDelegate(AbiToProjectionVftable.%);\n", + vtable_index, vmethod_name) : + w.write_temp("nativeVftbl[%] = (IntPtr)AbiToProjectionVftable._%;", vtable_index, vmethod_name) + ); + + if (have_generic_type_parameters) + { + auto create_delegate = + w.write_temp(R"(global::System.Delegate.CreateDelegate(%, typeof(%).GetMethod("Do_Abi_%", BindingFlags.NonPublic | BindingFlags.Static)))", + !signature_has_generic_parameters ? w.write_temp("typeof(%)", vtable_field_type) : vmethod_name + "_Type", + bind(type), + vmethod_name); + vtable_delegates.emplace_back(create_delegate); + + method_create_function_pointers_to_projection.emplace_back( + w.write_temp(R"(% = %%)", + vmethod_name, + !signature_has_generic_parameters ? w.write_temp("(%)", vtable_field_type) : "", + create_delegate)); + + if (signature_has_generic_parameters) + { + type_declarations.emplace_back(w.write_temp("Type %_Type = %(new Type[]{ typeof(void*), %typeof(int) });\n", + vmethod_name, + settings.netstandard_compat ? "global::WinRT.Projections.GetAbiDelegateType" : "Expression.GetDelegateType", + bind_each([&](writer& w, auto&& pair) + { + w.write("%, ", pair.first); + }, generic_abi_types))); + } + } + else + { + auto create_delegate = w.write_temp("new %(Do_Abi_%)", + delegate_type, + vmethod_name); + vtable_delegates.emplace_back(create_delegate); + method_create_function_pointers_to_projection.emplace_back( + w.write_temp("_% = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[%] = %)", + vmethod_name, + delegate_cache_index, + create_delegate)); + } + } + else if (settings.netstandard_compat) + { + auto create_delegate = w.write_temp("new %(Do_Abi_%)", + delegate_type, + vmethod_name); + vtable_delegates.emplace_back(create_delegate); + method_create_function_pointers_to_projection.emplace_back( + w.write_temp("_% = (void*)Marshal.GetFunctionPointerForDelegate(DelegateCache[%] = %)", + vmethod_name, + delegate_cache_index, + create_delegate)); + } + else + { + // Work around C# compiler's lack of support for UnmanagedCallersOnly + method_create_function_pointers_to_projection.emplace_back( + w.write_temp("_% = &Do_Abi_%", + vmethod_name, vmethod_name) + ); + } + } + } + + void write_vtable(writer& w, TypeDef const& type, std::string const& type_name, + std::string const& nongenerics_class, + std::vector& nongeneric_delegates) + { + auto methods = type.MethodList(); + auto is_generic = distance(type.GenericParam()) > 0; + std::vector method_marshals_to_abi; + std::vector method_marshals_to_projection; + std::vector method_create_delegates_to_projection; + + w.write(R"(% +public struct Vftbl +{ +internal IInspectable.Vftbl IInspectableVftbl; +%%%%%% +})", + bind(type), + bind_each([&](writer& w, MethodDef const& method) + { + bool signature_has_generic_parameters{}; + + auto generic_abi_types = get_generic_abi_types(w, method_signature{ method }); + bool have_generic_type_parameters = std::find_if(generic_abi_types.begin(), generic_abi_types.end(), + [](auto&& pair) { return !pair.second.empty(); }) != generic_abi_types.end(); + + auto vmethod_name = get_vmethod_name(w, type, method); + auto delegate_type = get_vmethod_delegate_type(w, method, vmethod_name); + std::string vtable_field_type; + bool function_pointer = false; + if(vtable_field_type == "") { delegate_type = nongenerics_class + "." + vmethod_name; writer::write_generic_type_name_guard g(w, [&](writer& /*w*/, uint32_t /*index*/) { @@ -5660,13 +7173,13 @@ internal IInspectable.Vftbl IInspectableVftbl; [&](writer& w) { if (!is_generic) return; - w.write("public static Guid PIID = GuidGenerator.CreateIID(typeof(%));\n", type_name); + w.write("public static readonly Guid PIID = GuidGenerator.CreateIID(typeof(%));\n", type_name); w.write(R"(% internal unsafe Vftbl(IntPtr thisPtr) : this() { -var vftblPtr = Marshal.PtrToStructure(thisPtr); -var vftbl = (IntPtr*)vftblPtr.Vftbl; -IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); +var vftblPtr = *(void***)thisPtr; +var vftbl = (IntPtr*)vftblPtr; +IInspectableVftbl = *(IInspectable.Vftbl*)vftblPtr; %} )", bind_each([&](writer& w, MethodDef const& method) @@ -5677,8 +7190,9 @@ IInspectableVftbl = Marshal.PtrToStructure(vftblPtr.Vftbl); { auto generic_abi_types = get_generic_abi_types(w, method_signature{ method }); - w.write("public static readonly Type %_Type = Expression.GetDelegateType(new Type[]{ typeof(void*), %typeof(int) });\n", + w.write("public static readonly Type %_Type = %(new Type[]{ typeof(void*), %typeof(int) });\n", vmethod_name, + settings.netstandard_compat ? "global::WinRT.Projections.GetAbiDelegateType" : "Expression.GetDelegateType", bind_each([&](writer& w, auto&& pair) { w.write("%, ", pair.first); @@ -5703,7 +7217,7 @@ AbiToProjectionVftable = new Vftbl IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, % }; -var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * %); +var nativeVftbl = (IntPtr*)ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * %); % AbiToProjectionVftablePtr = (IntPtr)nativeVftbl; } @@ -5731,7 +7245,7 @@ public static readonly IntPtr AbiToProjectionVftablePtr; % static unsafe Vftbl() { -AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * %); +AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.IInspectable.Vftbl) + sizeof(IntPtr) * %); (*(Vftbl*)AbiToProjectionVftablePtr) = new Vftbl { IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, @@ -5797,8 +7311,9 @@ IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, void write_authoring_metadata_type(writer& w, TypeDef const& type) { - w.write("%%%internal class % {}\n", + w.write("%%%%internal class % {}\n", bind(type), + bind(type, false), [&](writer& w) { auto category = get_category(type); @@ -5814,11 +7329,12 @@ IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, void write_contract(writer& w, TypeDef const& type) { auto type_name = write_type_name_temp(w, type); - w.write(R"(%public enum % + w.write(R"(%% enum % { } )", bind(type, false), + internal_accessibility(), type_name); } @@ -5869,7 +7385,7 @@ IInspectableVftbl = global::WinRT.IInspectable.Vftbl.AbiToProjectionVftable, bind(type), bind(type), bind(type, false), - is_exclusive_to(type) || (is_projection_internal(type) || (settings.internal || settings.embedded)) ? "internal" : "public", + (is_exclusive_to(type) && !settings.public_exclusiveto) || (is_projection_internal(type) || (settings.internal || settings.embedded)) ? "internal" : "public", type_name, bind(type, object_type{}, false, false), bind(type) @@ -5929,7 +7445,7 @@ return null; var vftblT = new Vftbl(thisPtr); return ObjectReference.FromAbi(thisPtr, vftblT); } -public static Guid PIID = Vftbl.PIID; +public static readonly Guid PIID = Vftbl.PIID; )"); }, type_name, @@ -5938,10 +7454,9 @@ public static Guid PIID = Vftbl.PIID; type.TypeName(), [&](writer& w) { - int index = 0; for (auto&& evt : type.EventList()) { - w.write("_% = %;\n", evt.Name(), bind(evt, index++, INSPECTABLE_METHOD_COUNT)); + w.write("_% = %;\n", evt.Name(), bind(evt, INSPECTABLE_METHOD_COUNT)); } }, [&](writer& w) { @@ -5990,6 +7505,109 @@ public static Guid PIID = Vftbl.PIID; return true; } + void write_generic_interface_impl_class(writer& w, TypeDef const& iface) + { + w.write(R"( +internal sealed class @Impl% : %, IWinRTObject +{ +private IObjectReference _inner; + +internal @Impl(IObjectReference _inner) +{ +this._inner = _inner; +} + +public static @Impl% CreateRcw(IInspectable obj) => new(obj.ObjRef); + +% + +IObjectReference IWinRTObject.NativeObject => _inner; + +bool IWinRTObject.HasUnwrappableNativeObject => true; + +private volatile global::System.Collections.Concurrent.ConcurrentDictionary _queryInterfaceCache; +private global::System.Collections.Concurrent.ConcurrentDictionary MakeQueryInterfaceCache() +{ +global::System.Threading.Interlocked.CompareExchange(ref _queryInterfaceCache, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); +return _queryInterfaceCache; +} +global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.QueryInterfaceCache => _queryInterfaceCache ?? MakeQueryInterfaceCache(); +private volatile global::System.Collections.Concurrent.ConcurrentDictionary _additionalTypeData; +private global::System.Collections.Concurrent.ConcurrentDictionary MakeAdditionalTypeData() +{ +global::System.Threading.Interlocked.CompareExchange(ref _additionalTypeData, new global::System.Collections.Concurrent.ConcurrentDictionary(), null); +return _additionalTypeData; +} +global::System.Collections.Concurrent.ConcurrentDictionary IWinRTObject.AdditionalTypeData => _additionalTypeData ?? MakeAdditionalTypeData(); + +% +} +)", + iface.TypeName(), + bind(iface), + bind(iface, typedef_name_type::Projected, false), + iface.TypeName(), + iface.TypeName(), + bind(iface), + [&](writer& w) + { + bool hasDerivedGenericInterfaces = distance(iface.GenericParam()) == 0 && has_derived_generic_interface(iface); + std::set writtenInterfaces; + std::function write_objref_defintion = [&](writer& w, type_semantics const& ifaceTypeSemantics) + { + auto objrefname = w.write_temp("%", bind(ifaceTypeSemantics)); + + // When writing derived interfaces of interfaces, we can sometimes encounter duplicate interfaces. + // To prevent writing them multiple times, we catch them here. + if (writtenInterfaces.find(objrefname) != writtenInterfaces.end()) + { + return; + } + writtenInterfaces.insert(objrefname); + + w.write(R"( +private volatile IObjectReference __%; +private IObjectReference Make__%() +{ +% +global::System.Threading.Interlocked.CompareExchange(ref __%, ((IWinRTObject)this).NativeObject.As(%.IID), null); +return __%; +} +private IObjectReference % => __% ?? Make__%(); +)", + objrefname, + objrefname, + [&](writer& w) { + // We initialize the generic interface instantiation class if the respective interface for this + // impl class is not generic but has derived generic interfaces. This is because in those cases + // we know the specific generic instantiations and can initialize them here rather than earlier. + // By deferring it to here, we are able to trim friendly allowing to trim unused interfaces. + if (hasDerivedGenericInterfaces) + { + write_ensure_generic_type_initialized(w, ifaceTypeSemantics); + } + }, + objrefname, + bind(ifaceTypeSemantics, typedef_name_type::StaticAbiClass, false), + objrefname, + objrefname, + objrefname, + objrefname); + + for_typedef(w, ifaceTypeSemantics, [&](auto type) + { + for (auto&& ii : type.InterfaceImpl()) + { + write_objref_defintion(w, get_type_semantics(ii.Interface())); + } + }); + }; + + write_objref_defintion(w, iface); + }, + bind(iface, false, true)); + } + void write_static_abi_classes(writer& w, TypeDef const& iface) { auto fast_abi_class_val = get_fast_abi_class_for_interface(iface); @@ -6001,554 +7619,956 @@ public static Guid PIID = Vftbl.PIID; } } - w.write(R"(% static class % + if (settings.netstandard_compat) + { + w.write(R"(% static class % { +internal static global::System.Guid IID { get; } = new Guid(new byte[] { % }); + % } -)", - (is_exclusive_to(iface) || is_projection_internal(iface)) ? "internal" : internal_accessibility(), - bind(iface, typedef_name_type::StaticAbiClass, false), - [&](writer& w) { - if (!fast_abi_class_val.has_value() || (!fast_abi_class_val.value().contains_other_interface(iface) && !interfaces_equal(fast_abi_class_val.value().default_interface, iface))) { - write_static_abi_class_members(w, iface, INSPECTABLE_METHOD_COUNT); - return; - } - auto abi_methods_start_index = INSPECTABLE_METHOD_COUNT; - write_static_abi_class_members(w, fast_abi_class_val.value().default_interface, abi_methods_start_index); - abi_methods_start_index += distance(fast_abi_class_val.value().default_interface.MethodList()) + get_class_hierarchy_index(fast_abi_class_val.value().class_type); - for (auto&& other_iface : fast_abi_class_val.value().other_interfaces) - { - write_static_abi_class_members(w, other_iface, abi_methods_start_index); - abi_methods_start_index += distance(other_iface.MethodList()); - } - }); - } - - bool write_abi_interface(writer& w, TypeDef const& type) - { - bool is_generic = distance(type.GenericParam()) > 0; - XLANG_ASSERT(get_category(type) == category::interface_type); - auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::ABI); - - // For exclusive interfaces which aren't overridable interfaces that are implemented by unsealed types, - // we do not need any of the Do_Abi functions or the vtable logic as we will not create CCWs for them. - // But we are still keeping the interface itself for any helper type lookup that may happen for like GUID lookup. - if (!is_generic && - is_exclusive_to(type) && - // check for !authored type - !(settings.component && settings.filter.includes(type))) +)", + (is_exclusive_to(iface) || is_projection_internal(iface)) ? "internal" : internal_accessibility(), + bind(iface, typedef_name_type::StaticAbiClass, false), + bind(iface), + [&](writer& w) { + if (!fast_abi_class_val.has_value() || (!fast_abi_class_val.value().contains_other_interface(iface) && !interfaces_equal(fast_abi_class_val.value().default_interface, iface))) { + write_static_abi_class_members(w, iface, INSPECTABLE_METHOD_COUNT); + return; + } + auto abi_methods_start_index = INSPECTABLE_METHOD_COUNT; + write_static_abi_class_members(w, fast_abi_class_val.value().default_interface, abi_methods_start_index); + abi_methods_start_index += distance(fast_abi_class_val.value().default_interface.MethodList()) + get_class_hierarchy_index(fast_abi_class_val.value().class_type); + for (auto&& other_iface : fast_abi_class_val.value().other_interfaces) + { + write_static_abi_class_members(w, other_iface, abi_methods_start_index); + abi_methods_start_index += distance(other_iface.MethodList()); + } + }); + } + else { - bool hasOverridableAttribute = false; - auto exclusive_to_type = get_exclusive_to_type(type); - for (auto&& iface : exclusive_to_type.InterfaceImpl()) + // Derived classes need access to the vftbl ptr if they are extending unsealed types. + auto write_vftable_ptr = !is_exclusive_to(iface) || settings.public_exclusiveto; + if (!write_vftable_ptr) { - for_typedef(w, get_type_semantics(iface.Interface()), [&](auto interface_type) + // Also write for both overridable interfaces and for default interfaces of authored types. + auto exclusive_to_type = get_exclusive_to_type(iface); + auto authored_component_interface = settings.component && settings.filter.includes(iface); + if (!exclusive_to_type.Flags().Sealed() || authored_component_interface) { - if (type == interface_type && is_overridable(iface)) + for (auto&& iface_impl : exclusive_to_type.InterfaceImpl()) { - hasOverridableAttribute = true; - } - }); + for_typedef(w, get_type_semantics(iface_impl.Interface()), [&](auto interface_type) + { + if (iface == interface_type && + (is_overridable(iface_impl) || + (authored_component_interface && is_default_interface(iface_impl)))) + { + write_vftable_ptr = true; + } + }); - if (hasOverridableAttribute) - { - break; + if (write_vftable_ptr) + { + break; + } + } } } - if (!hasOverridableAttribute) - { - w.write(R"(% -internal interface % : % -{ -} -)", -bind(type), -type_name, -bind(type, typedef_name_type::CCW, false) -); - return true; - } - } - - auto nongenerics_class = w.write_temp("%_Delegates", bind(type, typedef_name_type::ABI, false)); - - std::vector nongeneric_delegates; - - std::map required_interfaces; - write_required_interface_members_for_abi_type(w, type, required_interfaces, false); + bool is_generic = distance(iface.GenericParam()) > 0; - w.write(R"(% -% -internal unsafe interface % : % + w.write(R"(% static class % { -%%%%%} -)", -// Interface abi implementation - is_exclusive_to(type) ? "" : "[DynamicInterfaceCastableImplementation]", - bind(type), - type_name, - bind(type, typedef_name_type::CCW, false), +% +% +% +} +)", + ((is_exclusive_to(iface) && !write_vftable_ptr)|| is_projection_internal(iface)) ? "internal" : internal_accessibility(), + bind(iface, typedef_name_type::StaticAbiClass, false), [&](writer& w) { - w.write(is_generic ? "public static Guid PIID = Vftbl.PIID;\n\n" : ""); - }, - // Vftbl - bind([&](writer& w) - { - auto methods = type.MethodList(); if (is_generic) - { - write_vtable(w, type, type_name, nongenerics_class, nongeneric_delegates); - } - else { w.write(R"( -public static IntPtr AbiToProjectionVftablePtr; -static unsafe @() +internal volatile static bool _RcwHelperInitialized; +unsafe static @Methods() { -AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(@), sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * %); -*(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; -% +ComWrappersSupport.RegisterHelperType(typeof(%), typeof(%)); +if (RuntimeFeature.IsDynamicCodeCompiled) +{ +#if NET8_0_OR_GREATER + [RequiresDynamicCode("Generic instantiations might not be available in AOT scenarios.")] +#endif + [UnconditionalSuppressMessage("Trimming", "IL2080", Justification = "ABI types never have constructors.")] + [UnconditionalSuppressMessage("Trimming", "IL2081", Justification = "ABI types never have constructors.")] + [MethodImpl(MethodImplOptions.NoInlining)] + static void @MethodsFallback() + { + if (!_RcwHelperInitialized) + { + var ensureInitializedFallback = (Func)typeof(@Methods<%>).MakeGenericType(%). + GetMethod("InitRcwHelperFallback", BindingFlags.NonPublic | BindingFlags.Static). + CreateDelegate(typeof(Func)); + ensureInitializedFallback(); + } + } + + @MethodsFallback(); +} } -%%% )", - type.TypeName(), - type.TypeName(), - distance(methods), - bind_list([&](writer& w, MethodDef const& method) + iface.TypeName(), + bind(iface, typedef_name_type::Projected, false), + bind(iface, typedef_name_type::ABI, false), + iface.TypeName(), + iface.TypeName(), + bind([&](writer& w) { - auto method_index = get_vmethod_index(method.Parent(), method); - auto method_name = method.Name(); - w.write("((delegate* unmanaged[Stdcall]<%, int>*)AbiToProjectionVftablePtr)[%] = &Do_Abi_%_%;", - bind(method_signature{ method }), - method_index + 6 /* number of entries in IInspectable */, - method_name, - method_index); - }, "\n", methods), - bind_each(methods), - bind_each(type.PropertyList()), - bind_each(type.EventList())); + for (auto i = 0; i < (distance(iface.GenericParam()) * 2) - 1; i++) + { + w.write(","); + } + }), + bind([&](writer& w) + { + for (auto i = 0; i < distance(iface.GenericParam()); i++) + { + if (i != 0) + { + w.write(", "); + } + + w.write("typeof(%), Marshaler<%>.AbiType", + bind(i), + bind(i)); + } + }), + iface.TypeName()); } - }), - bind(type), - "", - [&](writer& w) { - for (auto required_interface : required_interfaces) + // If the interface inherits other generic interfaces, but itself isn't generic, + // we need to handle the case where the class implementing the interface can be trimmed + // and we end up relying on IDIC casts to access the generic interfaces. But on AOT, + // this doesn't work, which means we have a fallback helper impl class for the interface + // that we instead create. + else if (has_derived_generic_interface(iface)) { - w.write("%", required_interface.second.members); - } - } - ); - - if (!nongeneric_delegates.empty()) - { - w.write(R"([global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] -% static class % -{ -%} -)", -internal_accessibility(), -nongenerics_class, -bind_each(nongeneric_delegates)); - } - w.write("\n"); - - return true; - } - - void write_custom_query_interface_impl(writer& w, TypeDef const& type) - { - - bool has_base_class = !std::holds_alternative(get_type_semantics(type.Extends())); - separator s{ w, " || " }; - w.write(R"( -%bool IsOverridableInterface(Guid iid) => %%; - -global::System.Runtime.InteropServices.CustomQueryInterfaceResult global::System.Runtime.InteropServices.ICustomQueryInterface.GetInterface(ref Guid iid, out IntPtr ppv) + w.write(R"( +private volatile static bool _RcwHelperInitialized; +public static bool InitRcwHelper() { -ppv = IntPtr.Zero; -if (IsOverridableInterface(iid) || global::WinRT.InterfaceIIDs.IInspectable_IID == iid) +if (_RcwHelperInitialized) { -return global::System.Runtime.InteropServices.CustomQueryInterfaceResult.NotHandled; +return true; } -if (%.TryAs(iid, out ppv) >= 0) -{ -return global::System.Runtime.InteropServices.CustomQueryInterfaceResult.Handled; +global::WinRT.ComWrappersSupport.RegisterTypedRcwFactory(typeof(%), @Impl.CreateRcw); +_RcwHelperInitialized = true; +return true; } - -return global::System.Runtime.InteropServices.CustomQueryInterfaceResult.NotHandled; -})", - bind([&](writer& w) - { - auto visibility = "protected "; - auto overridable = "virtual "; - if (has_base_class) - { - overridable = "override "; +)", + bind(iface, typedef_name_type::Projected, false), + iface.TypeName()); } - else if (type.Flags().Sealed()) - { - visibility = "private "; - overridable = ""; + }, + [&](writer& w) { + if (!fast_abi_class_val.has_value() || (!fast_abi_class_val.value().contains_other_interface(iface) && !interfaces_equal(fast_abi_class_val.value().default_interface, iface))) { + write_static_abi_class_members(w, iface, INSPECTABLE_METHOD_COUNT, false); + return; } - w.write(visibility); - w.write(overridable); - }), - bind_each([&](writer& w, InterfaceImpl const& iface) - { - if (has_attribute(iface, "Windows.Foundation.Metadata", "OverridableAttribute")) + auto abi_methods_start_index = INSPECTABLE_METHOD_COUNT; + write_static_abi_class_members(w, fast_abi_class_val.value().default_interface, abi_methods_start_index, false); + abi_methods_start_index += distance(fast_abi_class_val.value().default_interface.MethodList()) + get_class_hierarchy_index(fast_abi_class_val.value().class_type); + for (auto&& other_iface : fast_abi_class_val.value().other_interfaces) { - s(); - w.write("GuidGenerator.GetIID(typeof(%)) == iid", - bind(get_type_semantics(iface.Interface()), typedef_name_type::ABI, false)); + write_static_abi_class_members(w, other_iface, abi_methods_start_index, false); + abi_methods_start_index += distance(other_iface.MethodList()); } - }, type.InterfaceImpl()), - bind([&](writer& w) - { - if (has_base_class) + }, + [&](writer& w) { + if (is_generic) { - s(); - w.write("base.IsOverridableInterface(iid)"); + w.write(R"( +public static global::System.Guid IID => GuidGenerator.CreateIID(typeof(%)); +)", + bind(iface, typedef_name_type::ABI, false)); } - if (s.first) + else { - w.write("false"); + w.write(R"( +public static ref readonly global::System.Guid IID +{ +[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +get +{ +global::System.ReadOnlySpan data = new byte[] { % }; +return ref global::System.Runtime.CompilerServices.Unsafe.As(ref global::System.Runtime.InteropServices.MemoryMarshal.GetReference(data)); +} +} +)", + bind(iface)); } - }), - settings.netstandard_compat ? "GetReferenceForQI()" : "((IWinRTObject)this).NativeObject"); - } - - void write_wrapper_class(writer& w, TypeDef const& type) - { - if (is_static(type)) - { - return; - } - auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::CCW); - auto wrapped_type_name = write_type_name_temp(w, type, "%", typedef_name_type::Projected); - auto default_interface_name = get_default_interface_name(w, type, false, true); - auto base_semantics = get_type_semantics(type.Extends()); - auto from_abi_new = !std::holds_alternative(base_semantics) ? "new " : ""; + if (write_vftable_ptr) + { + if (is_generic) + { + w.write(R"( +private static global::System.IntPtr abiToProjectionVftablePtr; +public static global::System.IntPtr AbiToProjectionVftablePtr => abiToProjectionVftablePtr; - w.write(R"(%%[global::WinRT.ProjectedRuntimeClass(typeof(%))] -%internal %class %% +internal static bool TryInitCCWVtable(global::System.IntPtr ptr) { -public %(% comp) -{ -_comp = comp; +return global::System.Threading.Interlocked.CompareExchange(ref abiToProjectionVftablePtr, ptr, global::System.IntPtr.Zero) == global::System.IntPtr.Zero; } -public static implicit operator %(% comp) + +% +% +% +)", + bind_each(iface.MethodList()), + bind_each(iface.PropertyList()), + bind_each(iface.EventList())); + } + else + { + w.write(R"( +public static global::System.IntPtr AbiToProjectionVftablePtr => %.AbiToProjectionVftablePtr; +)", + bind(iface, typedef_name_type::ABI, false)); + } + } + }); + + if (is_generic) + { + int32_t num_methods = distance(iface.MethodList()); + uint32_t index = 0; + + std::vector nongeneric_delegates; + std::vector method_marshals_to_abi; + std::vector method_marshals_to_projection; + std::vector vtable_delegates; + std::vector method_create_function_pointers_to_projection; + std::vector type_declarations; + std::vector vtable_members; + get_vtable_members( + w, + iface, + nongeneric_delegates, + method_marshals_to_abi, + method_marshals_to_projection, + vtable_delegates, + method_create_function_pointers_to_projection, + type_declarations, + vtable_members); + + w.write(R"( +% static class %% { -return comp._comp; -} -public static implicit operator %(% comp) +public unsafe static bool InitRcwHelper(%) { -return new %(comp); -} -public static %% FromAbi(IntPtr thisPtr) +if (%._RcwHelperInitialized) { -if (thisPtr == IntPtr.Zero) return null; -return MarshalInspectable<%>.FromAbi(thisPtr); +return true; } + % -private readonly % _comp; +global::WinRT.ComWrappersSupport.RegisterTypedRcwFactory(typeof(%), @Impl%.CreateRcw); +%._RcwHelperInitialized = true; +return true; } -)", - bind(type), - bind(type), - default_interface_name, - bind(type, false), - bind(type), - type_name, - bind(type, base_semantics, false, true), - type_name, - wrapped_type_name, - wrapped_type_name, - type_name, - type_name, - wrapped_type_name, - type_name, - from_abi_new, - wrapped_type_name, - wrapped_type_name, - bind(type, true), - wrapped_type_name); - } - - void write_class_netstandard(writer& w, TypeDef const& type) - { - if (settings.component) - { - write_wrapper_class(w, type); - return; - } - if (is_static(type)) - { - write_static_class(w, type); - return; - } +private unsafe static bool InitRcwHelperFallback() +{ +return InitRcwHelper(%); +} - auto type_name = write_type_name_temp(w, type); - auto default_interface_name = get_default_interface_name(w, type, false); - auto default_interface_abi_name = get_default_interface_name(w, type, true); - auto base_semantics = get_type_semantics(type.Extends()); - auto derived_new = std::holds_alternative(base_semantics) ? "" : "new "; - auto gc_pressure_amount = 0; - if (auto gc_pressure_attr = get_attribute(type, "Windows.Foundation.Metadata", "GCPressureAttribute")) - { - auto sig = gc_pressure_attr.Value(); - auto const& args = sig.NamedArgs(); - auto amount = std::get(std::get(std::get(args[0].value.value).value).value); - gc_pressure_amount = amount == 0 ? 12000 : amount == 1 ? 120000 : 1200000; - } +% - w.write(R"(%%[global::WinRT.ProjectedRuntimeClass(nameof(_default))] -%% %class %%, IEquatable<%> +public static unsafe bool InitCcw( +% +) { -public %IntPtr ThisPtr => _default.ThisPtr; +if (%.AbiToProjectionVftablePtr != default) +{ +return false; +} -private IObjectReference _inner = null; -private readonly Lazy<%> _defaultLazy; +var abiToProjectionVftablePtr = (IntPtr)NativeMemory.AllocZeroed((nuint)(sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * %)); +*(IInspectable.Vftbl*)abiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; % -private % _default => _defaultLazy.Value; -% -public static %% FromAbi(IntPtr thisPtr) +if (!%.TryInitCCWVtable(abiToProjectionVftablePtr)) { -if (thisPtr == IntPtr.Zero) return null; -return MarshalInspectable<%>.FromAbi(thisPtr); +NativeMemory.Free((void*)abiToProjectionVftablePtr); +return false; } -% %(% ifc)% +return true; +} + +private static global::System.Delegate[] DelegateCache; + +#if NET8_0_OR_GREATER +[RequiresDynamicCode("The necessary marshalling code or generic instantiations might not be available.")] +#endif +internal static unsafe void InitFallbackCCWVtable() { -_defaultLazy = new Lazy<%>(() => ifc); -%} % - -public static bool operator ==(% x, % y) => (x?.ThisPtr ?? IntPtr.Zero) == (y?.ThisPtr ?? IntPtr.Zero); -public static bool operator !=(% x, % y) => !(x == y); +DelegateCache = new global::System.Delegate[] +{ % +}; + +var abiToProjectionVftablePtr = (IntPtr)NativeMemory.AllocZeroed((nuint)(sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * %)); +*(IInspectable.Vftbl*)abiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; % -private struct InterfaceTag{}; +if (!%.TryInitCCWVtable(abiToProjectionVftablePtr)) +{ +NativeMemory.Free((void*)abiToProjectionVftablePtr); +} +} -private % AsInternal(InterfaceTag<%> _) => _default; -%% +% +% +% } + )", - bind(type), - bind(type), - bind(type, false), - internal_accessibility(), - bind(type), - type_name, - bind(type, base_semantics, true, false), - type_name, - derived_new, - default_interface_abi_name, - bind(type), - default_interface_abi_name, - bind(type), - derived_new, - type_name, - type_name, - type.Flags().Sealed() ? "internal" : "protected internal", - type_name, - default_interface_abi_name, - bind(base_semantics), - default_interface_abi_name, - - [&](writer& w) - { - if (!gc_pressure_amount) return; - w.write("GC.AddMemoryPressure(%);\n", gc_pressure_amount); - }, - [&](writer& w) - { - if (!gc_pressure_amount) return; - w.write(R"(~%() -{ -GC.RemoveMemoryPressure(%); -} -)", - type_name, - gc_pressure_amount); - }, - type_name, - type_name, - type_name, - type_name, - bind([&](writer& w) + ((is_exclusive_to(iface) && !write_vftable_ptr) || is_projection_internal(iface)) ? "internal" : internal_accessibility(), + bind([&](writer& w) { + writer::write_generic_type_name_guard g(w, [&](writer& w, uint32_t index) + { + write_projection_type_for_name_type(w, w.get_generic_arg_scope(index).first, typedef_name_type::Projected); + w.write(", "); + write_projection_type_for_name_type(w, w.get_generic_arg_scope(index).first, typedef_name_type::Projected); + w.write("Abi"); + }); + + w.write("%", bind(iface, typedef_name_type::StaticAbiClass, false)); + }), + bind_each([&](writer& w, GenericParam const& /*gp*/) + { + w.write(" where "); + write_generic_type_name(w, index++); + w.write("Abi : unmanaged"); + }, iface.GenericParam()), + [&](writer& w) { + bool write_delimiter = false; + for (auto& method : iface.MethodList()) + { + method_signature signature{ method }; + if (!settings.netstandard_compat && + !(is_special(method) && (starts_with(method.Name(), "add_") || starts_with(method.Name(), "remove_"))) && + projected_signature_has_generic_parameters(w, signature)) + { + if (write_delimiter) + { + w.write(",\n"); + } + + write_generic_method_delegate_variable(w, method, signature, true); + write_delimiter = true; + } + } + }, + bind(iface, typedef_name_type::StaticAbiClass, false), + bind_each([&](writer& w, MethodDef const& method) + { + method_signature signature{ method }; + if (!settings.netstandard_compat && + !(is_special(method) && (starts_with(method.Name(), "add_") || starts_with(method.Name(), "remove_"))) && + projected_signature_has_generic_parameters(w, signature)) + { + w.write("%._% = %;\n", + bind(iface, typedef_name_type::StaticAbiClass, false), + method.Name(), + method.Name()); + } + }, iface.MethodList()), + bind(iface, typedef_name_type::Projected, false), + iface.TypeName(), + bind(iface), + bind(iface, typedef_name_type::StaticAbiClass, false), + [&](writer& w) { + bool write_delimiter = false; + for (auto& method : iface.MethodList()) + { + method_signature signature{ method }; + if (!settings.netstandard_compat && + !(is_special(method) && (starts_with(method.Name(), "add_") || starts_with(method.Name(), "remove_"))) && + projected_signature_has_generic_parameters(w, signature)) + { + if (write_delimiter) + { + w.write(",\n"); + } + + bool signature_has_only_generic_return = + !settings.netstandard_compat && + abi_signature_has_generic_parameters(w, signature) && + !abi_signature_without_return_has_generic_parameters(w, signature); + if (signature_has_only_generic_return) + { + w.write("&%", method.Name()); + } + else + { + w.write("null"); + } + + write_delimiter = true; + } + } + }, + [&](writer& w) { + if (!fast_abi_class_val.has_value() || (!fast_abi_class_val.value().contains_other_interface(iface) && !interfaces_equal(fast_abi_class_val.value().default_interface, iface))) { + write_static_abi_class_members(w, iface, INSPECTABLE_METHOD_COUNT, true); + return; + } + auto abi_methods_start_index = INSPECTABLE_METHOD_COUNT; + write_static_abi_class_members(w, fast_abi_class_val.value().default_interface, abi_methods_start_index, true); + abi_methods_start_index += distance(fast_abi_class_val.value().default_interface.MethodList()) + get_class_hierarchy_index(fast_abi_class_val.value().class_type); + for (auto&& other_iface : fast_abi_class_val.value().other_interfaces) + { + write_static_abi_class_members(w, other_iface, abi_methods_start_index, true); + abi_methods_start_index += distance(other_iface.MethodList()); + } + }, + bind_list([&](writer& w, MethodDef const& method) + { + w.write("% %", + bind(method), + method.Name()); + }, ",\n", iface.MethodList()), + bind(iface, typedef_name_type::StaticAbiClass, false), + num_methods, + bind_list([&](writer& w, MethodDef const& method) + { + auto method_index = get_vmethod_index(method.Parent(), method); + auto method_name = method.Name(); + w.write("((%*)abiToProjectionVftablePtr)[%] = %;", + bind(method), + method_index + 6 /* number of entries in IInspectable */, + method_name); + }, "\n", iface.MethodList()), + bind(iface, typedef_name_type::StaticAbiClass, false), + bind_list(",\n", type_declarations), + bind_list(",\n", vtable_delegates), + num_methods, + bind_list([&](writer& w, MethodDef const& method) + { + auto method_index = get_vmethod_index(method.Parent(), method); + w.write("((IntPtr*)abiToProjectionVftablePtr)[%] = Marshal.GetFunctionPointerForDelegate(DelegateCache[%]);", + method_index + 6 /* number of entries in IInspectable */, + method_index); + }, "\n", iface.MethodList()), + bind(iface, typedef_name_type::StaticAbiClass, false), + bind_each(iface.MethodList()), + bind_each(iface.PropertyList()), + bind_each(iface.EventList()) + ); + + write_generic_interface_impl_class(w, iface); + } + else if (has_derived_generic_interface(iface)) { - bool return_type_matches = false; - if (!has_class_equals_method(type, &return_type_matches)) - { - w.write("public bool Equals(% other) => this == other;\n", type_name); - } - // Even though there is an equals method defined, it doesn't match the signature for IEquatable - // so we define an explicitly implemented one. - else if (!return_type_matches) + write_generic_interface_impl_class(w, iface); + } + } + } + + bool write_abi_interface(writer& w, TypeDef const& type) + { + bool is_generic = distance(type.GenericParam()) > 0; + XLANG_ASSERT(get_category(type) == category::interface_type); + auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::ABI); + + bool shouldOmitCcwCodegen = false; + + // For exclusive interfaces which aren't overridable interfaces that are implemented by unsealed types, + // we do not need any of the Do_Abi functions or the vtable logic as we will not create CCWs for them. + // But we are still keeping the interface itself for any helper type lookup that may happen for like GUID lookup. + // We avoid this path if we want to generate IDIC implementations for them though. + if (!is_generic && + (is_exclusive_to(type) && !settings.public_exclusiveto) && + // check for !authored type + !(settings.component && settings.filter.includes(type))) + { + bool hasOverridableAttribute = false; + auto exclusive_to_type = get_exclusive_to_type(type); + for (auto&& iface : exclusive_to_type.InterfaceImpl()) + { + for_typedef(w, get_type_semantics(iface.Interface()), [&](auto interface_type) { - w.write("bool IEquatable<%>.Equals(% other) => this == other;\n", type_name, type_name); - } + if (type == interface_type && is_overridable(iface)) + { + hasOverridableAttribute = true; + } + }); - if (!has_object_equals_method(type)) + if (hasOverridableAttribute) { - w.write("public override bool Equals(object obj) => obj is % that && this == that;\n", type_name); + break; } + } - if (!has_object_hashcode_method(type)) + if (!hasOverridableAttribute) + { + // Under normal conditions, we would stop here and just emit a minimal amount of code. + // However, if IDIC is requested, just continue normally, but omit the CCW generation. + // We know we can safely omit that because it wouldn't have normally be generated. + if (settings.idic_exclusiveto) { - w.write("public override int GetHashCode() => ThisPtr.GetHashCode();\n"); + shouldOmitCcwCodegen = true; } - }), - bind([&](writer& w) - { - bool has_base_type = !std::holds_alternative(get_type_semantics(type.Extends())); - - if (!type.Flags().Sealed()) + else { - w.write(R"( -protected %(global::WinRT.DerivedComposed _)% + // Otherwise, just write the minimal non-IDIC interface + w.write(R"(% +internal interface % : % { -_defaultLazy = new Lazy<%>(() => GetDefaultReference<%.Vftbl>()); -})", - type.TypeName(), - has_base_type ? ":base(_)" : "", - default_interface_abi_name, - default_interface_abi_name); +} +)", + bind(type), + type_name, + bind(type, typedef_name_type::CCW, false)); + + return true; } + } + } - std::string_view access_spec = "protected "; - std::string_view override_spec = has_base_type ? "override " : "virtual "; + auto nongenerics_class = w.write_temp("%_Delegates", bind(type, typedef_name_type::ABI, false)); - if (type.Flags().Sealed() && !has_base_type) - { - access_spec = "private "; - override_spec = " "; - } + std::string nongeneric_delegates = ""; - w.write(R"( -%%IObjectReference GetDefaultReference() => _default.AsInterface();)", - access_spec, - override_spec); + std::map required_interfaces; + write_required_interface_members_for_abi_type(w, type, required_interfaces, false); - w.write(R"( -%%IObjectReference GetReferenceForQI() => _inner ?? _default.ObjRef;)", - access_spec, - override_spec); - }), - default_interface_name, - default_interface_name, - bind(type, false), - bind(type)); - } + w.write(R"(% +% +internal unsafe interface % : % +{ +%%%%} +)", + // Interface abi implementation + is_exclusive_to(type) && !settings.idic_exclusiveto ? "" : "[DynamicInterfaceCastableImplementation]", + bind(type), + type_name, + bind(type, does_abi_interface_implement_ccw_interface(type) ? typedef_name_type::CCW : typedef_name_type::Projected, false), + // Vftbl + bind([&](writer& w) + { + if (shouldOmitCcwCodegen) + { + return; + } - - void write_class(writer& w, TypeDef const& type) + auto methods = type.MethodList(); + if (is_generic) + { + w.write(R"( +public static readonly Guid PIID = %.IID; +public static readonly IntPtr AbiToProjectionVftablePtr; +static unsafe @() +{ +if (RuntimeFeature.IsDynamicCodeCompiled) +{ +#if NET8_0_OR_GREATER + [RequiresDynamicCode("Generic instantiations might not be available in AOT scenarios.")] +#endif + [UnconditionalSuppressMessage("Trimming", "IL2080", Justification = "ABI types never have constructors.")] + [UnconditionalSuppressMessage("Trimming", "IL2081", Justification = "ABI types never have constructors.")] + [MethodImpl(MethodImplOptions.NoInlining)] + static void @Fallback() { - writer::write_platform_guard guard{ w }; - - if (settings.component) - { - write_wrapper_class(w, type); - return; - } - - if (is_static(type)) + if (%.AbiToProjectionVftablePtr == default) { - write_static_class(w, type); - return; + var initFallbackCCWVtable = (Action)typeof(@Methods<%>).MakeGenericType(%). + GetMethod("InitFallbackCCWVtable", BindingFlags.NonPublic | BindingFlags.Static). + CreateDelegate(typeof(Action)); + initFallbackCCWVtable(); } + } - auto type_name = write_type_name_temp(w, type); - auto default_interface_name = get_default_interface_name(w, type, false); - auto base_semantics = get_type_semantics(type.Extends()); - auto derived_new = std::holds_alternative(base_semantics) ? "" : "new "; + @Fallback(); +} - auto default_interface_typedef = for_typedef(w, get_type_semantics(get_default_interface(type)), [&](auto&& iface) { return iface; }); - auto is_manually_gen_default_interface = is_manually_generated_iface(default_interface_typedef); +AbiToProjectionVftablePtr = %.AbiToProjectionVftablePtr; +} - w.write(R"(%% -[global::WinRT.ProjectedRuntimeClass(typeof(%))] -[global::WinRT.ObjectReferenceWrapper(nameof(_inner))] -%% %class %%, IWinRTObject, IEquatable<%> +% +public unsafe struct Vftbl { -private IntPtr ThisPtr => _inner == null ? (((IWinRTObject)this).NativeObject).ThisPtr : _inner.ThisPtr; +internal IInspectable.Vftbl IInspectableVftbl; -private IObjectReference _inner = null; -% -% +public static readonly IntPtr AbiToProjectionVftablePtr = %.AbiToProjectionVftablePtr; -% -% -% -public static %% FromAbi(IntPtr thisPtr) -{ -if (thisPtr == IntPtr.Zero) return null; -return MarshalInspectable<%>.FromAbi(thisPtr); +public static readonly Guid PIID = %.IID; } +)", + bind(type, typedef_name_type::StaticAbiClass, false), + type.TypeName(), + type.TypeName(), + bind(type, typedef_name_type::StaticAbiClass, false), + type.TypeName(), + bind([&](writer& w) + { + for (auto i = 0; i < (distance(type.GenericParam()) * 2) - 1; i++) + { + w.write(","); + } + }), + bind([&](writer& w) + { + for (auto i = 0; i < distance(type.GenericParam()); i++) + { + if (i != 0) + { + w.write(", "); + } -% %(IObjectReference objRef)% + w.write("typeof(%), Marshaler<%>.AbiType", + bind(i), + bind(i)); + } + }), + type.TypeName(), + bind(type, typedef_name_type::StaticAbiClass, false), + bind(type), + bind(type, typedef_name_type::StaticAbiClass, false), + bind(type, typedef_name_type::StaticAbiClass, false)); + + nongeneric_delegates = + w.write_temp("%", + bind_each([&](writer& w, MethodDef const& method) { + bool signature_has_generic_parameters = false; + writer::write_generic_type_name_guard g(w, [&](writer& /*w*/, uint32_t /*index*/) { + signature_has_generic_parameters = true; + }); + + auto delegate_definition = w.write_temp("public unsafe delegate int %(%);\n", + get_vmethod_name(w, type, method), + bind(method_signature{ method })); + if (!signature_has_generic_parameters) + { + w.write(delegate_definition); + } + }, methods)); + } + else + { + w.write(R"( +public static readonly IntPtr AbiToProjectionVftablePtr; +static unsafe @() { -_inner = objRef.As(GuidGenerator.GetIID(typeof(%).GetHelperType())); -% -} - -public static bool operator ==(% x, % y) => (x?.ThisPtr ?? IntPtr.Zero) == (y?.ThisPtr ?? IntPtr.Zero); -public static bool operator !=(% x, % y) => !(x == y); -% -% - -private struct InterfaceTag{}; +AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(@), sizeof(IInspectable.Vftbl) + sizeof(IntPtr) * %); +*(IInspectable.Vftbl*)AbiToProjectionVftablePtr = IInspectable.Vftbl.AbiToProjectionVftable; % -%% } +%%% )", - bind(type), - bind(type), - default_interface_name, - bind(type, true), - internal_accessibility(), - bind(type), - type_name, - bind(type, base_semantics, true, false), - type_name, - bind(type), - bind([&](writer& w) + type.TypeName(), + type.TypeName(), + distance(methods), + bind_list([&](writer& w, MethodDef const& method) + { + auto method_index = get_vmethod_index(method.Parent(), method); + auto method_name = method.Name(); + w.write("((delegate* unmanaged[Stdcall]<%, int>*)AbiToProjectionVftablePtr)[%] = &Do_Abi_%_%;", + bind(method_signature{ method }), + method_index + 6 /* number of entries in IInspectable */, + method_name, + method_index); + }, "\n", methods), + bind_each(methods), + bind_each(type.PropertyList()), + bind_each(type.EventList())); + } + }), + bind(type), + "", + [&](writer& w) { + if (!is_exclusive_to(type) || settings.idic_exclusiveto) { - if (is_manually_gen_default_interface) + for (auto required_interface : required_interfaces) { - w.write("private readonly Lazy<%> _defaultLazy;", default_interface_name); + w.write("%", required_interface.second.members); } - }), - bind(type, type.Flags().Sealed()), + } + } + ); + + if (nongeneric_delegates.length() != 0) + { + w.write(R"([global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] +internal static class % +{ +%} +)", +nongenerics_class, +nongeneric_delegates); + } + w.write("\n"); + + return true; + } + + void write_custom_query_interface_impl(writer& w, TypeDef const& type) + { + + bool has_base_class = !std::holds_alternative(get_type_semantics(type.Extends())); + separator s{ w, " || " }; + w.write(R"( +%bool IsOverridableInterface(Guid iid) => %%; + +global::System.Runtime.InteropServices.CustomQueryInterfaceResult global::System.Runtime.InteropServices.ICustomQueryInterface.GetInterface(ref Guid iid, out IntPtr ppv) +{ +ppv = IntPtr.Zero; +if (IsOverridableInterface(iid) || global::WinRT.Interop.IID.IID_IInspectable == iid || global::WinRT.Interop.IID.IID_IWeakReferenceSource == iid) +{ +return global::System.Runtime.InteropServices.CustomQueryInterfaceResult.NotHandled; +} + +if (%.TryAs(iid, out ppv) >= 0) +{ +return global::System.Runtime.InteropServices.CustomQueryInterfaceResult.Handled; +} + +return global::System.Runtime.InteropServices.CustomQueryInterfaceResult.NotHandled; +})", bind([&](writer& w) + { + auto visibility = "protected "; + auto overridable = "virtual "; + if (has_base_class) { - w.write("private % _default => %;", default_interface_name, is_manually_gen_default_interface ? "_defaultLazy.Value" : "null"); - }), + overridable = "override "; + } + else if (type.Flags().Sealed()) + { + visibility = "private "; + overridable = ""; + } + w.write(visibility); + w.write(overridable); + }), + bind_each([&](writer& w, InterfaceImpl const& iface) + { + if (has_attribute(iface, "Windows.Foundation.Metadata", "OverridableAttribute")) + { + s(); + settings.netstandard_compat ? + w.write("GuidGenerator.GetIID(typeof(%)) == iid", + bind(get_type_semantics(iface.Interface()), typedef_name_type::ABI, false)) : + w.write("%.IID == iid", + bind(get_type_semantics(iface.Interface()), typedef_name_type::StaticAbiClass, true)); + } + }, type.InterfaceImpl()), + bind([&](writer& w) + { + if (has_base_class) + { + s(); + w.write("base.IsOverridableInterface(iid)"); + } + if (s.first) + { + w.write("false"); + } + }), + settings.netstandard_compat ? "GetReferenceForQI()" : "((IWinRTObject)this).NativeObject"); + } + + void write_wrapper_class(writer& w, TypeDef const& type) + { + if (is_static(type)) + { + return; + } + + auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::CCW); + auto wrapped_type_name = write_type_name_temp(w, type, "%", typedef_name_type::Projected); + auto default_interface_name = get_default_interface_name(w, type, false, true); + + if (settings.netstandard_compat) + { + auto base_semantics = get_type_semantics(type.Extends()); + auto from_abi_new = !std::holds_alternative(base_semantics) ? "new " : ""; + + // Fallback path for .NET Standard, with all interfaces of the user defined type implemented + // on the authoring metadata type as well. This is because on this target, CsWinRT will go + // through the list of implemented interfaces to construct the CCW and do other things. In + // theory, the type could be abstract and without actually providing an implementation of the + // interfaces it's declaring, but given this is a fallback path for backwards compatibility, + // it's simpler to just keep the existing code without any changes, to minimize risk. + w.write(R"(%%[global::WinRT.ProjectedRuntimeClass(typeof(%))] +%internal % partial class %% +{ +public %(% comp) +{ +_comp = comp; +} +public static implicit operator %(% comp) +{ +return comp._comp; +} +public static implicit operator %(% comp) +{ +return new %(comp); +} +public static %% FromAbi(IntPtr thisPtr) +{ +if (thisPtr == IntPtr.Zero) return null; +return MarshalInspectable<%>.FromAbi(thisPtr); +} +% +private readonly % _comp; +} +)", + bind(type), + bind(type), + default_interface_name, + bind(type, false), + bind(type), + type_name, + bind(type, base_semantics, false, true), + type_name, + wrapped_type_name, + wrapped_type_name, + type_name, + type_name, + wrapped_type_name, + type_name, + from_abi_new, + wrapped_type_name, + wrapped_type_name, + bind(type, true, false), + wrapped_type_name); + } + else + { + // This type can be empty, as it is only used for metadata lookup, but not as implementation. + // On modern .NET, we use [WinRTExposedType] to get all implemented interfaces for the vtable. + w.write(R"(%%[global::WinRT.ProjectedRuntimeClass(typeof(%))] +%internal % partial class % +{ +public static % FromAbi(IntPtr thisPtr) +{ +if (thisPtr == IntPtr.Zero) return null; +return MarshalInspectable<%>.FromAbi(thisPtr); +} +} +)", + bind(type), + bind(type), + default_interface_name, + bind(type, false), + bind(type), + type_name, + wrapped_type_name, + wrapped_type_name); + } + } + + void write_class_netstandard(writer& w, TypeDef const& type) + { + if (settings.component) + { + write_wrapper_class(w, type); + return; + } + + if (is_static(type)) + { + write_static_class(w, type); + return; + } + + auto type_name = write_type_name_temp(w, type); + auto default_interface_name = get_default_interface_name(w, type, false); + auto default_interface_abi_name = get_default_interface_name(w, type, true); + auto base_semantics = get_type_semantics(type.Extends()); + auto derived_new = std::holds_alternative(base_semantics) ? "" : "new "; + + auto gc_pressure_amount = get_gc_pressure_amount(type); + + w.write(R"(%%[global::WinRT.ProjectedRuntimeClass(nameof(_default))] +%% %class %%, IEquatable<%> +{ +public %IntPtr ThisPtr => _default.ThisPtr; + +private readonly IObjectReference _inner = null; +private readonly Lazy<%> _defaultLazy; +% + +private % _default => _defaultLazy.Value; +% +public static %% FromAbi(IntPtr thisPtr) +{ +if (thisPtr == IntPtr.Zero) return null; +return MarshalInspectable<%>.FromAbi(thisPtr); +} + +% %(% ifc)% +{ +_defaultLazy = new Lazy<%>(() => ifc); +%} +% + +public static bool operator ==(% x, % y) => (x?.ThisPtr ?? IntPtr.Zero) == (y?.ThisPtr ?? IntPtr.Zero); +public static bool operator !=(% x, % y) => !(x == y); +% +% + +private struct InterfaceTag{}; + +private % AsInternal(InterfaceTag<%> _) => _default; +%% +} +)", + bind(type), + bind(type), + bind(type, false), + internal_accessibility(), + bind(type), + type_name, + bind(type, base_semantics, true, false), + type_name, + derived_new, + default_interface_abi_name, + bind(type), + default_interface_abi_name, bind(type), - // FromAbi derived_new, type_name, type_name, - // ObjectReference constructor type.Flags().Sealed() ? "internal" : "protected internal", type_name, - bind(base_semantics), - default_interface_name, - bind([&](writer& w) - { - if (is_manually_gen_default_interface) - { - w.write("_defaultLazy = new Lazy<%>(() => (%)new SingleInterfaceOptimizedObject(typeof(%), _inner));", default_interface_name, default_interface_name, default_interface_name); - } - }), - // Equality operators + default_interface_abi_name, + bind(base_semantics), + default_interface_abi_name, + [&](writer& w) + { + if (!gc_pressure_amount) return; + w.write("GC.AddMemoryPressure(%);\n", gc_pressure_amount); + }, + [&](writer& w) + { + if (!gc_pressure_amount) return; + w.write(R"(~%() +{ +GC.RemoveMemoryPressure(%); +} +)", + type_name, + gc_pressure_amount); + }, type_name, type_name, type_name, @@ -6579,7 +8599,204 @@ private struct InterfaceTag{}; }), bind([&](writer& w) { - if (is_fast_abi_class(type)) + bool has_base_type = !std::holds_alternative(get_type_semantics(type.Extends())); + + if (!type.Flags().Sealed()) + { + w.write(R"( +protected %(global::WinRT.DerivedComposed _)% +{ +_defaultLazy = new Lazy<%>(() => GetDefaultReference<%.Vftbl>()); +})", + type.TypeName(), + has_base_type ? ":base(_)" : "", + default_interface_abi_name, + default_interface_abi_name); + } + + std::string_view access_spec = "protected "; + std::string_view override_spec = has_base_type ? "override " : "virtual "; + + if (type.Flags().Sealed() && !has_base_type) + { + access_spec = "private "; + override_spec = " "; + } + + w.write(R"( +%%IObjectReference GetDefaultReference() => _default.AsInterface();)", + access_spec, + override_spec); + + w.write(R"( +%%IObjectReference GetReferenceForQI() => _inner ?? _default.ObjRef;)", + access_spec, + override_spec); + }), + default_interface_name, + default_interface_name, + bind(type, false, false), + bind(type)); + } + + + void write_class(writer& w, TypeDef const& type) + { + writer::write_platform_guard guard{ w }; + + if (settings.component) + { + write_wrapper_class(w, type); + return; + } + + if (is_static(type)) + { + write_static_class(w, type); + return; + } + + auto type_namespace = type.TypeNamespace(); + auto type_name = write_type_name_temp(w, type); + auto default_interface_name = get_default_interface_name(w, type, false); + auto base_semantics = get_type_semantics(type.Extends()); + auto derived_new = std::holds_alternative(base_semantics) ? "" : "new "; + + auto gc_pressure_amount = get_gc_pressure_amount(type); + + auto default_interface_typedef = for_typedef(w, get_type_semantics(get_default_interface(type)), [&](auto&& iface) { return iface; }); + auto is_manually_gen_default_interface = is_manually_generated_iface(default_interface_typedef); + + w.write(R"(% +% +[global::ABI.%.%RcwFactory] +[global::WinRT.ProjectedRuntimeClass(typeof(%))] +%% %class %%, IWinRTObject, IEquatable<%> +{ +private IntPtr ThisPtr => _inner == null ? (((IWinRTObject)this).NativeObject).ThisPtr : _inner.ThisPtr; + +private readonly IObjectReference _inner = null; +% +% + +% +% +% +public static %% FromAbi(IntPtr thisPtr) +{ +if (thisPtr == IntPtr.Zero) return null; +return MarshalInspectable<%>.FromAbi(thisPtr); +} + +% %(IObjectReference objRef)% +{ +_inner = objRef.As(%.IID); +% +%} +% + +public static bool operator ==(% x, % y) => (x?.ThisPtr ?? IntPtr.Zero) == (y?.ThisPtr ?? IntPtr.Zero); +public static bool operator !=(% x, % y) => !(x == y); +% +% + +private struct InterfaceTag{}; +% +%% +} +)", + bind(type), + bind(type), + type_namespace, + type.TypeName(), + default_interface_name, + bind(type, true), + internal_accessibility(), + bind(type), + type_name, + bind(type, base_semantics, true, false), + type_name, + bind(type), + bind([&](writer& w) + { + if (is_manually_gen_default_interface) + { + w.write("private readonly Lazy<%> _defaultLazy;", default_interface_name); + } + }), + bind(type, type.Flags().Sealed()), + bind([&](writer& w) + { + if (is_manually_gen_default_interface) + { + w.write("private % _default => %;", default_interface_name, "_defaultLazy.Value"); + } + }), + bind(type), + // FromAbi + derived_new, + type_name, + type_name, + // ObjectReference constructor + type.Flags().Sealed() ? "internal" : "protected internal", + type_name, + bind(base_semantics), + bind(get_type_semantics(get_default_interface(type)), typedef_name_type::StaticAbiClass, true), + bind([&](writer& w) + { + if (is_manually_gen_default_interface) + { + w.write("_defaultLazy = new Lazy<%>(() => (%)new SingleInterfaceOptimizedObject(typeof(%), _inner));", default_interface_name, default_interface_name, default_interface_name); + } + }), + [&](writer& w) + { + if (!gc_pressure_amount) return; + w.write("GC.AddMemoryPressure(%);\n", gc_pressure_amount); + }, + [&](writer& w) + { + if (!gc_pressure_amount) return; + w.write(R"(~%() +{ +GC.RemoveMemoryPressure(%); +} +)", + type_name, + gc_pressure_amount); + }, + // Equality operators + type_name, + type_name, + type_name, + type_name, + bind([&](writer& w) + { + bool return_type_matches = false; + if (!has_class_equals_method(type, &return_type_matches)) + { + w.write("public bool Equals(% other) => this == other;\n", type_name); + } + // Even though there is an equals method defined, it doesn't match the signature for IEquatable + // so we define an explicitly implemented one. + else if (!return_type_matches) + { + w.write("bool IEquatable<%>.Equals(% other) => this == other;\n", type_name, type_name); + } + + if (!has_object_equals_method(type)) + { + w.write("public override bool Equals(object obj) => obj is % that && this == that;\n", type_name); + } + + if (!has_object_hashcode_method(type)) + { + w.write("public override int GetHashCode() => ThisPtr.GetHashCode();\n"); + } + }), + bind([&](writer& w) + { + if (is_fast_abi_class(type)) { int hierarchy_index = get_class_hierarchy_index(type); if (!type.Flags().Sealed() || hierarchy_index > 0) @@ -6660,10 +8877,37 @@ global::System.Collections.Concurrent.ConcurrentDictionary _) => _default;", default_interface_name, default_interface_name); } }), - bind(type, false), + bind(type, false, false), bind(type)); } + void write_winrt_implementation_type_rcw_factory_attribute_type(writer& w, TypeDef const& type) + { + if (settings.netstandard_compat) + { + return; + } + + if (settings.component) + { + return; + } + + if (is_static(type)) + { + return; + } + + w.write(R"([global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] +internal sealed class %RcwFactoryAttribute : global::WinRT.WinRTImplementationTypeRcwFactoryAttribute +{ + public override object CreateInstance(global::WinRT.IInspectable inspectable) + => new %(inspectable.ObjRef); +} +)", type.TypeName(), + bind(type, typedef_name_type::Projected, false)); + } + void write_abi_class(writer& w, TypeDef const& type) { if (is_static(type)) @@ -6686,6 +8930,7 @@ public static IntPtr FromManaged(% obj) => obj is null ? IntPtr.Zero : CreateMar public static unsafe MarshalInterfaceHelper<%>.MarshalerArray CreateMarshalerArray(%[] array) => MarshalInterfaceHelper<%>.CreateMarshalerArray2(array, (o) => CreateMarshaler2(o)); public static (int length, IntPtr data) GetAbiArray(object box) => MarshalInterfaceHelper<%>.GetAbiArray(box); public static unsafe %[] FromAbiArray(object box) => MarshalInterfaceHelper<%>.FromAbiArray(box, FromAbi); +public static void CopyAbiArray(%[] array, object box) => MarshalInterfaceHelper<%>.CopyAbiArray(array, box, FromAbi); public static (int length, IntPtr data) FromManagedArray(%[] array) => MarshalInterfaceHelper<%>.FromManagedArray(array, (o) => FromManaged(o)); public static void DisposeMarshaler(IObjectReference value) => MarshalInspectable.DisposeMarshaler(value); public static void DisposeMarshalerArray(MarshalInterfaceHelper<%>.MarshalerArray array) => MarshalInterfaceHelper<%>.DisposeMarshalerArray(array); @@ -6702,13 +8947,21 @@ public static unsafe void DisposeAbiArray(object box) => MarshalInspectable 0; + std::string iid; + if (settings.netstandard_compat) { - auto is_generic = distance(type.GenericParam()) > 0; - auto default_interface_abi_name = get_default_interface_name(w, type, true); - auto iid = is_generic ? w.write_temp("GuidGenerator.GetIID(%.Vftbl)", default_interface_abi_name) + iid = is_generic ? w.write_temp("GuidGenerator.GetIID(%.Vftbl)", default_interface_abi_name) : w.write_temp("GuidGenerator.GetIID(typeof(%).GetHelperType())", bind(get_type_semantics(get_default_interface(type)), typedef_name_type::CCW, false)); + } + else + { + iid = w.write_temp("%.IID", bind(get_type_semantics(get_default_interface(type)), typedef_name_type::StaticAbiClass, true)); + } + if (is_exclusive_to_default) + { w.write(R"( public static IObjectReference CreateMarshaler(% obj) => obj is null ? null : MarshalInspectable<%>.CreateMarshaler<%>(obj, %); public static ObjectReferenceValue CreateMarshaler2(% obj) => MarshalInspectable.CreateMarshaler2(obj, %);)", @@ -6724,12 +8977,12 @@ public static ObjectReferenceValue CreateMarshaler2(% obj) => MarshalInspectable auto default_interface_name = get_default_interface_name(w, type, false); w.write(R"( public static IObjectReference CreateMarshaler(% obj) => obj is null ? null : MarshalInterface<%>.CreateMarshaler(obj); -public static ObjectReferenceValue CreateMarshaler2(% obj) => MarshalInterface<%>.CreateMarshaler2(obj, GuidGenerator.GetIID(typeof(%).GetHelperType()));)", +public static ObjectReferenceValue CreateMarshaler2(% obj) => MarshalInterface<%>.CreateMarshaler2(obj, %);)", projected_type_name, default_interface_name, projected_type_name, default_interface_name, - default_interface_name); + iid); } }), projected_type_name, @@ -6744,6 +8997,8 @@ public static ObjectReferenceValue CreateMarshaler2(% obj) => MarshalInterface<% projected_type_name, projected_type_name, projected_type_name, + projected_type_name, + projected_type_name, projected_type_name); } @@ -6756,10 +9011,11 @@ public static ObjectReferenceValue CreateMarshaler2(% obj) => MarshalInterface<% } method_signature signature{ get_delegate_invoke(type) }; - w.write(R"(%%%% delegate % %(%); + w.write(R"(%%%%% delegate % %(%); )", bind(type), bind(type), + bind(type, false), bind(type, false), internal_accessibility(), bind(signature), @@ -6778,31 +9034,69 @@ public static ObjectReferenceValue CreateMarshaler2(% obj) => MarshalInterface<% bool have_generic_params = std::find_if(generic_abi_types.begin(), generic_abi_types.end(), [](auto&& pair){ return !pair.second.empty(); }) != generic_abi_types.end(); + auto write_delegate = [&](writer& w, bool is_abi_helper_method) + { + std::string invoke; + if (settings.netstandard_compat) + { + invoke = w.write_temp(R"( +global::WinRT.ComWrappersSupport.MarshalDelegateInvoke(%, (% invoke) => +{ +% +}))", + !is_abi_helper_method && have_generic_params ? "new IntPtr(thisPtr)" : "thisPtr", + type_name, + bind([&](writer& w) + { + if (signature.return_signature()) + { + w.write("return invoke(%);", "%"); + } + else + { + w.write("invoke(%);"); + } + })); + } + else + { + invoke = w.write_temp(R"( +global::WinRT.ComWrappersSupport.FindObject<%>(%).Invoke(%) +)", + type_name, + !is_abi_helper_method && have_generic_params ? "new IntPtr(thisPtr)" : "thisPtr", + "%"); + } + + if (is_abi_helper_method) + { + w.write(invoke, bind_list(", ", signature.params())); + w.write(";"); + } + else + { + write_managed_method_call(w, signature, invoke); + } + }; + w.write(R"([global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)] % % static class @% {% % -private static readonly global::WinRT.Interop.IDelegateVftbl AbiToProjectionVftable; -public static readonly IntPtr AbiToProjectionVftablePtr; +% +public static %IntPtr AbiToProjectionVftablePtr; static unsafe @() -{% -AbiToProjectionVftable = new global::WinRT.Interop.IDelegateVftbl { -IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, -Invoke = % -}; -var nativeVftbl = ComWrappersSupport.AllocateVtableMemory(typeof(@%), Marshal.SizeOf()); -Marshal.StructureToPtr(AbiToProjectionVftable, nativeVftbl, false); -AbiToProjectionVftablePtr = nativeVftbl; +% } % public static unsafe IObjectReference CreateMarshaler(% managedDelegate) => -managedDelegate is null ? null : MarshalDelegate.CreateMarshaler(managedDelegate, GuidGenerator.GetIID(typeof(@%))); +managedDelegate is null ? null : MarshalDelegate.CreateMarshaler(managedDelegate, IID); public static unsafe ObjectReferenceValue CreateMarshaler2(% managedDelegate) => -MarshalDelegate.CreateMarshaler2(managedDelegate, GuidGenerator.GetIID(typeof(@%))); +MarshalDelegate.CreateMarshaler2(managedDelegate, IID); public static IntPtr GetAbi(IObjectReference value) => MarshalInterfaceHelper<%>.GetAbi(value); @@ -6813,19 +9107,19 @@ return MarshalDelegate.FromAbi<%>(nativeDelegate); public static % CreateRcw(IntPtr ptr) { -return new %(new NativeDelegateWrapper(ComWrappersSupport.GetObjectReferenceForInterface(ptr, GuidGenerator.GetIID(typeof(@%)))).Invoke); +return new %(new NativeDelegateWrapper(ComWrappersSupport.GetObjectReferenceForInterface(ptr, IID)).Invoke); } -[global::WinRT.ObjectReferenceWrapper(nameof(_nativeDelegate))] #if !NET +[global::WinRT.ObjectReferenceWrapper(nameof(_nativeDelegate))] private sealed class NativeDelegateWrapper #else private sealed class NativeDelegateWrapper : IWinRTObject #endif { -private readonly ObjectReference _nativeDelegate; +private readonly ObjectReference _nativeDelegate; -public NativeDelegateWrapper(ObjectReference nativeDelegate) +public NativeDelegateWrapper(ObjectReference nativeDelegate) { _nativeDelegate = nativeDelegate; } @@ -6850,9 +9144,9 @@ global::System.Collections.Concurrent.ConcurrentDictionary CreateMarshaler2(managedD public static void DisposeMarshaler(IObjectReference value) => MarshalInterfaceHelper<%>.DisposeMarshaler(value); public static void DisposeAbi(IntPtr abi) => MarshalInterfaceHelper<%>.DisposeAbi(abi); + +public static unsafe MarshalInterfaceHelper<%>.MarshalerArray CreateMarshalerArray(%[] array) => MarshalInterfaceHelper<%>.CreateMarshalerArray2(array, (o) => CreateMarshaler2(o)); +public static (int length, IntPtr data) GetAbiArray(object box) => MarshalInterfaceHelper<%>.GetAbiArray(box); +public static unsafe %[] FromAbiArray(object box) => MarshalInterfaceHelper<%>.FromAbiArray(box, FromAbi); +public static void CopyAbiArray(%[] array, object box) => MarshalInterfaceHelper<%>.CopyAbiArray(array, box, FromAbi); +public static (int length, IntPtr data) FromManagedArray(%[] array) => MarshalInterfaceHelper<%>.FromManagedArray(array, (o) => FromManaged(o)); +public static void DisposeMarshalerArray(MarshalInterfaceHelper<%>.MarshalerArray array) => MarshalInterfaceHelper<%>.DisposeMarshalerArray(array); +public static unsafe void DisposeAbiArray(object box) => MarshalInspectable.DisposeAbiArray(box); + % private static unsafe int Do_Abi_Invoke% { @@ -6874,78 +9177,213 @@ private static unsafe int Do_Abi_Invoke% type.TypeName(), type_params, [&](writer& w) { - if (type_params.empty()) return; - w.write(R"( -public static Guid PIID = GuidGenerator.CreateIID(typeof(%));)", - type_name - ); + if (!type_params.empty()) + { + // Generating both PIID and IID for backcompat consistency and to have a common property to rely on + // similar to what we do in the manual projections. + w.write(R"( +public static readonly global::System.Guid PIID = GuidGenerator.CreateIID(typeof(%)); +public static global::System.Guid IID => PIID; +)", + type_name); + } + else + { + if (settings.netstandard_compat) + { + w.write(R"( +public static global::System.Guid IID{ get; } = new Guid(new byte[]{ % }); +)", + bind(type)); + } + else + { + w.write(R"( +public static ref readonly global::System.Guid IID +{ +[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] +get +{ +global::System.ReadOnlySpan data = new byte[] { % }; +return ref global::System.Runtime.CompilerServices.Unsafe.As(ref global::System.Runtime.InteropServices.MemoryMarshal.GetReference(data)); +} +} +)", + bind(type)); + } + } }, [&](writer& w) { - if (!is_generic) + if (!have_generic_params) { - if (settings.netstandard_compat) + // For invoke handlers that don't have generic params but within a generic handler, + // we handle them separately via its own delegate class. + if (!is_generic && settings.netstandard_compat) { w.write("private unsafe delegate int Abi_Invoke(%);\n", bind(signature)); } return; } - w.write(R"(private static readonly Type Abi_Invoke_Type = Expression.GetDelegateType(new Type[] { typeof(void*), %typeof(int) }); -)", - bind_each([&](writer& w, auto&& pair) - { - w.write("%, ", pair.first); - }, generic_abi_types)); + w.write("private static readonly Type Abi_Invoke_Type;"); }, + settings.netstandard_compat ? "private static readonly global::WinRT.Interop.IDelegateVftbl AbiToProjectionVftable;" : "", + settings.netstandard_compat || !is_generic ? "readonly " : "", // class constructor type.TypeName(), - [&](writer& w) { - if (!is_generic) + [&](writer& w) + { + if (settings.netstandard_compat) { - if (settings.netstandard_compat) + if (!is_generic) { w.write("\nAbiInvokeDelegate = new Abi_Invoke(Do_Abi_Invoke);"); } - return; - } - w.write("\nAbiInvokeDelegate = global::System.Delegate.CreateDelegate(Abi_Invoke_Type, typeof(@%).GetMethod(nameof(Do_Abi_Invoke), BindingFlags.Static | BindingFlags.NonPublic)%);", - type.TypeName(), - type_params, - [&](writer& w) { - if (!have_generic_params) return; - w.write(".MakeGenericMethod(new Type[]{ % })\n", + else if (!have_generic_params) + { + w.write("\nAbiInvokeDelegate = new @_Delegates.Invoke(Do_Abi_Invoke);", + type.TypeName()); + } + else + { + w.write(R"(Abi_Invoke_Type = global::WinRT.Projections.GetAbiDelegateType(new Type[] { typeof(void*), %typeof(int) }); +)", + bind_each([&](writer& w, auto&& pair) + { + w.write("%, ", pair.first); + }, generic_abi_types)); + + w.write("\nAbiInvokeDelegate = global::System.Delegate.CreateDelegate(Abi_Invoke_Type, typeof(@%).GetMethod(nameof(Do_Abi_Invoke), BindingFlags.Static | BindingFlags.NonPublic)%);", + type.TypeName(), + type_params, [&](writer& w) { - int count = 0; - for (auto&& pair : generic_abi_types) - { - if (pair.second.empty()) continue; - w.write(count++ == 0 ? "" : ", "); - w.write(pair.first); - } + if (!have_generic_params) return; + w.write(".MakeGenericMethod(new Type[]{ % })\n", + [&](writer& w) { + int count = 0; + for (auto&& pair : generic_abi_types) + { + if (pair.second.empty()) continue; + w.write(count++ == 0 ? "" : ", "); + w.write(pair.first); + } + }); }); - }); - }, - bind([&](writer& w) - { - if (settings.netstandard_compat || is_generic) + } + + w.write(R"( +AbiToProjectionVftable = new global::WinRT.Interop.IDelegateVftbl +{ +IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, +Invoke = Marshal.GetFunctionPointerForDelegate(AbiInvokeDelegate) +}; +var nativeVftbl = ComWrappersSupport.AllocateVtableMemory(typeof(@%), sizeof(IntPtr) * 4); +Marshal.StructureToPtr(AbiToProjectionVftable, nativeVftbl, false); +AbiToProjectionVftablePtr = nativeVftbl; +)", + type.TypeName(), + type_params); + } + else if (!is_generic) { - w.write("Marshal.GetFunctionPointerForDelegate(AbiInvokeDelegate)"); + w.write(R"( +AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(@), sizeof(IntPtr) * 4); +*(global::WinRT.Interop.IUnknownVftbl*)AbiToProjectionVftablePtr = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl; +((delegate* unmanaged[Stdcall]<%, int>*)AbiToProjectionVftablePtr)[3] = &Do_Abi_Invoke; +)", + type.TypeName(), + bind(signature)); } else { - w.write("(IntPtr)(delegate* unmanaged[Stdcall]<%, int>)&Do_Abi_Invoke", bind(signature)); + w.write(R"( +if (!RuntimeFeature.IsDynamicCodeCompiled) +{ + AbiToProjectionVftablePtr = %.AbiToProjectionVftablePtr; +} +else +{ +#if NET8_0_OR_GREATER + [RequiresDynamicCode("Generic instantiations might not be available in AOT scenarios.")] +#endif + [MethodImpl(MethodImplOptions.NoInlining)] + static global::System.Delegate InitializeAbiToProjectionVftablePtrFallback(%) + { +% + +global::System.Delegate abiInvokeDelegate = null; +if (%.AbiToProjectionVftablePtr == default) +{ + abiInvokeDelegate = %; + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(@%), sizeof(IntPtr) * 4); + *(global::WinRT.Interop.IUnknownVftbl*)AbiToProjectionVftablePtr = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl; + ((IntPtr*)AbiToProjectionVftablePtr)[3] = Marshal.GetFunctionPointerForDelegate(abiInvokeDelegate); +} +else +{ + AbiToProjectionVftablePtr = %.AbiToProjectionVftablePtr; +} +return abiInvokeDelegate; + } + + AbiInvokeDelegate = InitializeAbiToProjectionVftablePtrFallback(%); +} +)", + bind(type, typedef_name_type::StaticAbiClass, false), + !have_generic_params ? "" : "ref Type abiInvokeType", + !have_generic_params ? "" : + w.write_temp(R"( +if (%.AbiToProjectionVftablePtr == default || %._Invoke == default) +{ +abiInvokeType = Expression.GetDelegateType(new Type[] { typeof(void*), %typeof(int) }); +} +)", + bind(type, typedef_name_type::StaticAbiClass, false), + bind(type, typedef_name_type::StaticAbiClass, false), + bind_each([&](writer& w, auto&& pair) + { + w.write("%, ", pair.first); + }, generic_abi_types)), + bind(type, typedef_name_type::StaticAbiClass, false), + [&](writer& w) + { + if (have_generic_params) + { + w.write("global::System.Delegate.CreateDelegate(Abi_Invoke_Type, typeof(@%).GetMethod(nameof(Do_Abi_Invoke), BindingFlags.Static | BindingFlags.NonPublic)%)", + type.TypeName(), + type_params, + [&](writer& w) + { + if (!have_generic_params) return; + w.write(".MakeGenericMethod(new Type[]{ % })\n", + [&](writer& w) { + int count = 0; + for (auto&& pair : generic_abi_types) + { + if (pair.second.empty()) continue; + w.write(count++ == 0 ? "" : ", "); + w.write(pair.first); + } + }); + }); + } + else + { + w.write("new @_Delegates.Invoke(Do_Abi_Invoke)", type.TypeName()); + } + }, + type.TypeName(), + type_params, + bind(type, typedef_name_type::StaticAbiClass, false), + !have_generic_params ? "" : "ref Abi_Invoke_Type"); } - }), - type.TypeName(), - type_params, + + w.write("global::WinRT.ComWrappersSupport.RegisterDelegateFactory(typeof(%), CreateRcw);", bind(type, typedef_name_type::Projected, false)); + }, settings.netstandard_compat || is_generic ? "\npublic static global::System.Delegate AbiInvokeDelegate { get; }\n" : "", // CreateMarshaler type_name, - type.TypeName(), - type_params, type_name, - type.TypeName(), - type_params, // GetAbi type_name, // FromAbi @@ -6953,32 +9391,72 @@ public static Guid PIID = GuidGenerator.CreateIID(typeof(%));)", type_name, type_name, type_name, - type.TypeName(), - type_params, // NativeDelegateWrapper.Invoke bind(signature), bind_list(", ", signature.params()), + [&](writer& w) { + if (!settings.netstandard_compat && have_generic_params) + { + w.write(R"( +if (!RuntimeFeature.IsDynamicCodeCompiled) +{ + %._Invoke(_nativeDelegate, %); + return; +} + +if (%._Invoke != null) +{ +%._Invoke(_nativeDelegate, %); +} +else +)", + bind(type, typedef_name_type::StaticAbiClass, false), + bind_list(", ", signature.params()), + bind(type, typedef_name_type::StaticAbiClass, false), + bind(type, typedef_name_type::StaticAbiClass, false), + bind_list(", ", signature.params())); + } + }, bind([&](writer& w) { - if (is_generic || settings.netstandard_compat) + if (have_generic_params || settings.netstandard_compat) { - w.write("var abiInvoke = Marshal.GetDelegateForFunctionPointer%(_nativeDelegate.Vftbl.Invoke%);", - is_generic ? "" : "", - is_generic ? ", Abi_Invoke_Type" : ""); + if (have_generic_params) + { + w.write("var abiInvoke = Marshal.GetDelegateForFunctionPointer((IntPtr)(*(void***)_nativeDelegate.ThisPtr)[3], Abi_Invoke_Type);"); + } + else + { + w.write("var abiInvoke = Marshal.GetDelegateForFunctionPointer<%>((IntPtr)(*(void***)_nativeDelegate.ThisPtr)[3]);", + is_generic ? w.write_temp("@_Delegates.Invoke", type.TypeName()) : "Abi_Invoke"); + } } else { - w.write("var abiInvoke = (delegate* unmanaged[Stdcall]<%, int>)(_nativeDelegate.Vftbl.Invoke);", - bind(signature)); + w.write("var abiInvoke = (delegate* unmanaged[Stdcall]<%, int>)(*(void***)_nativeDelegate.ThisPtr)[3];", + bind(signature)); } }), - bind(signature, "abiInvoke", is_generic, false, is_noexcept(method)), + bind(signature, "abiInvoke", "_nativeDelegate", have_generic_params, false, is_noexcept(method), false), // FromManaged type_name, // DisposeMarshaler type_name, // DisposeAbi type_name, + // Array marshalers + type_name, + type_name, + type_name, + type_name, + type_name, + type_name, + type_name, + type_name, + type_name, + type_name, + type_name, + type_name, // Do_Abi_Invoke !is_generic && !settings.netstandard_compat ? "\n[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })]" : "", [&](writer& w) { @@ -6998,8 +9476,12 @@ public static Guid PIID = GuidGenerator.CreateIID(typeof(%));)", w.write(pair.second); } w.write(">"); + w.write("(void* thisPtr"); + } + else + { + w.write("(IntPtr thisPtr"); } - w.write("(void* thisPtr"); int index = 0; for (auto&& param : signature.params()) { @@ -7007,10 +9489,20 @@ public static Guid PIID = GuidGenerator.CreateIID(typeof(%));)", auto param_cat = get_param_category(param); if (!generic_type.empty() && (param_cat <= param_category::out)) { - w.write(", %% %", - param_cat == param_category::out ? "out " : "", - generic_type, - bind(param)); + if (settings.netstandard_compat) + { + w.write(", %% %", + param_cat == param_category::out ? "out " : "", + generic_type, + bind(param)); + } + else + { + w.write(", %% %", + generic_type, + param_cat == param_category::out ? "* " : "", + bind(param)); + } } else { @@ -7022,7 +9514,14 @@ public static Guid PIID = GuidGenerator.CreateIID(typeof(%));)", auto generic_type = generic_abi_types[index++].second; if (!return_sig.Type().is_szarray() && !generic_type.empty()) { - w.write(", out % %", generic_type, signature.return_param_name()); + if (settings.netstandard_compat) + { + w.write(", out % %", generic_type, signature.return_param_name()); + } + else + { + w.write(", %* %", generic_type, signature.return_param_name()); + } } else { @@ -7031,42 +9530,146 @@ public static Guid PIID = GuidGenerator.CreateIID(typeof(%));)", } w.write(")"); }, - [&](writer& w) { - if (settings.netstandard_compat) + bind(write_delegate, false)); + + if (is_generic) + { + if (!have_generic_params) { - write_managed_method_call(w, signature, - w.write_temp(R"( -global::WinRT.ComWrappersSupport.MarshalDelegateInvoke(%, (% invoke) => + w.write(R"( +internal static class @_Delegates { - % -}))", - is_generic ? "new IntPtr(thisPtr)" : "thisPtr", - type_name, - bind([&](writer& w) - { - if (signature.return_signature()) - { - w.write("return invoke(%);", "%"); - } - else - { - w.write("invoke(%);"); - } - }) - )); +public unsafe delegate int Invoke%; +} +)", + type.TypeName(), + bind(method)); } - else + + if (settings.netstandard_compat) { - write_managed_method_call(w, signature, + return; + } + + int index = 0; + std::string vtableAlloc = settings.netstandard_compat ? + "Marshal.AllocCoTaskMem((sizeof(IUnknownVftbl) + sizeof(IntPtr)))" : + "(IntPtr)NativeMemory.AllocZeroed((nuint)(sizeof(IUnknownVftbl) + sizeof(IntPtr)))"; + std::string vtableFree = settings.netstandard_compat ? + "Marshal.FreeCoTaskMem(abiToProjectionVftablePtr);" : + "NativeMemory.Free((void*)abiToProjectionVftablePtr);"; + + w.write(R"( +internal static class % +{ +% +private static IntPtr abiToProjectionVftablePtr; +internal static IntPtr AbiToProjectionVftablePtr => abiToProjectionVftablePtr; + +static @Methods() +{ +ComWrappersSupport.RegisterHelperType(typeof(%), typeof(%)); +} + +internal static bool TryInitCCWVtable(IntPtr ptr) +{ +bool success = global::System.Threading.Interlocked.CompareExchange(ref abiToProjectionVftablePtr, ptr, IntPtr.Zero) == IntPtr.Zero; +if (success) +{ +%.AbiToProjectionVftablePtr = abiToProjectionVftablePtr; +global::WinRT.ComWrappersSupport.RegisterComInterfaceEntries( +typeof(%), +global::WinRT.DelegateTypeDetails<%>.GetExposedInterfaces( +new ComWrappers.ComInterfaceEntry +{ +IID = %.PIID, +Vtable = abiToProjectionVftablePtr +} +) +); +} +return success; +} +} + +% static class %% +{ +% + +public static unsafe bool InitCcw(% invoke) +{ +if (%.AbiToProjectionVftablePtr != default) +{ +return false; +} + +var abiToProjectionVftablePtr = %; +*(IUnknownVftbl*)abiToProjectionVftablePtr = IUnknownVftbl.AbiToProjectionVftbl; +((%*)abiToProjectionVftablePtr)[3] = invoke; + +if (!%.TryInitCCWVtable(abiToProjectionVftablePtr)) +{ +% +return false; +} + +return true; +} + +public static % Abi_Invoke(IntPtr thisPtr%%) +{ +% +} +} +)", + bind(type, typedef_name_type::StaticAbiClass, false), + !have_generic_params ? "" : + w.write_temp("internal unsafe volatile static delegate* _Invoke;", + bind_list(", ", signature.params()), + signature.has_params() ? ", " : "", + bind(signature)), + type.TypeName(), + bind(type, typedef_name_type::Projected, false), + bind(type, typedef_name_type::ABI, false), + bind(type, typedef_name_type::ABI, false), + bind(type, typedef_name_type::Projected, false), + bind(type, typedef_name_type::Projected, false), + bind(type, typedef_name_type::ABI, false), + internal_accessibility(), + bind(type), + bind_each([&](writer& w, GenericParam const& /*gp*/) + { + w.write(" where "); + write_generic_type_name(w, index++); + w.write("Abi : unmanaged"); + }, type.GenericParam()), + !have_generic_params ? "" : w.write_temp(R"( -global::WinRT.ComWrappersSupport.FindObject<%>(%).Invoke(%) +public unsafe static bool InitRcwHelper(delegate* invoke) +{ +if (%._Invoke == null) +{ +%._Invoke = invoke; +} +return true; +} )", - type_name, - is_generic ? "new IntPtr(thisPtr)" : "thisPtr", - "%" - )); - } - }); + bind_list(", ", signature.params()), + signature.has_params() ? ", " : "", + bind(signature), + bind(type, typedef_name_type::StaticAbiClass, false), + bind(type, typedef_name_type::StaticAbiClass, false)), + bind(method), + bind(type, typedef_name_type::StaticAbiClass, false), + vtableAlloc, + bind(method), + bind(type, typedef_name_type::StaticAbiClass, false), + vtableFree, + bind(signature), + signature.has_params() ? ", " : "", + bind_list(", ", signature.params()), + bind(write_delegate, true)); + } } void write_constant(writer& w, Constant const& value) @@ -7082,667 +9685,1459 @@ global::WinRT.ComWrappersSupport.FindObject<%>(%).Invoke(%) } } - void write_enum(writer& w, TypeDef const& type) + void write_enum(writer& w, TypeDef const& type) + { + if (settings.component) + { + write_authoring_metadata_type(w, type); + return; + } + + if (is_flags_enum(type)) + { + w.write("[FlagsAttribute]\n"); + } + + auto enum_underlying_type = is_flags_enum(type) ? "uint" : "int"; + + w.write(R"(%%%% enum % : % +{ +)", + bind(type), + bind(type, false), + bind(type, true), + (settings.internal || settings.embedded) ? (settings.public_enums ? "public" : "internal") : "public", + bind(type, typedef_name_type::Projected, false), enum_underlying_type); + { + for (auto&& field : type.FieldList()) + { + if (auto constant = field.Constant()) + { + w.write("%% = unchecked((%)%),\n", + bind(field.CustomAttribute()), + field.Name(), enum_underlying_type, bind(constant)); + } + } + } + w.write("}\n"); + } + + void write_struct(writer& w, TypeDef const& type) + { + if (settings.component) + { + write_authoring_metadata_type(w, type); + return; + } + + auto name = w.write_temp("%", bind(type, typedef_name_type::Projected, false)); + + struct field_info + { + std::string type; + std::string name; + bool is_interface; + }; + std::vector fields; + for (auto&& field : type.FieldList()) + { + auto semantics = get_type_semantics(field.Signature().Type()); + field_info field_info{}; + field_info.type = w.write_temp("%", [&](writer& w){ write_projection_type(w, semantics); }); + field_info.name = field.Name(); + if (auto td = std::get_if(&semantics)) + { + field_info.is_interface = get_category(*td) == category::interface_type; + } + else if (auto gti = std::get_if(&semantics)) + { + field_info.is_interface = get_category(gti->generic_type) == category::interface_type; + } + fields.emplace_back(field_info); + } + + w.write(R"(%%%%% struct %: IEquatable<%> +{ +% +public %(%) +{ +% +} + +public static bool operator ==(% x, % y) => %; +public static bool operator !=(% x, % y) => !(x == y); +public bool Equals(% other) => this == other; +public override bool Equals(object obj) => obj is % that && this == that; +public override int GetHashCode() => %; +} +)", + // struct + bind(type), + bind(type), + bind(type, false), + bind(type, true), + internal_accessibility(), + name, + name, + bind_each([](writer& w, auto&& field) + { + w.write("public % %;\n", field.type, field.name); + }, fields), + // ctor + name, + bind_list([](writer& w, auto&& field) + { + w.write("% _%", field.type, field.name); + }, ", ", fields), + bind_each([](writer& w, auto&& field) + { + w.write("% = _%; ", field.name, field.name); + }, fields), + // == + name, + name, + bind_list([](writer& w, auto&& field) + { + w.write("x.% == y.%", + field.name, field.name); + }, " && ", fields), + // !=, Equals + name, + name, + name, + name, + // GetHashCode + bind_list([](writer& w, auto&& field) + { + w.write("%.GetHashCode()", field.name); + }, " ^ ", fields) + ); + } + + void write_abi_struct(writer& w, TypeDef const& type) + { + if (is_type_blittable(type)) + { + return; + } + + w.write("[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]\n% struct %\n{\n", + internal_accessibility(), + bind(type, typedef_name_type::ABI, false)); + + for (auto&& field : type.FieldList()) + { + w.write("public "); + write_abi_type(w, get_type_semantics(field.Signature().Type())); + w.write(" %;\n", field.Name()); + } + + auto projected_type = w.write_temp("%", bind(type)); + auto abi_type = w.write_temp("%", bind(type, typedef_name_type::ABI, false)); + + std::vector marshalers; + for (auto&& field : type.FieldList()) + { + abi_marshaler m{ std::string(field.Name()), -1 }; + set_abi_marshaler(w, field.Signature().Type(), m); + marshalers.push_back(std::move(m)); + } + + // blittable: (no marshaler) value type requiring no marshaling/disposing + // marshalable: (marshaler, is_value_type) value type requiring only marshaling, no disposing + // disposable: (marshaler, !is_value_type) ref type requiring marshaling and disposing + bool have_disposers = std::find_if(marshalers.begin(), marshalers.end(), [](abi_marshaler const& m) + { + return !m.is_value_type; + }) != marshalers.end(); + + w.write(R"( +public struct Marshaler +{ +%public % __abi; +)", + bind_each([](writer& w, abi_marshaler const& m) + { + if (m.marshaler_type.empty()) return; + w.write("public % _%;\n", m.local_type, m.param_name); + }, marshalers), + abi_type); + if (have_disposers) + { + w.write(R"(public void Dispose() +{ +%} +)", + bind_each([](writer& w, abi_marshaler const& m) + { + if (m.is_value_type) return; + w.write("%.DisposeMarshaler(_%);\n", + m.is_marshal_by_object_reference_value() ? "MarshalInspectable" : m.marshaler_type, + m.param_name); + }, marshalers)); + } + w.write("}\n"); + + w.write(R"( +public static Marshaler CreateMarshaler(% arg) +{ +var m = new Marshaler();)", + projected_type); + if (have_disposers) + { + w.write(R"( +bool success = false; +try +{)"); + } + for (auto&& m : marshalers) + { + if (m.marshaler_type.empty()) continue; + w.write("\nm._% = ", m.param_name); + m.write_create(w, "arg." + m.get_escaped_param_name(w)); + w.write(";"); + } + w.write(R"( +m.__abi = new %() +{ +%}; +% +return m;)", + abi_type, + [&](writer& w) + { + int count = 0; + for (auto&& m : marshalers) + { + w.write(count++ == 0 ? "" : ", "); + if (m.marshaler_type.empty()) + { + std::string format; + if (m.param_type == "bool") + { + format = "% = (byte)(arg.% ? 1 : 0)\n"; + } + else if (m.param_type == "char") + { + format = "% = (ushort)arg.%\n"; + } + else + { + format = "% = arg.%\n"; + } + w.write(format, + m.get_escaped_param_name(w), + m.get_escaped_param_name(w)); + continue; + } + w.write("% = %.GetAbi(m._%)\n", + m.get_escaped_param_name(w), + m.is_marshal_by_object_reference_value() ? "MarshalInspectable" : m.marshaler_type, + m.param_name); + } + }, + have_disposers ? "success = true;" : ""); + if (have_disposers) + { + w.write(R"( +} +finally +{ +if (!success) +{ +m.Dispose(); +} +} +)"); + } + w.write("}\n"); + + w.write(R"( +public static % GetAbi(Marshaler m) => m.__abi; +)", + abi_type); + + w.write(R"( +public static % FromAbi(% arg) +{ +return new %() +{ +%}; +} +)", + projected_type, + abi_type, + projected_type, + [&](writer& w) + { + int count = 0; + for (auto&& m : marshalers) + { + w.write(count++ == 0 ? "" : ", "); + if (m.marshaler_type.empty()) + { + std::string format; + if (m.param_type == "bool") + { + format = "% = arg.% != 0\n"; + } + else if (m.param_type == "char") + { + format = "% = (char)arg.%\n"; + } + else + { + format = "% = arg.%\n"; + } + w.write(format, + m.get_escaped_param_name(w), + m.get_escaped_param_name(w)); + continue; + } + w.write("% = %\n", + m.get_escaped_param_name(w), + [&](writer& w) {m.write_from_abi(w, "arg." + m.get_escaped_param_name(w)); }); + } + }); + + w.write(R"( +public static % FromManaged(% arg) +{ +return new %() +{ +%}; +} +)", + abi_type, + projected_type, + abi_type, + [&](writer& w) + { + int count = 0; + for (auto&& m : marshalers) + { + w.write(count++ == 0 ? "" : ", "); + if (m.marshaler_type.empty()) + { + std::string format; + if (m.param_type == "bool") + { + format = "% = (byte)(arg.% ? 1 : 0)\n"; + } + else if (m.param_type == "char") + { + format = "% = (ushort)arg.%\n"; + } + else + { + format = "% = arg.%\n"; + } + w.write(format, + m.get_escaped_param_name(w), + m.get_escaped_param_name(w)); + continue; + } + w.write("% = %\n", + m.get_escaped_param_name(w), [&](writer& w) { + m.write_from_managed(w, "arg." + m.get_escaped_param_name(w)); }); + } + }); + + w.write(R"( +public static unsafe void CopyAbi(Marshaler arg, IntPtr dest) => + *(%*)dest.ToPointer() = GetAbi(arg); +)", + abi_type); + + w.write(R"( +public static unsafe void CopyManaged(% arg, IntPtr dest) => + *(%*)dest.ToPointer() = FromManaged(arg); +)", + projected_type, + abi_type); + + w.write(R"( +public static void DisposeMarshaler(Marshaler m) % +)", + have_disposers ? "=> m.Dispose();" : "{}"); + + w.write(R"( +public static void DisposeAbi(% abi) +{ +%} +} +)", + abi_type, + bind_each([](writer& w, abi_marshaler const& m) + { + if (m.is_value_type) return; + w.write("%.DisposeAbi(abi.%);\n", + m.marshaler_type, + m.param_name); + }, marshalers)); + } + + void write_factory_class_inheritance(writer& w, TypeDef const& type) + { + auto delimiter{ ", " }; + auto write_delimiter = [&]() + { + w.write(delimiter); + }; + + for (auto&& [interface_name, factory] : get_attributed_types(w, type)) + { + if ((factory.activatable || factory.statics) && factory.type) + { + write_delimiter(); + w.write("%", bind(factory.type, typedef_name_type::CCW, false)); + } + } + } + + void write_factory_activatable_method(writer& w, MethodDef const& method, std::string_view activatable_type) + { + method_signature signature{ method }; + w.write(R"( +public % %(%) => new %(%); +)", +activatable_type, +method.Name(), +bind_list(", ", signature.params()), +activatable_type, +bind_list(", ", signature.params()) +); + } + + void write_factory_class_members(writer& w, TypeDef const& type) + { + auto delimiter{ ", " }; + auto write_delimiter = [&]() + { + w.write(delimiter); + }; + + auto projected_type_name = write_type_name_temp(w, type, "%", typedef_name_type::Projected); + for (auto&& [interface_name, factory] : get_attributed_types(w, type)) + { + if (factory.type) + { + if (factory.activatable) + { + w.write_each(factory.type.MethodList(), projected_type_name); + } + else if (factory.statics) + { + w.write_each(factory.type.MethodList(), projected_type_name, ""sv); + w.write_each(factory.type.PropertyList(), projected_type_name, ""sv); + w.write_each(factory.type.EventList(), projected_type_name, ""sv); + } + } + } + } + + + void write_factory_class(writer& w, TypeDef const& type) + { + auto factory_type_name = write_type_name_temp(w, type, "%ServerActivationFactory", typedef_name_type::CCW); + auto is_activatable = !is_static(type) && has_default_constructor(type); + auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::Projected); + + // If the type is activatable, we implement IActivationFactory by creating an + // instance and marshalling it to IntPtr. Otherwise, we just throw an exception. + auto activate_instance_body = is_activatable + ? w.write_temp(R"(% comp = new %(); + + return MarshalInspectable<%>.FromManaged(comp);)", type_name, type_name, type_name) + : "throw new NotImplementedException();"; + + w.write(R"( +% +internal sealed class % : global::WinRT.Interop.IActivationFactory% +{ + +static %() +{ +RuntimeHelpers.RunClassConstructor(typeof(%).TypeHandle); +} + +public static IntPtr Make() +{ + return MarshalInspectable<%>.CreateMarshaler2(_factory, IID.IID_IActivationFactory).Detach(); +} + +static readonly % _factory = new %(); + +public IntPtr ActivateInstance() +{ + % +} + +% +} +)", +bind(type, true), +factory_type_name, +bind(type), +factory_type_name, +type_name, +factory_type_name, +factory_type_name, +factory_type_name, +activate_instance_body, +bind(type) +); + } + + void write_module_activation_factory(writer& w, std::set const& types) + { + w.write(R"( +using System; +namespace WinRT +{ +% static partial class Module +{ +public static unsafe IntPtr GetActivationFactory(% runtimeClassId) +{ +switch (runtimeClassId) +{ +% +default: + return %; +} +} +% +% +} +} +)", + internal_accessibility(), + // We want to leverage C# support for switching on a ReadOnlySpan, which allows the exported + // 'DllGetActivationFactory' method to avoid the temporary allocation of the string instance for the + // input runtime class name. Because that's a C# 11 feature, we only enable this on .NET 7 and above. + // If that's the TFM in use, we generate this method using ReadOnlySpan, and then a string overload + // just calling it. Otherwise, we switch on the string parameter, and generate a dummy ReadOnlySpan + // overload just allocating and calling that. We always need both exports to make sure that hosting + // scenarios also work, since those will be looking up the string overload via reflection. + settings.netstandard_compat ? "string" : "ReadOnlySpan", +bind_each([](writer& w, TypeDef const& type) + { + w.write(R"( +case "%.%": + return %ServerActivationFactory.Make(); +)", +type.TypeNamespace(), +type.TypeName(), +bind(type, typedef_name_type::CCW, true) +); + }, + types + ), + settings.partial_factory ? "GetActivationFactoryPartial(runtimeClassId)" : "IntPtr.Zero", + settings.netstandard_compat ? R"( +public static IntPtr GetActivationFactory(ReadOnlySpan runtimeClassId) +{ + return GetActivationFactory(runtimeClassId.ToString()); +} +)" : R"( +public static IntPtr GetActivationFactory(string runtimeClassId) +{ + return GetActivationFactory(runtimeClassId.AsSpan()); +} +)", +bind([&](writer& w) { + if (settings.partial_factory) + { + if (!settings.netstandard_compat) + { + w.write("private static partial IntPtr GetActivationFactoryPartial(ReadOnlySpan runtimeClassId);"); + } + else + { + w.write("private static partial IntPtr GetActivationFactoryPartial(string runtimeClassId);"); + } + } + }) +); + } + + void write_event_source_generic_args(writer& w, cswinrt::type_semantics eventTypeSemantics) + { + if (!std::holds_alternative(eventTypeSemantics)) + { + return; + } + for_typedef(w, eventTypeSemantics, [&](TypeDef const&) + { + std::vector genericArgs; + auto arg_count = std::get(eventTypeSemantics).generic_args.size(); + for (int i = 0; i < (int) arg_count; ++i ) + { + auto semantics = w.get_generic_arg_scope(i).first; + if (std::holds_alternative(semantics)) + { + genericArgs.push_back(w.write_temp("%", bind(i))); + } + } + if (genericArgs.size() == 0) + { + return; + } + w.write("<%>", bind_list([](writer& w, auto&& value) + { + w.write(value); + }, ", "sv, genericArgs)); + }); + } + + void write_event_source_subclass(writer& w, cswinrt::type_semantics eventTypeSemantics) + { + auto genericInstantiationInitialization = w.write_temp("%", bind(eventTypeSemantics, true)); + + auto abiTypeName = w.write_temp("%", bind(eventTypeSemantics, typedef_name_type::ABI, true)); + for_typedef(w, eventTypeSemantics, [&](TypeDef const& eventType) + { + if ((eventType.TypeNamespace() == "Windows.Foundation" || eventType.TypeNamespace() == "System") && eventType.TypeName() == "EventHandler`1") + { + return; + } + + auto eventTypeCode = w.write_temp("%", bind(eventType, typedef_name_type::Projected, false)); + auto invokeMethodSig = get_event_invoke_method_signature(eventType); + w.write(R"( +internal sealed unsafe class %% : global::ABI.WinRT.Interop.EventSource<%> +{ +% + +internal %(IObjectReference obj, int vtableIndexForAddHandler) : base(obj, vtableIndexForAddHandler) +{ +% +} + +protected override ObjectReferenceValue CreateMarshaler(% handler) => +%.CreateMarshaler2(handler); + +protected override global::ABI.WinRT.Interop.EventSourceState<%> CreateEventSourceState() => +new EventState(ObjectReference.ThisPtr, Index); + +% +private sealed class EventState : global::ABI.WinRT.Interop.EventSourceState<%> +{ +public EventState(System.IntPtr obj, int index) +: base(obj, index) +{ +} + +protected override % GetEventInvoke() +{ +return (%) => +{ +var targetDelegate = TargetDelegate; +if (targetDelegate is null) +{% +return %; +} +%targetDelegate.Invoke(%); +}; +} +} +} +)", +bind(eventTypeSemantics), +bind(eventTypeSemantics), +eventTypeCode, // EventSource<%> +genericInstantiationInitialization, +bind(eventTypeSemantics), +genericInstantiationInitialization == "" ? "" : "_ = initialized;", +eventTypeCode, // % handler +abiTypeName, +eventTypeCode, // EventSourceState<%> +settings.netstandard_compat ? "" : "[global::WinRT.WinRTExposedType]", +eventTypeCode, // EventSourceState<%> +eventTypeCode, // % GetEventInvoke() +bind(invokeMethodSig), +bind(invokeMethodSig), +bind(invokeMethodSig), +bind(invokeMethodSig), +bind(invokeMethodSig)); +}); + } + + void write_temp_class_event_source_subclass(writer& w, TypeDef const& classType, concurrency::concurrent_unordered_map& typeNameToDefinitionMap) + { + for (auto&& ii : classType.InterfaceImpl()) + { + for_typedef(w, get_type_semantics(ii.Interface()), [&](TypeDef const& interfaceType) + { + for (auto&& eventObj : interfaceType.EventList()) + { + auto&& eventTypeSemantics = get_type_semantics(eventObj.EventType()); + auto&& eventTypeCode = w.write_temp("%", bind(eventTypeSemantics, typedef_name_type::Projected, false)); + auto&& eventClass = w.write_temp("%", bind(eventTypeSemantics)); + typeNameToDefinitionMap[eventTypeCode] = eventClass; + } + }); + } + } + + void write_temp_interface_event_source_subclass(writer& w, TypeDef const& interfaceType, concurrency::concurrent_unordered_map& typeNameToDefinitionMap) + { + for (auto&& eventObj : interfaceType.EventList()) + { + auto&& eventTypeSemantics = get_type_semantics(eventObj.EventType()); + auto&& eventTypeCode = w.write_temp("%", bind(eventTypeSemantics, typedef_name_type::Projected, false)); + auto&& eventClass = w.write_temp("%", bind(eventTypeSemantics)); + typeNameToDefinitionMap[eventTypeCode] = eventClass; + } + } + + void add_base_type_entry(TypeDef const& classType, concurrency::concurrent_unordered_map& typeNameToBaseTypeMap) + { + writer w(""); + auto base_type = get_type_semantics(classType.Extends()); + bool has_base_type = !std::holds_alternative(base_type); + if (has_base_type) + { + int numChars = (int)strlen("global::"); + auto&& typeName = w.write_temp("%", bind(classType, typedef_name_type::Projected, true)).substr(numChars); + auto&& baseTypeName = w.write_temp("%", bind(base_type, typedef_name_type::Projected, true)).substr(numChars); + typeNameToBaseTypeMap[typeName] = baseTypeName; + } + } + + void add_metadata_type_entry(TypeDef const& type, concurrency::concurrent_unordered_map& authoredTypeNameToMetadataTypeNameMap) { if (settings.component) { - write_authoring_metadata_type(w, type); - return; - } + if ((get_category(type) == category::class_type && is_static(type)) || + (get_category(type) == category::interface_type && is_exclusive_to(type))) + { + return; + } - if (is_flags_enum(type)) - { - w.write("[FlagsAttribute]\n"); + writer w(""); + auto&& typeName = w.write_temp("%", bind(type, typedef_name_type::Projected, true)); + auto&& metadataTypeName = w.write_temp("%", bind(type, typedef_name_type::CCW, true)); + authoredTypeNameToMetadataTypeNameMap[typeName] = metadataTypeName; } + } - auto enum_underlying_type = is_flags_enum(type) ? "uint" : "int"; - - w.write(R"(%%% enum % : % -{ -)", - bind(type), - bind(type, true), - (settings.internal || settings.embedded) ? (settings.public_enums ? "public" : "internal") : "public", - bind(type, typedef_name_type::Projected, false), enum_underlying_type); - { - for (auto&& field : type.FieldList()) + // Checking for if this is an ABI delegate that will need to be code generated or + // whether it is one that we manually already added in Projections.cs such as for + // classes where the ABI type is IntPtr and we can handle generically. + bool is_abi_delegate_required_for_type(type_semantics const& semantics) + { + return call(semantics, + [&](guid_type) { - if (auto constant = field.Constant()) + return true; + }, + [&](type_definition const& type) + { + switch (get_category(type)) { - w.write("%% = unchecked((%)%),\n", - bind(field.CustomAttribute()), - field.Name(), enum_underlying_type, bind(constant)); + case category::enum_type: + case category::struct_type: + return true; + default: + return false; } - } - } - w.write("}\n"); + }, + [&](fundamental_type type) + { + return type != fundamental_type::String; + }, + [](auto) + { + return false; + }); } - void write_struct(writer& w, TypeDef const& type) + void add_abi_delegates_for_type(std::string_view typeNamespace, std::string_view typeName, std::vector generics, concurrency::concurrent_unordered_set& abiDelegateEntries) { - if (settings.component) - { - write_authoring_metadata_type(w, type); - return; - } - - auto name = w.write_temp("%", bind(type, typedef_name_type::Projected, false)); - - struct field_info - { - std::string type; - std::string name; - bool is_interface; - }; - std::vector fields; - for (auto&& field : type.FieldList()) + writer w; + if (typeNamespace == "Windows.Foundation" || typeNamespace == "Windows.Foundation.Collections") { - auto semantics = get_type_semantics(field.Signature().Type()); - field_info field_info{}; - field_info.type = w.write_temp("%", [&](writer& w){ write_projection_type(w, semantics); }); - field_info.name = field.Name(); - if (auto td = std::get_if(&semantics)) + if (typeName == "IIterator`1") { - field_info.is_interface = get_category(*td) == category::interface_type; + if (is_abi_delegate_required_for_type(generics[0])) + { + auto abiType = w.write_temp("%", bind(generics[0])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_get_Current_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_Current_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) + }); + } } - else if (auto gti = std::get_if(&semantics)) + else if (typeName == "IKeyValuePair`2") { - field_info.is_interface = get_category(gti->generic_type) == category::interface_type; - } - fields.emplace_back(field_info); - } + if (is_abi_delegate_required_for_type(generics[0])) + { + auto abiType = w.write_temp("%", bind(generics[0])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); - w.write(R"(%%%% struct %: IEquatable<%> -{ -% -public %(%) -{ -% -} + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_get_Key_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_Key_%(IntPtr thisPtr, %* __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(IntPtr), typeof(%*), typeof(int) }", abiType) + }); + } -public static bool operator ==(% x, % y) => %; -public static bool operator !=(% x, % y) => !(x == y); -public bool Equals(% other) => this == other; -public override bool Equals(object obj) => obj is % that && this == that; -public override int GetHashCode() => %; -} -)", - // struct - bind(type), - bind(type), - bind(type, true), - internal_accessibility(), - name, - name, - bind_each([](writer& w, auto&& field) - { - w.write("public % %;\n", field.type, field.name); - }, fields), - // ctor - name, - bind_list([](writer& w, auto&& field) - { - w.write("% _%", field.type, field.name); - }, ", ", fields), - bind_each([](writer& w, auto&& field) - { - w.write("% = _%; ", field.name, field.name); - }, fields), - // == - name, - name, - bind_list([](writer& w, auto&& field) - { - w.write("x.% == y.%", - field.name, field.name); - }, " && ", fields), - // !=, Equals - name, - name, - name, - name, - // GetHashCode - bind_list([](writer& w, auto&& field) + if (is_abi_delegate_required_for_type(generics[1])) + { + auto abiType = w.write_temp("%", bind(generics[1])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_get_Value_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_Value_%(IntPtr thisPtr, %* __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(IntPtr), typeof(%*), typeof(int) }", abiType) + }); + } + } + else if (typeName == "IMapView`2") { - w.write("%.GetHashCode()", field.name); - }, " ^ ", fields) - ); - } + if (is_abi_delegate_required_for_type(generics[0]) || is_abi_delegate_required_for_type(generics[1])) + { + auto keyAbiType = w.write_temp("%", bind(generics[0])); + auto escapedKeyAbiType = escape_type_name_for_identifier(keyAbiType); - void write_abi_struct(writer& w, TypeDef const& type) - { - if (is_type_blittable(type)) - { - return; - } + auto valueAbiType = w.write_temp("%", bind(generics[1])); + auto escapedValueAbiType = escape_type_name_for_identifier(valueAbiType); - w.write("[global::System.ComponentModel.EditorBrowsable(global::System.ComponentModel.EditorBrowsableState.Never)]\n% struct %\n{\n", - internal_accessibility(), - bind(type, typedef_name_type::ABI, false)); + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_lookup_%_%", escapedKeyAbiType, escapedValueAbiType), + w.write_temp("internal unsafe delegate int _lookup_%_%(void* thisPtr, % key, out % value);", escapedKeyAbiType, escapedValueAbiType, keyAbiType, valueAbiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(%).MakeByRefType(), typeof(int) }", keyAbiType, valueAbiType) + }); - for (auto&& field : type.FieldList()) - { - w.write("public "); - write_abi_type(w, get_type_semantics(field.Signature().Type())); - w.write(" %;\n", field.Name()); - } + if (is_abi_delegate_required_for_type(generics[0])) + { + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_has_key_%", escapedKeyAbiType), + w.write_temp("internal unsafe delegate int _has_key_%(void* thisPtr, % key, out byte found);", escapedKeyAbiType, keyAbiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(byte).MakeByRefType(), typeof(int) }", keyAbiType) + }); + } + } + } + else if (typeName == "IMap`2") + { + if (is_abi_delegate_required_for_type(generics[0]) || is_abi_delegate_required_for_type(generics[1])) + { + auto keyAbiType = w.write_temp("%", bind(generics[0])); + auto escapedKeyAbiType = escape_type_name_for_identifier(keyAbiType); - auto projected_type = w.write_temp("%", bind(type)); - auto abi_type = w.write_temp("%", bind(type, typedef_name_type::ABI, false)); + auto valueAbiType = w.write_temp("%", bind(generics[1])); + auto escapedValueAbiType = escape_type_name_for_identifier(valueAbiType); - std::vector marshalers; - for (auto&& field : type.FieldList()) - { - abi_marshaler m{ std::string(field.Name()), -1 }; - set_abi_marshaler(w, field.Signature().Type(), m); - marshalers.push_back(std::move(m)); - } + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_lookup_%_%", escapedKeyAbiType, escapedValueAbiType), + w.write_temp("internal unsafe delegate int _lookup_%_%(void* thisPtr, % key, out % value);", escapedKeyAbiType, escapedValueAbiType, keyAbiType, valueAbiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(%).MakeByRefType(), typeof(int) }", keyAbiType, valueAbiType) + }); - // blittable: (no marshaler) value type requiring no marshaling/disposing - // marshalable: (marshaler, is_value_type) value type requiring only marshaling, no disposing - // disposable: (marshaler, !is_value_type) ref type requiring marshaling and disposing - bool have_disposers = std::find_if(marshalers.begin(), marshalers.end(), [](abi_marshaler const& m) - { - return !m.is_value_type; - }) != marshalers.end(); + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_insert_%_%", escapedKeyAbiType, escapedValueAbiType), + w.write_temp("internal unsafe delegate int _insert_%_%(void* thisPtr, % key, % value, out byte replaced);", escapedKeyAbiType, escapedValueAbiType, keyAbiType, valueAbiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(%), typeof(byte).MakeByRefType(), typeof(int) }", keyAbiType, valueAbiType) + }); - w.write(R"( -public struct Marshaler -{ -%public % __abi; -)", - bind_each([](writer& w, abi_marshaler const& m) + if (is_abi_delegate_required_for_type(generics[0])) + { + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_has_key_%", escapedKeyAbiType), + w.write_temp("internal unsafe delegate int _has_key_%(void* thisPtr, % key, out byte found);", escapedKeyAbiType, keyAbiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(byte).MakeByRefType(), typeof(int) }", keyAbiType) + }); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_remove_%", escapedKeyAbiType), + w.write_temp("internal unsafe delegate int _remove_%(void* thisPtr, % key);", escapedKeyAbiType, keyAbiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(int) }", keyAbiType) + }); + } + } + } + else if (typeName == "IVectorView`1") { - if (m.marshaler_type.empty()) return; - w.write("public % _%;\n", m.local_type, m.param_name); - }, marshalers), - abi_type); - if (have_disposers) - { - w.write(R"(public void Dispose() -{ -%} -)", - bind_each([](writer& w, abi_marshaler const& m) + if (is_abi_delegate_required_for_type(generics[0])) { - if (m.is_value_type) return; - w.write("%.DisposeMarshaler(_%);\n", - m.is_marshal_by_object_reference_value() ? "MarshalInspectable" : m.marshaler_type, - m.param_name); - }, marshalers)); - } - w.write("}\n"); + auto abiType = w.write_temp("%", bind(generics[0])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); - w.write(R"( -public static Marshaler CreateMarshaler(% arg) -{ -var m = new Marshaler();)", - projected_type); - if (have_disposers) - { - w.write(R"( -bool success = false; -try -{)"); - } - for (auto&& m : marshalers) - { - if (m.marshaler_type.empty()) continue; - w.write("\nm._% = ", m.param_name); - m.write_create(w, "arg." + m.get_escaped_param_name(w)); - w.write(";"); - } - w.write(R"( -m.__abi = new %() -{ -%}; -% -return m;)", - abi_type, - [&](writer& w) + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_get_at_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_at_%(void* thisPtr, uint index, out % __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(uint), typeof(%).MakeByRefType(), typeof(int) }", abiType) + }); + + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_index_of_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _index_of_%(void* thisPtr, % value, out uint index, out byte found);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }", abiType) + }); + + // GetEnumerator in IVectorView + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_get_Current_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_Current_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) + }); + } + } + else if (typeName == "IVector`1") { - int count = 0; - for (auto&& m : marshalers) + if (is_abi_delegate_required_for_type(generics[0])) { - w.write(count++ == 0 ? "" : ", "); - if (m.marshaler_type.empty()) - { - std::string format; - if (m.param_type == "bool") + auto abiType = w.write_temp("%", bind(generics[0])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate { - format = "% = (byte)(arg.% ? 1 : 0)\n"; - } - else if (m.param_type == "char") + w.write_temp("_get_at_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_at_%(void* thisPtr, uint index, out % __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(uint), typeof(%).MakeByRefType(), typeof(int) }", abiType) + }); + + abiDelegateEntries.insert(generic_abi_delegate { - format = "% = (ushort)arg.%\n"; - } - else + w.write_temp("_index_of_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _index_of_%(void* thisPtr, % value, out uint index, out byte found);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(uint).MakeByRefType(), typeof(byte).MakeByRefType(), typeof(int) }", abiType) + }); + + // SetAt / InsertAt + abiDelegateEntries.insert(generic_abi_delegate { - format = "% = arg.%\n"; - } - w.write(format, - m.get_escaped_param_name(w), - m.get_escaped_param_name(w)); - continue; - } - w.write("% = %.GetAbi(m._%)\n", - m.get_escaped_param_name(w), - m.is_marshal_by_object_reference_value() ? "MarshalInspectable" : m.marshaler_type, - m.param_name); - } - }, - have_disposers ? "success = true;" : ""); - if (have_disposers) - { - w.write(R"( -} -finally -{ -if (!success) -{ -m.Dispose(); -} -} -)"); - } - w.write("}\n"); + w.write_temp("_set_at_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _set_at_%(void* thisPtr, uint index, % value);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(uint), typeof(%), typeof(int) }", abiType) + }); - w.write(R"( -public static % GetAbi(Marshaler m) => m.__abi; -)", - abi_type); + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_append_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _append_%(void* thisPtr, % value);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(int) }", abiType) + }); - w.write(R"( -public static % FromAbi(% arg) -{ -return new %() -{ -%}; -} -)", - projected_type, - abi_type, - projected_type, - [&](writer& w) + // GetEnumerator in IVector + abiDelegateEntries.insert(generic_abi_delegate + { + w.write_temp("_get_Current_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_Current_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) + }); + } + } + else if (typeName == "EventHandler`1") { - int count = 0; - for (auto&& m : marshalers) + if (is_abi_delegate_required_for_type(generics[0])) { - w.write(count++ == 0 ? "" : ", "); - if (m.marshaler_type.empty()) - { - std::string format; - if (m.param_type == "bool") + auto abiType = w.write_temp("%", bind(generics[0])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate { - format = "% = arg.% != 0\n"; - } - else if (m.param_type == "char") + w.write_temp("_invoke_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _invoke_%(void* thisPtr, IntPtr sender, % args);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(IntPtr), typeof(%), typeof(int) }", abiType) + }); + } + } + else if (typeName == "IReference`1") + { + if (is_abi_delegate_required_for_type(generics[0])) + { + auto abiType = w.write_temp("%", bind(generics[0])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate { - format = "% = (char)arg.%\n"; - } - else + w.write_temp("_get_Value_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_Value_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) + }); + } + } + else if (typeName == "IMapChangedEventArgs`1" || + typeName == "IAsyncOperation`1" || + typeName == "IAsyncOperationWithProgress`2") + { + if (is_abi_delegate_required_for_type(generics[0])) + { + auto abiType = w.write_temp("%", bind(generics[0])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate { - format = "% = arg.%\n"; - } - w.write(format, - m.get_escaped_param_name(w), - m.get_escaped_param_name(w)); - continue; - } - w.write("% = %\n", - m.get_escaped_param_name(w), - [&](writer& w) {m.write_from_abi(w, "arg." + m.get_escaped_param_name(w)); }); + w.write_temp("_get_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _get_%(void* thisPtr, out % __return_value__);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%).MakeByRefType(), typeof(int) }", abiType) + }); } - }); - w.write(R"( -public static % FromManaged(% arg) -{ -return new %() -{ -%}; -} -)", - abi_type, - projected_type, - abi_type, - [&](writer& w) + // Add ABI delegates for AsyncOperationProgressHandler as it is referenced in a property of IAsyncOperationWithProgress + // which isn't handled separately. + if (typeName == "IAsyncOperationWithProgress`2" && is_abi_delegate_required_for_type(generics[1])) + { + add_abi_delegates_for_type("Windows.Foundation", "AsyncOperationProgressHandler`2", generics, abiDelegateEntries); + } + } + else if (typeName == "TypedEventHandler`2") { - int count = 0; - for (auto&& m : marshalers) + if (is_abi_delegate_required_for_type(generics[0]) || is_abi_delegate_required_for_type(generics[1])) { - w.write(count++ == 0 ? "" : ", "); - if (m.marshaler_type.empty()) - { - std::string format; - if (m.param_type == "bool") + auto senderAbiType = w.write_temp("%", bind(generics[0])); + auto escapedSenderAbiType = escape_type_name_for_identifier(senderAbiType); + + auto resultAbiType = w.write_temp("%", bind(generics[1])); + auto escapedResultAbiType = escape_type_name_for_identifier(resultAbiType); + + abiDelegateEntries.insert(generic_abi_delegate { - format = "% = (byte)(arg.% ? 1 : 0)\n"; - } - else if (m.param_type == "char") + w.write_temp("_invoke_%_%", escapedSenderAbiType, escapedResultAbiType), + w.write_temp("internal unsafe delegate int _invoke_%_%(void* thisPtr, % sender, % args);", escapedSenderAbiType, escapedResultAbiType, senderAbiType, resultAbiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(%), typeof(%), typeof(int) }", senderAbiType, resultAbiType) + }); + } + } + else if (typeName == "AsyncOperationProgressHandler`2") + { + if (is_abi_delegate_required_for_type(generics[1])) + { + auto abiType = w.write_temp("%", bind(generics[1])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate { - format = "% = (ushort)arg.%\n"; - } - else + w.write_temp("_invoke_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _invoke_%(void* thisPtr, IntPtr asyncInfo, % progressInfo);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(IntPtr), typeof(%), typeof(int) }", abiType) + }); + } + } + else if (typeName == "AsyncActionProgressHandler`1") + { + if (is_abi_delegate_required_for_type(generics[0])) + { + auto abiType = w.write_temp("%", bind(generics[0])); + auto escapedAbiType = escape_type_name_for_identifier(abiType); + + abiDelegateEntries.insert(generic_abi_delegate { - format = "% = arg.%\n"; - } - w.write(format, - m.get_escaped_param_name(w), - m.get_escaped_param_name(w)); - continue; - } - w.write("% = %\n", - m.get_escaped_param_name(w), [&](writer& w) { - m.write_from_managed(w, "arg." + m.get_escaped_param_name(w)); }); + w.write_temp("_invoke_%", escapedAbiType), + w.write_temp("internal unsafe delegate int _invoke_%(void* thisPtr, IntPtr asyncInfo, % progressInfo);", escapedAbiType, abiType), + w.write_temp("new global::System.Type[] { typeof(void*), typeof(IntPtr), typeof(%), typeof(int) }", abiType) + }); } - }); + } + } + } - w.write(R"( -public static unsafe void CopyAbi(Marshaler arg, IntPtr dest) => - *(%*)dest.ToPointer() = GetAbi(arg); -)", - abi_type); + void add_if_generic_type_reference(cswinrt::type_semantics const& typeSemantics, bool isArray, concurrency::concurrent_unordered_set& abiDelegateEntries) + { + call(typeSemantics, + [&](generic_type_instance const& type) + { + // Handle generics of generics where their delegate entries should also be added. + for (auto& arg : type.generic_args) + { + // None of the generics should be an array given WinRT doens't support that. + add_if_generic_type_reference(arg, false, abiDelegateEntries); + } - w.write(R"( -public static unsafe void CopyManaged(% arg, IntPtr dest) => - *(%*)dest.ToPointer() = FromManaged(arg); -)", - projected_type, - abi_type); - - w.write(R"( -public static void DisposeMarshaler(Marshaler m) % -)", - have_disposers ? "=> m.Dispose();" : "{}"); + add_abi_delegates_for_type(type.generic_type.TypeNamespace(), type.generic_type.TypeName(), type.generic_args, abiDelegateEntries); + }, + [&](type_definition const& type) + { + switch (get_category(type)) + { + case category::struct_type: + { + // Struct fields can have IReference generics which we should generate delegates for. + for (auto&& field : type.FieldList()) + { + auto fieldType = field.Signature().Type(); + add_if_generic_type_reference(get_type_semantics(fieldType), fieldType.is_szarray(), abiDelegateEntries); + } + break; + } + } - w.write(R"( -public static void DisposeAbi(% abi) -{ -%} -} -)", - abi_type, - bind_each([](writer& w, abi_marshaler const& m) + // On the CCW for arrays, we also generate an IVector CCW, so we need to generate the appropriate delegates. + if (isArray) + { + std::vector genericArgs = { typeSemantics }; + add_abi_delegates_for_type("Windows.Foundation.Collections", "IVector`1", genericArgs, abiDelegateEntries); + } + }, + [&](auto) { - if (m.is_value_type) return; - w.write("%.DisposeAbi(abi.%);\n", - m.marshaler_type, - m.param_name); - }, marshalers)); + // On the CCW for arrays, we also generate an IVector CCW, so we need to generate the appropriate delegates. + if (isArray) + { + std::vector genericArgs = { typeSemantics }; + add_abi_delegates_for_type("Windows.Foundation.Collections", "IVector`1", genericArgs, abiDelegateEntries); + } + } + ); } - void write_factory_class_inheritance(writer& w, TypeDef const& type) + void add_generic_type_references_in_interface_type(TypeDef const& interfaceType, concurrency::concurrent_unordered_set& abiDelegateEntries) { - auto delimiter{ ", " }; - auto write_delimiter = [&]() + for (auto&& method : interfaceType.MethodList()) { - w.write(delimiter); - }; + if (is_special(method)) + { + continue; + } - for (auto&& [interface_name, factory] : get_attributed_types(w, type)) - { - if ((factory.activatable || factory.statics) && factory.type) + method_signature signature{ method }; + if (signature.has_params()) { - write_delimiter(); - w.write("%", bind(factory.type, typedef_name_type::CCW, false)); + for (auto&& param : signature.params()) + { + add_if_generic_type_reference(get_type_semantics(param.second->Type()), param.second->Type().is_szarray(), abiDelegateEntries); + } + } + + if (auto& returnSignature = signature.return_signature()) + { + add_if_generic_type_reference(get_type_semantics(returnSignature.Type()), returnSignature.Type().is_szarray(), abiDelegateEntries); } } - } - void write_factory_activatable_method(writer& w, MethodDef const& method, std::string_view activatable_type) - { - method_signature signature{ method }; - w.write(R"( -public % %(%) => new %(%); -)", -activatable_type, -method.Name(), -bind_list(", ", signature.params()), -activatable_type, -bind_list(", ", signature.params()) -); + for (auto&& prop : interfaceType.PropertyList()) + { + add_if_generic_type_reference(get_type_semantics(prop.Type().Type()), prop.Type().Type().is_szarray(), abiDelegateEntries); + } + + for (auto&& evt : interfaceType.EventList()) + { + add_if_generic_type_reference(get_type_semantics(evt.EventType()), false, abiDelegateEntries); + } } - void write_factory_class_members(writer& w, TypeDef const& type) + void add_generic_type_references_in_type(TypeDef const& type, concurrency::concurrent_unordered_set& abiDelegateEntries) { - auto delimiter{ ", " }; - auto write_delimiter = [&]() + switch (get_category(type)) { - w.write(delimiter); - }; - - auto projected_type_name = write_type_name_temp(w, type, "%", typedef_name_type::Projected); - for (auto&& [interface_name, factory] : get_attributed_types(w, type)) + case category::delegate_type: { - if (factory.type) + method_signature signature{ get_delegate_invoke(type) }; + if (signature.has_params()) { - if (factory.activatable) - { - w.write_each(factory.type.MethodList(), projected_type_name); - } - else if (factory.statics) + for (auto&& param : signature.params()) { - w.write_each(factory.type.MethodList(), projected_type_name, true, ""sv); - w.write_each(factory.type.PropertyList(), projected_type_name, true, ""sv); - w.write_each(factory.type.EventList(), projected_type_name, true, ""sv); + add_if_generic_type_reference(get_type_semantics(param.second->Type()), param.second->Type().is_szarray(), abiDelegateEntries); } } + + if (auto& returnSignature = signature.return_signature()) + { + add_if_generic_type_reference(get_type_semantics(returnSignature.Type()), returnSignature.Type().is_szarray(), abiDelegateEntries); + } + + break; + } + case category::interface_type: + add_generic_type_references_in_interface_type(type, abiDelegateEntries); + break; } } + bool has_generic_type_instantiations() + { + return generic_type_instances.size() != 0; + } - void write_factory_class(writer& w, TypeDef const& type) + void write_generic_type_instantiation(writer& w, generic_type_instance instance, std::vector& rcwFunctions, std::vector& vtableFunctions) { - auto factory_type_name = write_type_name_temp(w, type, "%ServerActivationFactory", typedef_name_type::CCW); - auto base_class = (is_static(type) || !has_default_constructor(type)) ? - "ComponentActivationFactory" : write_type_name_temp(w, type, "ActivatableComponentActivationFactory<%>", typedef_name_type::Projected); - auto type_name = write_type_name_temp(w, type, "%", typedef_name_type::Projected); + auto get_invoke_info = [&](MethodDef const& method, bool isDelegate = false) + { + return w.write_temp("(*(delegate* unmanaged[Stdcall]<%, int>**)ThisPtr)[%]", + bind([&](writer& w) { + writer::write_generic_type_name_guard g(w, [&](writer& w, uint32_t index) + { + write_abi_type(w, w.get_generic_arg_scope(index).first); + }); - w.write(R"( -internal class % : %% -{ + write_abi_parameter_types_pointer(w, method_signature{ method }); + }), + isDelegate ? 3 : get_vmethod_index(instance.generic_type, method) + INSPECTABLE_METHOD_COUNT); + }; -static %() -{ -RuntimeHelpers.RunClassConstructor(typeof(%).TypeHandle); -} + if (get_category(instance.generic_type) == category::delegate_type) + { + auto method = get_event_invoke_method(instance.generic_type); + method_signature signature{ method }; -public static IntPtr Make() -{ -using var marshaler = MarshalInspectable<%>.CreateMarshaler(_factory).As(); -return marshaler.GetRef(); -} + auto guard{ w.push_generic_args(instance) }; -static readonly % _factory = new %(); -public static ObjectReference ActivateInstance() + if (abi_signature_has_generic_parameters(w, signature)) + { + rcwFunctions.emplace_back(method.Name()); + + auto invoke_target = get_invoke_info(method, true); + w.write(R"( +private static unsafe % %(IObjectReference _obj%%) { -IntPtr instance = _factory.ActivateInstance(); -return ObjectReference.Attach(ref instance).As(); -} +var ThisPtr = _obj.ThisPtr; +%} +)", + bind(signature), + method.Name(), + signature.has_params() ? ", " : "", + bind_list(", ", signature.params()), + bind(signature, invoke_target, "_obj", false, false, is_noexcept(method), true)); + } + vtableFunctions.emplace_back(method.Name()); + + w.write(R"( +[UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] +private static unsafe int Do_Abi_Invoke(%) +{ % } )", -factory_type_name, -base_class, -bind(type), -factory_type_name, -type_name, -factory_type_name, -factory_type_name, -factory_type_name, -bind(type) -); - } + [&](writer& w) { + writer::write_generic_type_name_guard g(w, [&](writer& w, uint32_t index) + { + write_abi_type(w, w.get_generic_arg_scope(index).first); + }); - void write_module_activation_factory(writer& w, std::set const& types) - { - w.write(R"( -using System; -namespace WinRT -{ -% static class Module + write_abi_parameters(w, signature); + }, + [&](writer& w) { + auto invoke = w.write_temp( + "%%.Abi_Invoke(thisPtr, %)", + bind(instance.generic_type, typedef_name_type::StaticAbiClass), + [&](writer& w) + { + writer::write_generic_type_name_guard g(w, [&](writer& w, uint32_t index) + { + write_projection_type(w, w.get_generic_arg_scope(index).first); + w.write(", "); + write_abi_type(w, w.get_generic_arg_scope(index).first); + }); + + write_type_params(w, instance.generic_type); + }, + "%"); + write_managed_method_call(w, signature, invoke, true); + }); + } + else + { + for (auto&& method : instance.generic_type.MethodList()) + { + method_signature signature{ method }; + + if (!projected_signature_has_generic_parameters(w, signature)) + { + continue; + } + + // Adding RCW function names here including the synthesized methods for + // properties so that we have them in the right vtable order even if we + // don't write them here. + if (!(is_special(method) && + (starts_with(method.Name(), "add_") || + starts_with(method.Name(), "remove_")))) + { + rcwFunctions.emplace_back(method.Name()); + } + + if (is_special(method)) + { + continue; + } + + auto guard{ w.push_generic_args(instance) }; + + auto invoke_target = get_invoke_info(method); + w.write(R"( +private static unsafe % %(IObjectReference _obj%%) { -public static unsafe IntPtr GetActivationFactory(String runtimeClassId) -{% -return IntPtr.Zero; -} -} -} +var ThisPtr = _obj.ThisPtr; +%} )", - internal_accessibility(), -bind_each([](writer& w, TypeDef const& type) - { - w.write(R"( + bind(signature), + method.Name(), + signature.has_params() ? ", " : "", + bind_list(", ", signature.params()), + bind(signature, invoke_target, "_obj", false, false, is_noexcept(method), true)); + } + + for (auto&& prop : instance.generic_type.PropertyList()) + { + auto guard{ w.push_generic_args(instance) }; + auto [getter, setter] = get_property_methods(prop); -if (runtimeClassId == "%.%") + if (getter && projected_signature_has_generic_parameters(w, method_signature{ getter })) + { + auto invoke_target = get_invoke_info(getter); + auto signature = method_signature(getter); + auto marshalers = get_abi_marshalers(w, signature, false, prop.Name(), false, true); + w.write(R"(private static unsafe % %(IObjectReference _obj) { -return %ServerActivationFactory.Make(); -} +var ThisPtr = _obj.ThisPtr; +%} )", -type.TypeNamespace(), -type.TypeName(), -bind(type, typedef_name_type::CCW, true) -); - }, - types - )); + write_prop_type(w, prop), + getter.Name(), + bind(invoke_target, "_obj", false, marshalers, is_noexcept(prop))); + } + if (setter && projected_signature_has_generic_parameters(w, method_signature{ setter })) + { + auto invoke_target = get_invoke_info(setter); + auto signature = method_signature(setter); + auto marshalers = get_abi_marshalers(w, signature, false, prop.Name(), false, true); + marshalers[0].param_name = "value"; + w.write(R"(private static unsafe void %(IObjectReference _obj, % value) +{ +var ThisPtr = _obj.ThisPtr; +%} +)", + setter.Name(), + write_prop_type(w, prop), + bind(invoke_target, "_obj", false, marshalers, is_noexcept(prop))); + } + w.write("\n"); + } + } } - void write_event_source_generic_args(writer& w, cswinrt::type_semantics eventTypeSemantics) + generic_type_instance ConvertGenericTypeInstanceToConcreteType(writer& w, const generic_type_instance& generic_instance) { - if (!std::holds_alternative(eventTypeSemantics)) + generic_type_instance converted = generic_instance; + for (size_t idx = 0; idx < generic_instance.generic_args.size(); idx++) { - return; - } - for_typedef(w, eventTypeSemantics, [&](TypeDef const&) + auto& semantics = generic_instance.generic_args[idx]; + if (auto gti = std::get_if(&semantics)) { - std::vector genericArgs; - auto arg_count = std::get(eventTypeSemantics).generic_args.size(); - for (int i = 0; i < (int) arg_count; ++i ) - { - auto semantics = w.get_generic_arg_scope(i).first; - if (std::holds_alternative(semantics)) - { - genericArgs.push_back(w.write_temp("%", bind(i))); - } - } - if (genericArgs.size() == 0) - { - return; - } - w.write("<%>", bind_list([](writer& w, auto&& value) - { - w.write(value); - }, ", "sv, genericArgs)); - }); + converted.generic_args[idx] = w.get_generic_arg_scope(gti->index).first; + } + else if (auto instance = std::get_if(&semantics)) + { + converted.generic_args[idx] = ConvertGenericTypeInstanceToConcreteType(w, *instance); + } + } + + return converted; } - void write_event_source_subclass(writer& w, cswinrt::type_semantics eventTypeSemantics) + void write_generic_type_instantiations(writer& w) { - auto abiTypeName = w.write_temp("%", bind(eventTypeSemantics, typedef_name_type::ABI, true)); - for_typedef(w, eventTypeSemantics, [&](TypeDef const& eventType) + // Go through all the generic types and write instantiation classes for them. + // While writing them, their implementations might also reference generic types + // which may also need instantiation classes if one hasn't been generated already. + concurrency::concurrent_unordered_set written_generic_type_instances; + bool types_written = true; + while (generic_type_instances.size() != 0 && types_written) + { + types_written = false; + concurrency::concurrent_unordered_set current_generic_type_instances = generic_type_instances; + generic_type_instances = concurrency::concurrent_unordered_set(); + for (auto& instance : current_generic_type_instances) { - if ((eventType.TypeNamespace() == "Windows.Foundation" || eventType.TypeNamespace() == "System") && eventType.TypeName() == "EventHandler`1") + if (written_generic_type_instances.find(instance) != written_generic_type_instances.end()) { - return; + continue; } - auto eventTypeCode = w.write_temp("%", bind(eventType, typedef_name_type::Projected, false)); - auto invokeMethodSig = get_event_invoke_method(eventType); + written_generic_type_instances.insert(instance); + types_written = true; + + std::vector rcwFunctions, vtableFunctions; w.write(R"( -internal sealed unsafe class %% : EventSource<%> +internal static class % { -internal %(IObjectReference obj, -delegate* unmanaged[Stdcall] addHandler, -delegate* unmanaged[Stdcall] removeHandler, int index) : base(obj, addHandler, removeHandler, index) -{ -} +private static bool Initialized { get; } = Init(); -protected override ObjectReferenceValue CreateMarshaler(% del) => -%.CreateMarshaler2(del); +public static bool EnsureInitialized() => Initialized; -protected override State CreateEventState() => -new EventState(_obj.ThisPtr, _index); - -private sealed class EventState : State -{ -public EventState(System.IntPtr obj, int index) -: base(obj, index) -{ -} +% -protected override System.Delegate GetEventInvoke() -{ -% invoke = (%) => +private unsafe static bool Init() { -var localDel = (%) del; -if (localDel == null) -{% -return %; -} -%localDel.Invoke(%); -}; -return invoke; -} +% +% +return true; } } )", -bind(eventTypeSemantics), -bind(eventTypeSemantics), -eventTypeCode, -bind(eventTypeSemantics), -eventTypeCode, -abiTypeName, -eventTypeCode, -bind(invokeMethodSig), -eventTypeCode, -bind(invokeMethodSig), -bind(invokeMethodSig), -bind(invokeMethodSig), -bind(invokeMethodSig)); -}); - } + instance.instantiation_class_name, + bind(instance.instance, rcwFunctions, vtableFunctions), + bind([&](writer& w) { + auto guard{ w.push_generic_args(instance.instance) }; + + auto genericInstantiationMethodsClass = w.write_temp("%%", + bind(instance.instance.generic_type, typedef_name_type::StaticAbiClass), + [&](writer& w) + { + writer::write_generic_type_name_guard g(w, [&](writer& w, uint32_t index) + { + write_projection_type(w, w.get_generic_arg_scope(index).first); + w.write(", "); + write_abi_type(w, w.get_generic_arg_scope(index).first); + }); - void write_temp_class_event_source_subclass(writer& w, TypeDef const& classType, concurrency::concurrent_unordered_map& typeNameToDefinitionMap) - { - for (auto&& ii : classType.InterfaceImpl()) - { - for_typedef(w, get_type_semantics(ii.Interface()), [&](TypeDef const& interfaceType) - { - for (auto&& eventObj : interfaceType.EventList()) - { - auto&& eventTypeSemantics = get_type_semantics(eventObj.EventType()); - auto&& eventTypeCode = w.write_temp("%", bind(eventTypeSemantics, typedef_name_type::Projected, false)); - auto&& eventClass = w.write_temp("%", bind(eventTypeSemantics)); - typeNameToDefinitionMap[eventTypeCode] = eventClass; - } - }); - } - } + write_type_params(w, instance.instance.generic_type); + }); - void write_temp_interface_event_source_subclass(writer& w, TypeDef const& interfaceType, concurrency::concurrent_unordered_map& typeNameToDefinitionMap) - { - for (auto&& eventObj : interfaceType.EventList()) - { - auto&& eventTypeSemantics = get_type_semantics(eventObj.EventType()); - auto&& eventTypeCode = w.write_temp("%", bind(eventTypeSemantics, typedef_name_type::Projected, false)); - auto&& eventClass = w.write_temp("%", bind(eventTypeSemantics)); - typeNameToDefinitionMap[eventTypeCode] = eventClass; - } - } + if (rcwFunctions.size() != 0 || get_category(instance.instance.generic_type) != category::delegate_type) + { + w.write("%.InitRcwHelper(%);", + genericInstantiationMethodsClass, + bind_list([](writer& w, std::string const& rcwFunction) + { + w.write("&%", rcwFunction); + }, ",\n", rcwFunctions)); + } - void add_base_type_entry(TypeDef const& classType, concurrency::concurrent_unordered_map& typeNameToBaseTypeMap) - { - writer w(""); - auto base_type = get_type_semantics(classType.Extends()); - bool has_base_type = !std::holds_alternative(base_type); - if (has_base_type) - { - int numChars = (int)strlen("global::"); - auto&& typeName = w.write_temp("%", bind(classType, typedef_name_type::Projected, true)).substr(numChars); - auto&& baseTypeName = w.write_temp("%", bind(base_type, typedef_name_type::Projected, true)).substr(numChars); - typeNameToBaseTypeMap[typeName] = baseTypeName; + if (get_category(instance.instance.generic_type) == category::delegate_type) + { + w.write("\n%.InitCcw(&Do_Abi_Invoke);", genericInstantiationMethodsClass); + } + }), + bind([&](writer& w) { + auto guard{ w.push_generic_args(instance.instance) }; + + // Initialize any interfaces implemented by this type as they + // can be called by the consumer when using this interface. + for (auto&& iface : instance.instance.generic_type.InterfaceImpl()) + { + write_ensure_generic_type_initialized(w, get_type_semantics(iface.Interface()), false); + } + + // Events don't have an implementation in the generic instantiation classes + // given they use generated event sources and are shared in a projection. + // But this can mean that the instantiation classes for generic events types in + // generic interfaces aren't initialized. This handles those initializations. + for (auto&& evt : instance.instance.generic_type.EventList()) + { + write_ensure_generic_type_initialized(w, get_type_semantics(evt.EventType()), false); + } + })); + } } } @@ -7758,4 +11153,4 @@ bind(invokeMethodSig)); //------------------------------------------------------------------------------ )", VERSION_STRING); } -} +} \ No newline at end of file diff --git a/src/cswinrt/cswinrt.vcxproj b/src/cswinrt/cswinrt.vcxproj index 674c12582..e297886a6 100644 --- a/src/cswinrt/cswinrt.vcxproj +++ b/src/cswinrt/cswinrt.vcxproj @@ -1,7 +1,7 @@  + - 15.0 {6acfd2b2-e8aa-4cd4-aad8-213ce8bb2637} @@ -77,6 +77,7 @@ + @@ -90,8 +91,10 @@ + + @@ -109,10 +112,15 @@ + + + + + + - - + @@ -125,8 +133,8 @@ This project references NuGet package(s) that are missing on this computer. Enable 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/cswinrt/cswinrt.vcxproj.filters b/src/cswinrt/cswinrt.vcxproj.filters index 57e31721d..293cbe51d 100644 --- a/src/cswinrt/cswinrt.vcxproj.filters +++ b/src/cswinrt/cswinrt.vcxproj.filters @@ -51,15 +51,27 @@ {369a6d8a-43e6-48af-82c1-bb2e555909a4} + + {22aeef74-f4df-40f4-a18f-8053ac54f966} + + + {bb545b29-ba72-4949-b044-790f57ba17a5} + + + {8451fa83-9750-4aec-ae8c-a4149c4b98a6} + + + {98453be9-888b-4032-b5cd-4034b17197c9} + + + {03980bb6-f657-4c07-a456-5b4774de5773} + - + strings - - strings - strings\additions\Windows.Foundation @@ -174,6 +186,15 @@ strings + + strings\additions\Windows.Foundation + + + strings\additions\Windows.Storage.Streams + + + strings\additions\Windows.Storage.Streams + diff --git a/src/cswinrt/helpers.h b/src/cswinrt/helpers.h index 32eeaed58..9968b22ef 100644 --- a/src/cswinrt/helpers.h +++ b/src/cswinrt/helpers.h @@ -670,7 +670,8 @@ namespace cswinrt } mapped_types[] = { // Make sure to keep this table consistent with the registrations in WinRT.Runtime/Projections.cs - // and the reverse mapping in WinRT.SourceGenerator/WinRTTypeWriter.cs. + // and the reverse mapping in WinRT.SourceGenerator/TypeMapper.cs. + // This table can include both the MUX and WUX types as only one will be selected at runtime. // NOTE: Must keep namespaces sorted (outer) and abi type names sorted (inner) { "Microsoft.UI.Xaml", { @@ -810,19 +811,94 @@ namespace cswinrt { "Windows.UI", { { "Color", "Windows.UI", "Color" }, - { "ColorHelper" }, - { "IColorHelper" }, - { "IColorHelperStatics" }, - { "IColorHelperStatics2" }, } }, - // Temporary, until WinUI provides TypeName + { "Windows.UI.Xaml", + { + { "CornerRadius", "Windows.UI.Xaml", "CornerRadius" }, + { "CornerRadiusHelper" }, + { "Duration", "Windows.UI.Xaml", "Duration" }, + { "DurationHelper" }, + { "DurationType", "Windows.UI.Xaml", "DurationType" }, + { "GridLength", "Windows.UI.Xaml", "GridLength" }, + { "GridLengthHelper" }, + { "GridUnitType", "Windows.UI.Xaml", "GridUnitType" }, + { "ICornerRadiusHelper" }, + { "ICornerRadiusHelperStatics" }, + { "IDurationHelper" }, + { "IDurationHelperStatics" }, + { "IGridLengthHelper" }, + { "IGridLengthHelperStatics" }, + { "IThicknessHelper" }, + { "IThicknessHelperStatics" }, + { "Thickness", "Windows.UI.Xaml", "Thickness" }, + { "ThicknessHelper" }, + { "IXamlServiceProvider", "System", "IServiceProvider" }, + } + }, + { "Windows.UI.Xaml.Controls.Primitives", + { + { "GeneratorPosition", "Windows.UI.Xaml.Controls.Primitives", "GeneratorPosition" }, + { "GeneratorPositionHelper" }, + { "IGeneratorPositionHelper" }, + { "IGeneratorPositionHelperStatics" }, + } + }, + { "Windows.UI.Xaml.Data", + { + { "DataErrorsChangedEventArgs", "System.ComponentModel", "DataErrorsChangedEventArgs" }, + { "INotifyDataErrorInfo", "System.ComponentModel", "INotifyDataErrorInfo", true, true }, + { "INotifyPropertyChanged", "System.ComponentModel", "INotifyPropertyChanged" }, + { "PropertyChangedEventArgs", "System.ComponentModel", "PropertyChangedEventArgs" }, + { "PropertyChangedEventHandler", "System.ComponentModel", "PropertyChangedEventHandler" }, + } + }, + { "Windows.UI.Xaml.Input", + { + { "ICommand", "System.Windows.Input", "ICommand", true } + } + }, { "Windows.UI.Xaml.Interop", { + { "IBindableIterable", "System.Collections", "IEnumerable", true, true }, + { "IBindableVector", "System.Collections", "IList", true, true }, + { "INotifyCollectionChanged", "System.Collections.Specialized", "INotifyCollectionChanged", true }, + { "NotifyCollectionChangedAction", "System.Collections.Specialized", "NotifyCollectionChangedAction" }, + { "NotifyCollectionChangedEventArgs", "System.Collections.Specialized", "NotifyCollectionChangedEventArgs", true }, + { "NotifyCollectionChangedEventHandler", "System.Collections.Specialized", "NotifyCollectionChangedEventHandler", true }, { "TypeKind", "Windows.UI.Xaml.Interop", "TypeKind", true }, { "TypeName", "System", "Type", true } } }, + { "Windows.UI.Xaml.Media", + { + { "IMatrixHelper" }, + { "IMatrixHelperStatics" }, + { "Matrix", "Windows.UI.Xaml.Media", "Matrix" }, + { "MatrixHelper" }, + } + }, + { "Windows.UI.Xaml.Media.Animation", + { + { "IKeyTimeHelper" }, + { "IKeyTimeHelperStatics" }, + { "IRepeatBehaviorHelper" }, + { "IRepeatBehaviorHelperStatics" }, + { "KeyTime", "Windows.UI.Xaml.Media.Animation", "KeyTime" }, + { "KeyTimeHelper" }, + { "RepeatBehavior", "Windows.UI.Xaml.Media.Animation", "RepeatBehavior" }, + { "RepeatBehaviorHelper" }, + { "RepeatBehaviorType", "Windows.UI.Xaml.Media.Animation", "RepeatBehaviorType" } + } + }, + { "Windows.UI.Xaml.Media.Media3D", + { + { "IMatrix3DHelper" }, + { "IMatrix3DHelperStatics" }, + { "Matrix3D", "Windows.UI.Xaml.Media.Media3D", "Matrix3D" }, + { "Matrix3DHelper" }, + } + }, }; auto nsItr = std::lower_bound(std::begin(mapped_types), std::end(mapped_types), typeNamespace, [](auto&& v, std::string_view ns) @@ -1421,4 +1497,102 @@ namespace cswinrt return get_fast_abi_class_for_class(fast_abi_class_type.value()); } + int get_gc_pressure_amount(TypeDef const& classType) + { + auto gc_pressure_amount = 0; + if (auto gc_pressure_attr = get_attribute(classType, "Windows.Foundation.Metadata", "GCPressureAttribute")) + { + // Restricting to sealed scenarios because unsealed scenarios require more handling to prevent mismatches in + // adding and removing memory pressure and none of the Windows namespace types which use it today are unsealed. + if (classType.Flags().Sealed()) + { + auto sig = gc_pressure_attr.Value(); + auto const& args = sig.NamedArgs(); + auto amount = std::get(std::get(std::get(args[0].value.value).value).value); + gc_pressure_amount = amount == 0 ? 12000 : amount == 1 ? 120000 : 1200000; + } + } + return gc_pressure_amount; + } + + struct generic_abi_delegate + { + std::string abi_delegate_name; + std::string abi_delegate_declaration; + std::string abi_delegate_types; + + // Hash / equality for the hast set this is added to is based on the types in the delegate + // as we do not need duplicate delegate entries from different collection types. + bool operator==(const generic_abi_delegate& entry) const + { + return abi_delegate_types == entry.abi_delegate_types; + } + }; + + struct generic_type_instantiation + { + generic_type_instance instance; + std::string instantiation_class_name; + + // Hash / equality for the hash set. + bool operator==(const generic_type_instantiation& other) const + { + return instantiation_class_name == other.instantiation_class_name; + } + }; + + std::string escape_type_name_for_identifier(std::string typeName) + { + std::regex re(R"-((\ |:|<|>|`|,|\.))-"); + return std::regex_replace(typeName, re, "_"); + } + + std::string get_fundamental_type_guid_signature(fundamental_type type) + { + switch (type) + { + case fundamental_type::Boolean: return "b1"; + case fundamental_type::Char: return "c2"; + case fundamental_type::Int8: return "i1"; + case fundamental_type::UInt8: return "u1"; + case fundamental_type::Int16: return "i2"; + case fundamental_type::UInt16: return "u2"; + case fundamental_type::Int32: return "i4"; + case fundamental_type::UInt32: return "u4"; + case fundamental_type::Int64: return "i8"; + case fundamental_type::UInt64: return "u8"; + case fundamental_type::Float: return "f4"; + case fundamental_type::Double: return "f8"; + case fundamental_type::String: return "string"; + default: throw_invalid("Unknown type"); + } + } + + // Returns whether the ABI interface should implement the CCW interface or not. + // In authoring scenarios, exclusive interfaces don't exist, so we use the CCW impl type. + bool does_abi_interface_implement_ccw_interface(TypeDef const& type) + { + return settings.component && + settings.filter.includes(type) && + is_exclusive_to(type); + } +} + +namespace std +{ + template<> + struct hash { + size_t operator()(const cswinrt::generic_abi_delegate& entry) const + { + return hash()(entry.abi_delegate_types); + } + }; + + template<> + struct hash { + size_t operator()(const cswinrt::generic_type_instantiation& instantiation) const + { + return hash()(instantiation.instantiation_class_name); + } + }; } diff --git a/src/cswinrt/main.cpp b/src/cswinrt/main.cpp index a407ba43f..0a2241eb1 100644 --- a/src/cswinrt/main.cpp +++ b/src/cswinrt/main.cpp @@ -5,6 +5,7 @@ #include "type_writers.h" #include "code_writers.h" #include +#include namespace cswinrt { @@ -32,12 +33,16 @@ namespace cswinrt { "output", 0, 1, "", "Location of generated projection" }, { "include", 0, option::no_max, "", "One or more prefixes to include in projection" }, { "exclude", 0, option::no_max, "", "One or more prefixes to exclude from projection" }, - { "target", 0, 1, "", "Target TFM for projection. Omit for compatibility with newest TFM (net6.0)." }, + { "addition_exclude", 0, option::no_max, "", "One or more namespace prefixes to exclude from the projection additions" }, + { "target", 0, 1, "", "Target TFM for projection (.NET 8 is the default)" }, { "component", 0, 0, {}, "Generate component projection." }, { "verbose", 0, 0, {}, "Show detailed progress information" }, { "internal", 0, 0, {}, "Generates a private projection."}, { "embedded", 0, 0, {}, "Generates an embedded projection."}, { "public_enums", 0, 0, {}, "Used with embedded option to generate enums as public"}, + { "public_exclusiveto", 0, 0, {}, "Make exclusiveto interfaces public in the projection (default is internal)"}, + { "idic_exclusiveto", 0, 0, {}, "Make exclusiveto interfaces support IDynamicInterfaceCastable (IDIC) for RCW scenarios (default is false)"}, + { "partial_factory", 0, 0, {}, "Allows to provide an additional component activation factory (default is false)"}, { "help", 0, option::no_max, {}, "Show detailed help" }, { "?", 0, option::no_max, {}, {} }, }; @@ -90,15 +95,23 @@ Where is one or more of: settings.verbose = args.exists("verbose"); auto target = args.value("target"); - if (!target.empty() && target != "netstandard2.0" && !starts_with(target, "net5.0") && !starts_with(target, "net6.0") && !starts_with(target, "net7.0")) + if (!target.empty() && target != "netstandard2.0" && !starts_with(target, "net8.0")) { throw usage_exception(); } + else if (target.empty()) + { + // Default to .NET 8 if no explicit target is set + target = "net8.0"; + } settings.netstandard_compat = target == "netstandard2.0"; settings.component = args.exists("component"); settings.internal = args.exists("internal"); settings.embedded = args.exists("embedded"); settings.public_enums = args.exists("public_enums"); + settings.public_exclusiveto = args.exists("public_exclusiveto"); + settings.idic_exclusiveto = args.exists("idic_exclusiveto"); + settings.partial_factory = args.exists("partial_factory"); settings.input = args.files("input", database::is_database); for (auto && include : args.values("include")) @@ -111,6 +124,11 @@ Where is one or more of: settings.exclude.insert(exclude); } + for (auto&& addition_exclude : args.values("addition_exclude")) + { + settings.addition_exclude.insert(addition_exclude); + } + settings.output_folder = std::filesystem::absolute(args.value("output", "output")); create_directories(settings.output_folder); } @@ -135,6 +153,9 @@ Where is one or more of: cache c{ get_files_to_cache() }; settings.filter = { settings.include, settings.exclude }; + // Include all additions for included namespaces by default + settings.addition_filter = { settings.include, settings.addition_exclude }; + std::set componentActivatableClasses; if (settings.component) { @@ -174,11 +195,12 @@ Where is one or more of: task_group group; - concurrency::concurrent_unordered_map typeNameToEventDefinitionMap, typeNameToBaseTypeMap; + concurrency::concurrent_unordered_map typeNameToEventDefinitionMap, typeNameToBaseTypeMap, authoredTypeNameToMetadataTypeNameMap; + concurrency::concurrent_unordered_set abiDelegateEntries; bool projectionFileWritten = false; for (auto&& ns_members : c.namespaces()) { - group.add([&ns_members, &componentActivatableClasses, &projectionFileWritten, &typeNameToEventDefinitionMap, &typeNameToBaseTypeMap] + group.add([&ns_members, &componentActivatableClasses, &projectionFileWritten, &typeNameToEventDefinitionMap, &typeNameToBaseTypeMap, &abiDelegateEntries, &authoredTypeNameToMetadataTypeNameMap] { auto&& [ns, members] = ns_members; std::string_view currentType = ""; @@ -220,6 +242,7 @@ Where is one or more of: { write_class(w, type); add_base_type_entry(type, typeNameToBaseTypeMap); + add_metadata_type_entry(type, authoredTypeNameToMetadataTypeNameMap); } if (settings.component && componentActivatableClasses.count(type) == 1) { @@ -231,14 +254,17 @@ Where is one or more of: break; case category::delegate_type: write_delegate(w, type); + add_metadata_type_entry(type, authoredTypeNameToMetadataTypeNameMap); break; case category::enum_type: write_enum(w, type); + add_metadata_type_entry(type, authoredTypeNameToMetadataTypeNameMap); type_requires_abi = false; break; case category::interface_type: write_interface(w, type); write_temp_interface_event_source_subclass(helperWriter, type, typeNameToEventDefinitionMap); + add_metadata_type_entry(type, authoredTypeNameToMetadataTypeNameMap); break; case category::struct_type: if (is_api_contract_type(type)) @@ -248,11 +274,13 @@ Where is one or more of: else { write_struct(w, type); + add_metadata_type_entry(type, authoredTypeNameToMetadataTypeNameMap); type_requires_abi = !is_type_blittable(type); } break; } + add_generic_type_references_in_type(type, abiDelegateEntries); written = true; requires_abi = requires_abi || type_requires_abi; } @@ -276,17 +304,24 @@ Where is one or more of: { case category::class_type: write_abi_class(w, type); + if (settings.component && componentActivatableClasses.count(type) == 1) + { + write_winrt_exposed_type_class(w, type, true); + } + write_winrt_implementation_type_rcw_factory_attribute_type(w, type); break; case category::delegate_type: write_abi_delegate(w, type); + write_winrt_exposed_type_class(w, type, false); break; case category::interface_type: if (settings.netstandard_compat) { + write_static_abi_classes(w, type); write_abi_interface_netstandard(w, type); } else - { + { write_static_abi_classes(w, type); write_abi_interface(w, type); } @@ -306,7 +341,7 @@ Where is one or more of: // Custom additions to namespaces for (auto addition : strings::additions) { - if (ns == addition.name) + if (ns == addition.name && settings.addition_filter.includes(ns)) { w.write(addition.value); } @@ -321,7 +356,7 @@ Where is one or more of: { writer console; console.write("error: '%' when processing %%%\n", e.what(), ns, currentType.empty() ? "" : ".", currentType); - console.flush_to_console(); + console.flush_to_console_error(); throw; } }); @@ -343,7 +378,7 @@ Where is one or more of: writer eventHelperWriter("WinRT"); write_file_header(eventHelperWriter); - eventHelperWriter.write("namespace WinRT\n{\n%\n}", bind([&](writer& w) { + eventHelperWriter.write("using System;\nnamespace WinRT\n{\n%\n}", bind([&](writer& w) { for (auto&& [key, value] : typeNameToEventDefinitionMap) { w.write("%", value); @@ -359,29 +394,130 @@ Where is one or more of: { internal static class ProjectionTypesInitializer { -internal readonly static System.Collections.Generic.Dictionary TypeNameToBaseTypeNameMapping = new System.Collections.Generic.Dictionary(%, System.StringComparer.Ordinal) +internal static readonly System.Collections.Generic.Dictionary TypeNameToBaseTypeNameMapping = new System.Collections.Generic.Dictionary(%, System.StringComparer.Ordinal) { % }; [System.Runtime.CompilerServices.ModuleInitializer] -internal static void InitalizeProjectionTypes() +internal static void InitializeProjectionTypes() { ComWrappersSupport.RegisterProjectionTypeBaseTypeMapping(TypeNameToBaseTypeNameMapping); } } })", - typeNameToBaseTypeMap.size(), - bind([&](writer& w) { - for (auto&& [key, value] : typeNameToBaseTypeMap) - { - w.write(R"(["%"] = "%",)", key, value); - w.write("\n"); - } - })); +typeNameToBaseTypeMap.size(), +bind([&](writer& w) { + for (auto&& [key, value] : typeNameToBaseTypeMap) + { + w.write(R"(["%"] = "%",)", key, value); + w.write("\n"); + } + })); baseTypeWriter.flush_to_file(settings.output_folder / "WinRTBaseTypeMappingHelper.cs"); } + if (!abiDelegateEntries.empty() && settings.netstandard_compat) + { + writer baseTypeWriter("WinRT"); + write_file_header(baseTypeWriter); + baseTypeWriter.write(R"( +using System; + +namespace WinRT +{ +internal static class AbiDelegatesInitializer +{ + +[System.Runtime.CompilerServices.ModuleInitializer] +internal static void InitializeAbiDelegates() +{ +% +} + +% +} +})", + bind([&](writer& w) { + for (auto&& entry : abiDelegateEntries) + { + w.write("Projections.RegisterAbiDelegate(%, typeof(%));\n", entry.abi_delegate_types, entry.abi_delegate_name); + } + + if (settings.filter.includes("Windows.Foundation.AsyncStatus")) + { + w.write("Projections.RegisterAbiDelegate(new Type[] { typeof(void*), typeof(IntPtr), typeof(global::Windows.Foundation.AsyncStatus), typeof(int) }, typeof(_invoke_IntPtr_AsyncStatus));\n"); + } + }), + bind([&](writer& w) { + for (auto&& entry : abiDelegateEntries) + { + w.write("%\n", entry.abi_delegate_declaration); + } + + if (settings.filter.includes("Windows.Foundation.AsyncStatus")) + { + w.write("internal unsafe delegate int _invoke_IntPtr_AsyncStatus(void* thisPtr, IntPtr asyncInfo, global::Windows.Foundation.AsyncStatus asyncStatus);\n"); + } + })); + baseTypeWriter.flush_to_file(settings.output_folder / "WinRTAbiDelegateInitializer.cs"); + } + + if (!settings.netstandard_compat && has_generic_type_instantiations()) + { + writer genericTypeInstantiationWriter("WinRT.GenericTypeInstantiations"); + write_file_header(genericTypeInstantiationWriter); + genericTypeInstantiationWriter.write(R"( +using System; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace WinRT.GenericTypeInstantiations +{ +% +})", + bind()); + genericTypeInstantiationWriter.flush_to_file(settings.output_folder / "WinRTGenericTypeInstantiations.cs"); + } + + if (!authoredTypeNameToMetadataTypeNameMap.empty() && settings.component) + { + writer metadataMappingTypeWriter("WinRT"); + write_file_header(metadataMappingTypeWriter); + metadataMappingTypeWriter.write(R"( +using System; + +namespace WinRT +{ +internal static class AuthoringMetadataTypeInitializer +{ + +private static Type GetMetadataTypeMapping(Type type) +{ +return type switch +{ +% +_ => null +}; +} + +[System.Runtime.CompilerServices.ModuleInitializer] +internal static void InitializeAuthoringTypeMapping() +{ +ComWrappersSupport.RegisterAuthoringMetadataTypeLookup(new Func(GetMetadataTypeMapping)); +} +} +})", + bind([&](writer& w) { + for (auto&& [key, value] : authoredTypeNameToMetadataTypeNameMap) + { + w.write(R"(Type _ when type == typeof(%) => typeof(%),)", key, value); + w.write("\n"); + } + })); + metadataMappingTypeWriter.flush_to_file(settings.output_folder / "AuthoringMetadataTypeMappingHelper.cs"); + } + if (projectionFileWritten) { for (auto&& string : strings::base) @@ -411,6 +547,8 @@ ComWrappersSupport.RegisterProjectionTypeBaseTypeMapping(TypeNameToBaseTypeNameM { w.write(" error: %\n", e.what()); result = 1; + w.flush_to_console_error(); + return result; } w.flush_to_console(); @@ -421,4 +559,4 @@ ComWrappersSupport.RegisterProjectionTypeBaseTypeMapping(TypeNameToBaseTypeNameM int main(int const argc, char** argv) { return cswinrt::run(argc, argv); -} \ No newline at end of file +} diff --git a/src/cswinrt/packages.config b/src/cswinrt/packages.config index 293ccb4f7..93c9c42be 100644 --- a/src/cswinrt/packages.config +++ b/src/cswinrt/packages.config @@ -1,5 +1,5 @@  - + \ No newline at end of file diff --git a/src/cswinrt/settings.h b/src/cswinrt/settings.h index 978bfacd8..8fd59dc73 100644 --- a/src/cswinrt/settings.h +++ b/src/cswinrt/settings.h @@ -9,12 +9,17 @@ namespace cswinrt bool verbose{}; std::set include; std::set exclude; + std::set addition_exclude; winmd::reader::filter filter; + winmd::reader::filter addition_filter; bool netstandard_compat{}; bool component{}; bool internal{}; bool embedded{}; bool public_enums{}; + bool public_exclusiveto{}; + bool idic_exclusiveto{}; + bool partial_factory{}; }; extern settings_type settings; diff --git a/src/cswinrt/strings/ComInteropHelpers.cs b/src/cswinrt/strings/ComInteropHelpers.cs index 94d095d18..162c335b7 100644 --- a/src/cswinrt/strings/ComInteropHelpers.cs +++ b/src/cswinrt/strings/ComInteropHelpers.cs @@ -66,7 +66,7 @@ internal static class IWindowNativeMethods try { // We use 3 here because IWindowNative only implements IUnknown, whose only functions are AddRef, Release and QI - global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3](ThisPtr, out __retval)); + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3](ThisPtr, &__retval)); return __retval; } finally @@ -485,3 +485,69 @@ public static DisplayInformation GetForMonitor(IntPtr monitor) } #endif } + +namespace Windows.ApplicationModel.DataTransfer +{ +#if UAC_VERSION_1 +#if NET + [global::System.Runtime.Versioning.SupportedOSPlatform("Windows10.0.10240.0")] +#endif +#if EMBED + internal +#else + public +#endif + static class DataTransferManagerInterop + { + private static readonly global::System.Guid riid = new global::System.Guid(0xA5CAEE9B, 0x8708, 0x49D1, 0x8D, 0x36, 0x67, 0xD2, 0x5A, 0x8D, 0xA0, 0x0C); + +#if NET + private static global::WinRT.IObjectReference objectReference = global::WinRT.ActivationFactory.Get("Windows.ApplicationModel.DataTransfer.DataTransferManager", new global::System.Guid(0x3A3DCD6C, 0x3EAB, 0x43DC, 0xBC, 0xDE, 0x45, 0x67, 0x1C, 0xE8, 0x00, 0xC8)); +#else + private static global::WinRT.ObjectReference objectReference = global::WinRT.ActivationFactory.Get("Windows.ApplicationModel.DataTransfer.DataTransferManager", new global::System.Guid(0x3A3DCD6C, 0x3EAB, 0x43DC, 0xBC, 0xDE, 0x45, 0x67, 0x1C, 0xE8, 0x00, 0xC8)); +#endif + + public static global::Windows.ApplicationModel.DataTransfer.DataTransferManager GetForWindow(global::System.IntPtr appWindow) + { + return IDataTransferManagerInteropMethods.GetForWindow(objectReference, appWindow, riid); + } + + public static void ShowShareUIForWindow(global::System.IntPtr appWindow) + { + IDataTransferManagerInteropMethods.ShowShareUIForWindow(objectReference, appWindow); + } + } +#endif + + internal static class IDataTransferManagerInteropMethods + { + internal static unsafe global::Windows.ApplicationModel.DataTransfer.DataTransferManager GetForWindow(global::WinRT.IObjectReference _obj, global::System.IntPtr appWindow, in global::System.Guid riid) + { + global::System.IntPtr thisPtr = _obj.ThisPtr; + global::System.IntPtr ptr = default; + + + try + { + // IDataTransferManagerInterop inherits IUnknown (3 functions) and provides GetForWindow giving a total of 5 functions + fixed(Guid* _riid = &riid) + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[3](thisPtr, appWindow, _riid, &ptr)); + GC.KeepAlive(_obj); + return global::WinRT.MarshalInspectable.FromAbi(ptr); + } + finally + { + global::WinRT.MarshalInspectable.DisposeAbi(ptr); + } + } + + internal static unsafe void ShowShareUIForWindow(global::WinRT.IObjectReference _obj, global::System.IntPtr appWindow) + { + global::System.IntPtr thisPtr = _obj.ThisPtr; + + // IDataTransferManagerInterop inherits IUnknown (3 functions) and provides ShowShareUIForWindow giving a total of 5 functions + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)thisPtr)[4](thisPtr, appWindow)); + GC.KeepAlive(_obj); + } + } +} diff --git a/src/cswinrt/strings/InitializeProjection.cs b/src/cswinrt/strings/InitializeProjection.cs new file mode 100644 index 000000000..dc20c3c4c --- /dev/null +++ b/src/cswinrt/strings/InitializeProjection.cs @@ -0,0 +1,34 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Runtime.CompilerServices; + +#pragma warning disable CA2255 + +#if !NET +namespace System.Runtime.CompilerServices +{ + [AttributeUsage(AttributeTargets.Method)] + internal sealed class ModuleInitializerAttribute : Attribute + { + } +} +#endif + +namespace WinRT +{ + /// + /// Contains a module initializer for generated CsWinRT projections. + /// + internal static class ProjectionInitializer + { + /// + /// The module initializer registering the current assembly via . + /// + [ModuleInitializer] + public static void InitializeProjection() + { + ComWrappersSupport.RegisterProjectionAssembly(typeof(ProjectionInitializer).Assembly); + } + } +} diff --git a/src/cswinrt/strings/WinRT.cs b/src/cswinrt/strings/WinRT.cs deleted file mode 100644 index 4f38e7f9e..000000000 --- a/src/cswinrt/strings/WinRT.cs +++ /dev/null @@ -1,884 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; -using System.Threading; -using WinRT.Interop; - -#pragma warning disable 0169 // The field 'xxx' is never used -#pragma warning disable 0649 // Field 'xxx' is never assigned to, and will always have its default value -#pragma warning disable CA1060 - -namespace WinRT -{ - internal static class DelegateExtensions - { - public static void DynamicInvokeAbi(this System.Delegate del, object[] invoke_params) - { - Marshal.ThrowExceptionForHR((int)del.DynamicInvoke(invoke_params)); - } - } - - internal sealed class Platform - { - [DllImport("api-ms-win-core-com-l1-1-0.dll")] - internal static extern unsafe int CoCreateInstance(ref Guid clsid, IntPtr outer, uint clsContext, ref Guid iid, IntPtr* instance); - - [DllImport("api-ms-win-core-com-l1-1-0.dll")] - internal static extern int CoDecrementMTAUsage(IntPtr cookie); - - [DllImport("api-ms-win-core-com-l1-1-0.dll")] - internal static extern unsafe int CoIncrementMTAUsage(IntPtr* cookie); - - [DllImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool FreeLibrary(IntPtr moduleHandle); - - [DllImport("kernel32.dll", EntryPoint = "GetProcAddress", SetLastError = true, BestFitMapping = false)] - internal static unsafe extern void* TryGetProcAddress(IntPtr moduleHandle, [MarshalAs(UnmanagedType.LPStr)] string functionName); - internal static unsafe void* GetProcAddress(IntPtr moduleHandle, string functionName) - { - void* functionPtr = Platform.TryGetProcAddress(moduleHandle, functionName); - if (functionPtr == null) - { - Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error(), new IntPtr(-1)); - } - return functionPtr; - } - - [DllImport("kernel32.dll", SetLastError = true)] - internal static extern IntPtr LoadLibraryExW([MarshalAs(UnmanagedType.LPWStr)] string fileName, IntPtr fileHandle, uint flags); - - [DllImport("api-ms-win-core-winrt-l1-1-0.dll")] - internal static extern unsafe int RoGetActivationFactory(IntPtr runtimeClassId, ref Guid iid, IntPtr* factory); - - [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] - internal static extern unsafe int WindowsCreateString([MarshalAs(UnmanagedType.LPWStr)] string sourceString, - int length, - IntPtr* hstring); - - [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] - internal static extern unsafe int WindowsCreateStringReference(char* sourceString, - int length, - IntPtr* hstring_header, - IntPtr* hstring); - - [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] - internal static extern int WindowsDeleteString(IntPtr hstring); - - [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] - internal static extern unsafe int WindowsDuplicateString(IntPtr sourceString, - IntPtr* hstring); - - [DllImport("api-ms-win-core-winrt-string-l1-1-0.dll", CallingConvention = CallingConvention.StdCall)] - internal static extern unsafe char* WindowsGetStringRawBuffer(IntPtr hstring, uint* length); - - [DllImport("api-ms-win-core-com-l1-1-1.dll", CallingConvention = CallingConvention.StdCall)] - internal static extern unsafe int RoGetAgileReference(uint options, ref Guid iid, IntPtr unknown, IntPtr* agileReference); - } - - internal struct VftblPtr - { - public IntPtr Vftbl; - } - - internal unsafe sealed class DllModule - { - readonly string _fileName; - readonly IntPtr _moduleHandle; - readonly delegate* unmanaged[Stdcall] _GetActivationFactory; - readonly delegate* unmanaged[Stdcall] _CanUnloadNow; // TODO: Eventually periodically call - - static readonly string _currentModuleDirectory = AppContext.BaseDirectory; - - static Dictionary _cache = new System.Collections.Generic.Dictionary(StringComparer.Ordinal); - - public static bool TryLoad(string fileName, out DllModule module) - { - lock (_cache) - { - if (_cache.TryGetValue(fileName, out module)) - { - return true; - } - else if (TryCreate(fileName, out module)) - { - _cache[fileName] = module; - return true; - } - return false; - } - } - - private static unsafe bool TryCreate(string fileName, out DllModule module) - { - // Explicitly look for module in the same directory as this one, and - // use altered search path to ensure any dependencies in the same directory are found. - var moduleHandle = Platform.LoadLibraryExW(System.IO.Path.Combine(_currentModuleDirectory, fileName), IntPtr.Zero, /* LOAD_WITH_ALTERED_SEARCH_PATH */ 8); -#if NET - if (moduleHandle == IntPtr.Zero) - { - NativeLibrary.TryLoad(fileName, Assembly.GetExecutingAssembly(), null, out moduleHandle); - } -#endif - if (moduleHandle == IntPtr.Zero) - { - module = null; - return false; - } - - var getActivationFactory = Platform.TryGetProcAddress(moduleHandle, "DllGetActivationFactory"); - if (getActivationFactory == null) - { - module = null; - return false; - } - - module = new DllModule( - fileName, - moduleHandle, - getActivationFactory); - return true; - } - - private DllModule(string fileName, IntPtr moduleHandle, void* getActivationFactory) - { - _fileName = fileName; - _moduleHandle = moduleHandle; - _GetActivationFactory = (delegate* unmanaged[Stdcall])getActivationFactory; - - var canUnloadNow = Platform.TryGetProcAddress(_moduleHandle, "DllCanUnloadNow"); - if (canUnloadNow != null) - { - _CanUnloadNow = (delegate* unmanaged[Stdcall])canUnloadNow; - } - } - - public unsafe (ObjectReference obj, int hr) GetActivationFactory(string runtimeClassId) - { - IntPtr instancePtr = IntPtr.Zero; - try - { - MarshalString.Pinnable __runtimeClassId = new(runtimeClassId); - fixed (void* ___runtimeClassId = __runtimeClassId) - { - int hr = _GetActivationFactory(MarshalString.GetAbi(ref __runtimeClassId), &instancePtr); - if (hr == 0) - { - var objRef = ComWrappersSupport.GetObjectReferenceForInterface(instancePtr); - return (objRef, hr); - } - else - { - return (null, hr); - } - } - } - finally - { - MarshalInspectable.DisposeAbi(instancePtr); - } - } - - ~DllModule() - { - System.Diagnostics.Debug.Assert(_CanUnloadNow == null || _CanUnloadNow() == 0); // S_OK - lock (_cache) - { - _cache.Remove(_fileName); - } - if ((_moduleHandle != IntPtr.Zero) && !Platform.FreeLibrary(_moduleHandle)) - { - Marshal.ThrowExceptionForHR(Marshal.GetHRForLastWin32Error()); - } - } - } - - internal sealed class WinrtModule - { - readonly IntPtr _mtaCookie; - volatile static WinrtModule _instance; - private static WinrtModule MakeWinRTModule() - { - global::System.Threading.Interlocked.CompareExchange(ref _instance, new WinrtModule(), null); - return _instance; - } - public static WinrtModule Instance => _instance ?? MakeWinRTModule(); - - public unsafe WinrtModule() - { - IntPtr mtaCookie; - Marshal.ThrowExceptionForHR(Platform.CoIncrementMTAUsage(&mtaCookie)); - _mtaCookie = mtaCookie; - } - - public static unsafe (IntPtr instancePtr, int hr) GetActivationFactory(IntPtr hstrRuntimeClassId) - { - var module = Instance; // Ensure COM is initialized - Guid iid = IActivationFactoryVftbl.IID; - IntPtr instancePtr; - int hr = Platform.RoGetActivationFactory(hstrRuntimeClassId, ref iid, &instancePtr); - return (hr == 0 ? instancePtr : IntPtr.Zero, hr); - } - - public static unsafe (ObjectReference obj, int hr) GetActivationFactory(string runtimeClassId) - { - IntPtr instancePtr = IntPtr.Zero; - try - { - MarshalString.Pinnable __runtimeClassId = new(runtimeClassId); - fixed (void* ___runtimeClassId = __runtimeClassId) - { - int hr; - (instancePtr, hr) = GetActivationFactory(MarshalString.GetAbi(ref __runtimeClassId)); - if (hr == 0) - { - var objRef = ComWrappersSupport.GetObjectReferenceForInterface(instancePtr); - return (objRef, hr); - } - else - { - return (null, hr); - } - } - } - finally - { - MarshalInspectable.DisposeAbi(instancePtr); - } - } - - ~WinrtModule() - { - Marshal.ThrowExceptionForHR(Platform.CoDecrementMTAUsage(_mtaCookie)); - } - } - - internal class BaseActivationFactory - { - private readonly ObjectReference _IActivationFactory; - - public ObjectReference Value { get => _IActivationFactory; } - - public I AsInterface() => _IActivationFactory.AsInterface(); - - public BaseActivationFactory(string typeNamespace, string typeFullName) - { - // Prefer the RoGetActivationFactory HRESULT failure over the LoadLibrary/etc. failure - int hr; - (_IActivationFactory, hr) = WinrtModule.GetActivationFactory(typeFullName); - if (_IActivationFactory != null) { return; } - - var moduleName = typeNamespace; - while (true) - { - DllModule module = null; - if (DllModule.TryLoad(moduleName + ".dll", out module)) - { - (_IActivationFactory, _) = module.GetActivationFactory(typeFullName); - if (_IActivationFactory != null) { return; } - } - - var lastSegment = moduleName.LastIndexOf(".", StringComparison.Ordinal); - if (lastSegment <= 0) - { - Marshal.ThrowExceptionForHR(hr); - } - moduleName = moduleName.Remove(lastSegment); - } - } - -#if NET - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:RequiresUnreferencedCode", - Justification = "No members of the generic type are dynamically accessed in this code path.")] -#endif - public unsafe ObjectReference _ActivateInstance() - { - IntPtr instancePtr; - Marshal.ThrowExceptionForHR(_IActivationFactory.Vftbl.ActivateInstance(_IActivationFactory.ThisPtr, &instancePtr)); - try - { - return ComWrappersSupport.GetObjectReferenceForInterface(instancePtr); - } - finally - { - MarshalInspectable.DisposeAbi(instancePtr); - } - } - -#if NET - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:RequiresUnreferencedCode", - Justification = "No members of the generic type are dynamically accessed in this code path.")] -#endif - public ObjectReference _As() => _IActivationFactory.As(); - public IObjectReference _As(Guid iid) => _IActivationFactory.As(iid); - } - - internal sealed class ActivationFactory : BaseActivationFactory - { - public ActivationFactory() : base(typeof(T).Namespace, typeof(T).FullName) { } - - static readonly ActivationFactory _factory = new ActivationFactory(); - public static new I AsInterface() => _factory.Value.AsInterface(); - public static ObjectReference As() => _factory._As(); - public static IObjectReference As(Guid iid) => _factory._As(iid); - -#if NET - [UnconditionalSuppressMessage("ReflectionAnalysis", "IL2091:RequiresUnreferencedCode", - Justification = "No members of the generic type are dynamically accessed in this code path.")] -#endif - public static ObjectReference ActivateInstance< -#if NET - [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.None)] -#endif - I>() => _factory._ActivateInstance(); - } - - internal class ComponentActivationFactory : global::WinRT.Interop.IActivationFactory - { - public IntPtr ActivateInstance() - { - throw new NotImplementedException(); - } - } - - internal class ActivatableComponentActivationFactory : ComponentActivationFactory, global::WinRT.Interop.IActivationFactory where T : class, new() - { - public new IntPtr ActivateInstance() - { - T comp = new T(); - return MarshalInspectable.FromManaged(comp); - } - } - -#pragma warning disable CA2002 - - // Registration state and delegate cached separately to survive EventSource garbage collection - // and to prevent the generated event delegate from impacting the lifetime of the - // event source. - internal abstract class State : IDisposable - { - public EventRegistrationToken token; - public System.Delegate del; - public System.Delegate eventInvoke; - private bool disposedValue; - private readonly IntPtr obj; - private readonly int index; - private readonly System.WeakReference cacheEntry; - private IntPtr eventInvokePtr; - private IntPtr referenceTrackerTargetPtr; - - protected State(IntPtr obj, int index) - { - this.obj = obj; - this.index = index; - eventInvoke = GetEventInvoke(); - cacheEntry = new System.WeakReference(this); - } - - // The lifetime of this object is managed by the delegate / eventInvoke - // through its target reference to it. Once the delegate no longer has - // any references, this object will also no longer have any references. - ~State() - { - Dispose(false); - } - - // Allows to retrieve a singleton like weak reference to use - // with the cache to allow for proper removal with comparision. - public System.WeakReference GetWeakReferenceForCache() - { - return cacheEntry; - } - - protected abstract System.Delegate GetEventInvoke(); - - protected virtual void Dispose(bool disposing) - { - // Uses the dispose pattern to ensure we only remove - // from the cache once: either via unsubscribe or via - // the finalizer. - if (!disposedValue) - { - Cache.Remove(obj, index, cacheEntry); - disposedValue = true; - } - } - - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - public void InitalizeReferenceTracking(IntPtr ptr) - { - eventInvokePtr = ptr; - Guid iid = IReferenceTrackerTargetVftbl.IID; - int hr = Marshal.QueryInterface(ptr, ref iid, out referenceTrackerTargetPtr); - if (hr != 0) - { - referenceTrackerTargetPtr = default; - } - else - { - // We don't want to keep ourselves alive and as long as this object - // is alive, the CCW still exists. - Marshal.Release(referenceTrackerTargetPtr); - } - } - - public unsafe bool HasComReferences() - { - if (eventInvokePtr != default) - { - IUnknownVftbl vftblIUnknown = **(IUnknownVftbl**)eventInvokePtr; - vftblIUnknown.AddRef(eventInvokePtr); - uint comRefCount = vftblIUnknown.Release(eventInvokePtr); - if (comRefCount != 0) - { - return true; - } - } - - if (referenceTrackerTargetPtr != default) - { - IReferenceTrackerTargetVftbl vftblReferenceTracker = **(IReferenceTrackerTargetVftbl**)referenceTrackerTargetPtr; - vftblReferenceTracker.AddRefFromReferenceTracker(referenceTrackerTargetPtr); - uint refTrackerCount = vftblReferenceTracker.ReleaseFromReferenceTracker(referenceTrackerTargetPtr); - if (refTrackerCount != 0) - { - // Note we can't tell if the reference tracker ref is pegged or not, so this is best effort where if there - // are any reference tracker references, we assume the event has references. - return true; - } - } - - return false; - } - } - - internal sealed class Cache - { - private Cache(IWeakReference target, int index, System.WeakReference state) - { - this.target = target; - SetState(index, state); - } - - private IWeakReference target; - private readonly ConcurrentDictionary> states = new ConcurrentDictionary>(); - - private static readonly ReaderWriterLockSlim cachesLock = new ReaderWriterLockSlim(); - private static readonly ConcurrentDictionary caches = new ConcurrentDictionary(); - - private Cache Update(IWeakReference target, int index, System.WeakReference state) - { - // If target no longer exists, destroy cache - lock (this) - { - using var resolved = this.target.Resolve(InterfaceIIDs.IUnknown_IID); - if (resolved == null) - { - this.target = target; - states.Clear(); - } - } - SetState(index, state); - return this; - } - - private System.WeakReference GetState(int index) - { - // If target no longer exists, destroy cache - lock (this) - { - using var resolved = this.target.Resolve(InterfaceIIDs.IUnknown_IID); - if (resolved == null) - { - return null; - } - } - - if (states.TryGetValue(index, out var weakState)) - { - return weakState; - } - return null; - } - - private void SetState(int index, System.WeakReference state) - { - states[index] = state; - } - - public static void Create(IObjectReference obj, int index, System.WeakReference state) - { - // If event source implements weak reference support, track event registrations so that - // unsubscribes will work across garbage collections. Note that most static/factory classes - // do not implement IWeakReferenceSource, so static codegen caching approach is also used. - IWeakReference target = null; -#if !NET - try - { - var weakRefSource = (IWeakReferenceSource)typeof(IWeakReferenceSource).GetHelperType().GetConstructor(new[] { typeof(IObjectReference) }).Invoke(new object[] { obj }); - if (weakRefSource == null) - { - return; - } - target = weakRefSource.GetWeakReference(); - } - catch(Exception) - { - return; - } -#else - int hr = obj.TryAs(InterfaceIIDs.IWeakReferenceSource_IID, out var weakRefSource); - if (hr != 0) - { - return; - } - - target = ABI.WinRT.Interop.IWeakReferenceSourceMethods.GetWeakReference(weakRefSource); -#endif - - cachesLock.EnterReadLock(); - try - { - caches.AddOrUpdate(obj.ThisPtr, - (IntPtr ThisPtr) => new Cache(target, index, state), - (IntPtr ThisPtr, Cache cache) => cache.Update(target, index, state)); - } - finally - { - cachesLock.ExitReadLock(); - } - } - - public static System.WeakReference GetState(IObjectReference obj, int index) - { - if (caches.TryGetValue(obj.ThisPtr, out var cache)) - { - return cache.GetState(index); - } - - return null; - } - - public static void Remove(IntPtr thisPtr, int index, System.WeakReference state) - { - if (caches.TryGetValue(thisPtr, out var cache)) - { -#if !NET - // https://devblogs.microsoft.com/pfxteam/little-known-gems-atomic-conditional-removals-from-concurrentdictionary/ - ((ICollection>>)cache.states).Remove( - new KeyValuePair>(index, state)); -#else - cache.states.TryRemove(new KeyValuePair>(index, state)); -#endif - // using double-checked lock idiom - if (cache.states.IsEmpty) - { - cachesLock.EnterWriteLock(); - try - { - if (cache.states.IsEmpty) - { - caches.TryRemove(thisPtr, out var _); - } - } - finally - { - cachesLock.ExitWriteLock(); - } - } - } - } - } - - internal unsafe abstract class EventSource - where TDelegate : class, MulticastDelegate - { - protected readonly IObjectReference _obj; - protected readonly int _index; - readonly delegate* unmanaged[Stdcall] _addHandler; - readonly delegate* unmanaged[Stdcall] _removeHandler; - protected System.WeakReference _state; - private readonly (Action, Action) _handlerTuple; - - protected EventSource(IObjectReference obj, - delegate* unmanaged[Stdcall] addHandler, - delegate* unmanaged[Stdcall] removeHandler, - int index = 0) - { - _obj = obj; - _addHandler = addHandler; - _removeHandler = removeHandler; - _index = index; - _state = Cache.GetState(obj, index); - _handlerTuple = (Subscribe, Unsubscribe); - } - - protected abstract ObjectReferenceValue CreateMarshaler(TDelegate del); - - protected abstract State CreateEventState(); - - public void Subscribe(TDelegate del) - { - lock (this) - { - State state = null; - bool registerHandler = - _state is null || - !_state.TryGetTarget(out state) || - // We have a wrapper delegate, but no longer has any references from any event source. - !state.HasComReferences(); - if (registerHandler) - { - state = CreateEventState(); - _state = state.GetWeakReferenceForCache(); - Cache.Create(_obj, _index, _state); - } - - state.del = (TDelegate)global::System.Delegate.Combine(state.del, del); - if (registerHandler) - { - var eventInvoke = (TDelegate)state.eventInvoke; - var marshaler = CreateMarshaler(eventInvoke); - try - { - var nativeDelegate = marshaler.GetAbi(); - state.InitalizeReferenceTracking(nativeDelegate); - ExceptionHelpers.ThrowExceptionForHR(_addHandler(_obj.ThisPtr, nativeDelegate, out state.token)); - } - finally - { - // Dispose our managed reference to the delegate's CCW. - // Either the native event holds a reference now or the _addHandler call failed. - marshaler.Dispose(); - } - } - } - } - - public void Unsubscribe(TDelegate del) - { - if (_state is null || !_state.TryGetTarget(out var state)) - { - return; - } - - lock (this) - { - var oldEvent = state.del; - state.del = (TDelegate)global::System.Delegate.Remove(state.del, del); - if (oldEvent is object && state.del is null) - { - UnsubscribeFromNative(state); - } - } - } - - public (Action, Action) EventActions => _handlerTuple; - - private void UnsubscribeFromNative(State state) - { - ExceptionHelpers.ThrowExceptionForHR(_removeHandler(_obj.ThisPtr, state.token)); - state.Dispose(); - _state = null; - } - } - - internal unsafe sealed class EventSource__EventHandler : EventSource> - { - internal EventSource__EventHandler(IObjectReference obj, - delegate* unmanaged[Stdcall] addHandler, - delegate* unmanaged[Stdcall] removeHandler, - int index) : base(obj, addHandler, removeHandler, index) - { - } - - protected override ObjectReferenceValue CreateMarshaler(System.EventHandler del) => - ABI.System.EventHandler.CreateMarshaler2(del); - - protected override State CreateEventState() => - new EventState(_obj.ThisPtr, _index); - - private sealed class EventState : State - { - public EventState(IntPtr obj, int index) - : base(obj, index) - { - } - - protected override Delegate GetEventInvoke() - { - System.EventHandler handler = (System.Object obj, T e) => - { - var localDel = (System.EventHandler) del; - if (localDel != null) - localDel.Invoke(obj, e); - }; - return handler; - } - } - } - -#pragma warning restore CA2002 - - // An event registration token table stores mappings from delegates to event tokens, in order to support - // sourcing WinRT style events from managed code. - internal sealed class EventRegistrationTokenTable where T : class, global::System.Delegate - { - // Note this dictionary is also used as the synchronization object for this table - private readonly Dictionary m_tokens = new Dictionary(); - - public EventRegistrationToken AddEventHandler(T handler) - { - // Windows Runtime allows null handlers. Assign those the default token (token value 0) for simplicity - if (handler == null) - { - return default; - } - - lock (m_tokens) - { - return AddEventHandlerNoLock(handler); - } - } - - private EventRegistrationToken AddEventHandlerNoLock(T handler) - { - Debug.Assert(handler != null); - - // Get a registration token, making sure that we haven't already used the value. This should be quite - // rare, but in the case it does happen, just keep trying until we find one that's unused. - EventRegistrationToken token = GetPreferredToken(handler); - while (m_tokens.ContainsKey(token)) - { - token = new EventRegistrationToken { Value = token.Value + 1 }; - } - m_tokens[token] = handler; - - return token; - } - - // Generate a token that may be used for a particular event handler. We will frequently be called - // upon to look up a token value given only a delegate to start from. Therefore, we want to make - // an initial token value that is easily determined using only the delegate instance itself. Although - // in the common case this token value will be used to uniquely identify the handler, it is not - // the only possible token that can represent the handler. - // - // This means that both: - // * if there is a handler assigned to the generated initial token value, it is not necessarily - // this handler. - // * if there is no handler assigned to the generated initial token value, the handler may still - // be registered under a different token - // - // Effectively the only reasonable thing to do with this value is either to: - // 1. Use it as a good starting point for generating a token for handler - // 2. Use it as a guess to quickly see if the handler was really assigned this token value - private static EventRegistrationToken GetPreferredToken(T handler) - { - Debug.Assert(handler != null); - - // We want to generate a token value that has the following properties: - // 1. is quickly obtained from the handler instance - // 2. uses bits in the upper 32 bits of the 64 bit value, in order to avoid bugs where code - // may assume the value is really just 32 bits - // 3. uses bits in the bottom 32 bits of the 64 bit value, in order to ensure that code doesn't - // take a dependency on them always being 0. - // - // The simple algorithm chosen here is to simply assign the upper 32 bits the metadata token of the - // event handler type, and the lower 32 bits the hash code of the handler instance itself. Using the - // metadata token for the upper 32 bits gives us at least a small chance of being able to identify a - // totally corrupted token if we ever come across one in a minidump or other scenario. - // - // The hash code of a unicast delegate is not tied to the method being invoked, so in the case - // of a unicast delegate, the hash code of the target method is used instead of the full delegate - // hash code. - // - // While calculating this initial value will be somewhat more expensive than just using a counter - // for events that have few registrations, it will also give us a shot at preventing unregistration - // from becoming an O(N) operation. - // - // We should feel free to change this algorithm as other requirements / optimizations become - // available. This implementation is sufficiently random that code cannot simply guess the value to - // take a dependency upon it. (Simply applying the hash-value algorithm directly won't work in the - // case of collisions, where we'll use a different token value). - - uint handlerHashCode; - global::System.Delegate[] invocationList = ((global::System.Delegate)(object)handler).GetInvocationList(); - if (invocationList.Length == 1) - { - handlerHashCode = (uint)invocationList[0].Method.GetHashCode(); - } - else - { - handlerHashCode = (uint)handler.GetHashCode(); - } - - ulong tokenValue = ((ulong)(uint)typeof(T).MetadataToken << 32) | handlerHashCode; - return new EventRegistrationToken { Value = (long)tokenValue }; - } - - // Remove the event handler from the table and - // Get the delegate associated with an event registration token if it exists - // If the event registration token is not registered, returns false - public bool RemoveEventHandler(EventRegistrationToken token, out T handler) - { - lock (m_tokens) - { - if (m_tokens.TryGetValue(token, out handler)) - { - RemoveEventHandlerNoLock(token); - return true; - } - } - - return false; - } - - private void RemoveEventHandlerNoLock(EventRegistrationToken token) - { - if (m_tokens.TryGetValue(token, out T handler)) - { - m_tokens.Remove(token); - } - } - } - - internal static class InterfaceIIDs - { - internal static readonly Guid IInspectable_IID = new(0xAF86E2E0, 0xB12D, 0x4c6a, 0x9C, 0x5A, 0xD7, 0xAA, 0x65, 0x10, 0x1E, 0x90); - internal static readonly Guid IUnknown_IID = new(0, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46); - internal static readonly Guid IWeakReferenceSource_IID = new(0x00000038, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46); - } -} - -#if !NET -namespace System.Runtime.CompilerServices -{ - [AttributeUsage(AttributeTargets.Method)] - internal sealed class ModuleInitializerAttribute : Attribute { } -} -#endif - -namespace WinRT -{ - internal static class ProjectionInitializer - { - [ModuleInitializer] - internal static void InitalizeProjection() - { - ComWrappersSupport.RegisterProjectionAssembly(typeof(ProjectionInitializer).Assembly); - } - } -} diff --git a/src/cswinrt/strings/WinRT_Interop.cs b/src/cswinrt/strings/WinRT_Interop.cs deleted file mode 100644 index 38266aed5..000000000 --- a/src/cswinrt/strings/WinRT_Interop.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) Microsoft Corporation. -// Licensed under the MIT License. - - -using System; -using System.Runtime.InteropServices; - -#pragma warning disable CS0649 - -namespace WinRT.Interop -{ - // IActivationFactory - [Guid("00000035-0000-0000-C000-000000000046")] - internal unsafe struct IActivationFactoryVftbl - { - public IInspectable.Vftbl IInspectableVftbl; - private void* _ActivateInstance; - public delegate* unmanaged[Stdcall] ActivateInstance => (delegate* unmanaged[Stdcall])_ActivateInstance; - - internal static readonly Guid IID = new(0x00000035, 0, 0, 0xC0, 0, 0, 0, 0, 0, 0, 0x46); - } - - // IDelegate - internal struct IDelegateVftbl - { - public IUnknownVftbl IUnknownVftbl; - public IntPtr Invoke; - } - - [Guid("64BD43F8-bFEE-4EC4-B7EB-2935158DAE21")] - [StructLayout(LayoutKind.Sequential)] - internal unsafe struct IReferenceTrackerTargetVftbl - { - public global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; - private void* _AddRefFromReferenceTracker; - public delegate* unmanaged[Stdcall] AddRefFromReferenceTracker => (delegate* unmanaged[Stdcall])_AddRefFromReferenceTracker; - private void* _ReleaseFromReferenceTracker; - public delegate* unmanaged[Stdcall] ReleaseFromReferenceTracker => (delegate* unmanaged[Stdcall])_ReleaseFromReferenceTracker; - private void* _Peg; - private void* _Unpeg; - - internal static readonly Guid IID = new(0x64BD43F8, 0xbFEE, 0x4EC4, 0xB7, 0xEB, 0x29, 0x35, 0x15, 0x8D, 0xAE, 0x21); - } - -} diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Controls.Primitives/Microsoft.UI.Xaml.Controls.Primitives.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Controls.Primitives/Microsoft.UI.Xaml.Controls.Primitives.cs index 6fc65bf3a..f0ecbcf61 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Controls.Primitives/Microsoft.UI.Xaml.Controls.Primitives.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Controls.Primitives/Microsoft.UI.Xaml.Controls.Primitives.cs @@ -1,10 +1,13 @@ namespace Microsoft.UI.Xaml.Controls.Primitives { - using Windows.Foundation; + using global::Windows.Foundation; [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Controls.Primitives.GeneratorPosition))] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Controls.Primitives.GeneratorPosition))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif [StructLayout(LayoutKind.Sequential)] #if EMBED internal diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.cs index f267a9613..1ad2d3305 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Animation/Microsoft.UI.Xaml.Media.Animation.cs @@ -1,10 +1,13 @@ namespace Microsoft.UI.Xaml.Media.Animation { - using Windows.Foundation; + using global::Windows.Foundation; [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Media.Animation.KeyTime))] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Media.Animation.KeyTime))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif [StructLayout(LayoutKind.Sequential)] #if EMBED internal @@ -79,6 +82,9 @@ public TimeSpan TimeSpan } [global::WinRT.WindowsRuntimeType("Microsoft.UI")] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.EnumTypeDetails))] +#endif #if EMBED internal #else @@ -92,7 +98,10 @@ enum RepeatBehaviorType } [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Media.Animation.RepeatBehavior))] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Media.Animation.RepeatBehavior))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif [StructLayout(LayoutKind.Sequential)] #if EMBED internal diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.cs index 88ff2ccd8..fdf1a9974 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media.Media3D/Microsoft.UI.Xaml.Media.Media3D.cs @@ -1,10 +1,13 @@ namespace Microsoft.UI.Xaml.Media.Media3D { - using Windows.Foundation; + using global::Windows.Foundation; [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Media.Media3D.Matrix3D))] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Media.Media3D.Matrix3D))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif [StructLayout(LayoutKind.Sequential)] #if EMBED internal diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.cs index e8f91861b..b361bcbab 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml.Media/Microsoft.UI.Xaml.Media.cs @@ -1,10 +1,13 @@ namespace Microsoft.UI.Xaml.Media { - using Windows.Foundation; + using global::Windows.Foundation; [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Media.Matrix))] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Media.Matrix))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif [StructLayout(LayoutKind.Sequential)] #if EMBED internal diff --git a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.cs b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.cs index f901fb1ad..d9388a8f1 100644 --- a/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.cs +++ b/src/cswinrt/strings/additions/Microsoft.UI.Xaml/Microsoft.UI.Xaml.cs @@ -1,10 +1,13 @@ namespace Microsoft.UI.Xaml { - using Windows.Foundation; + using global::Windows.Foundation; [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.CornerRadius))] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.CornerRadius))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif [StructLayout(LayoutKind.Sequential)] #if EMBED internal @@ -152,6 +155,9 @@ public double BottomLeft } [global::WinRT.WindowsRuntimeType("Microsoft.UI")] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.EnumTypeDetails))] +#endif #if EMBED internal #else @@ -165,7 +171,10 @@ enum GridUnitType } [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.GridLength))] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.GridLength))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif [StructLayout(LayoutKind.Sequential)] #if EMBED internal @@ -283,7 +292,10 @@ internal string ToString(global::System.Globalization.CultureInfo cultureInfo) } [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Thickness))] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Thickness))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif [StructLayout(LayoutKind.Sequential)] #if EMBED internal @@ -397,6 +409,9 @@ public override int GetHashCode() } [global::WinRT.WindowsRuntimeType("Microsoft.UI")] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.EnumTypeDetails))] +#endif #if EMBED internal #else @@ -410,7 +425,10 @@ enum DurationType } [global::WinRT.WindowsRuntimeType("Microsoft.UI")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Duration))] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Microsoft.UI.Xaml.Duration))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif [StructLayout(LayoutKind.Sequential)] #if EMBED internal diff --git a/src/cswinrt/strings/additions/Windows.Foundation/AsyncInfo.cs b/src/cswinrt/strings/additions/Windows.Foundation/AsyncInfo.cs index c43aaac31..81378030f 100644 --- a/src/cswinrt/strings/additions/Windows.Foundation/AsyncInfo.cs +++ b/src/cswinrt/strings/additions/Windows.Foundation/AsyncInfo.cs @@ -121,28 +121,28 @@ public static IAsyncOperationWithProgress Run CreateCompletedAction() + public static IAsyncActionWithProgress CompletedActionWithProgress() { var asyncInfo = new TaskToAsyncActionWithProgressAdapter(isCanceled: false); return asyncInfo; } - internal static IAsyncOperation CreateCompletedOperation(TResult synchronousResult) + public static IAsyncOperation FromResult(TResult synchronousResult) { var asyncInfo = new TaskToAsyncOperationAdapter(synchronousResult); return asyncInfo; } - internal static IAsyncOperationWithProgress CreateCompletedOperation(TResult synchronousResult) + public static IAsyncOperationWithProgress FromResultWithProgress(TResult synchronousResult) { var asyncInfo = new TaskToAsyncOperationWithProgressAdapter(synchronousResult); return asyncInfo; @@ -150,10 +150,9 @@ internal static IAsyncOperationWithProgress CreateCompletedO #endregion Factory methods for creating IAsyncInfo instances that have already completed synchronously - #region Factory methods for creating IAsyncInfo instances that have already completed synchronously with an error - internal static IAsyncAction CreateFaultedAction(Exception error) + public static IAsyncAction FromException(Exception error) { if (error == null) throw new ArgumentNullException(nameof(error)); @@ -167,7 +166,7 @@ internal static IAsyncAction CreateFaultedAction(Exception error) } - internal static IAsyncActionWithProgress CreateFaultedAction(Exception error) + public static IAsyncActionWithProgress FromExceptionWithProgress(Exception error) { if (error == null) throw new ArgumentNullException(nameof(error)); @@ -181,7 +180,7 @@ internal static IAsyncActionWithProgress CreateFaultedAction CreateFaultedOperation(Exception error) + public static IAsyncOperation FromException(Exception error) { if (error == null) throw new ArgumentNullException(nameof(error)); @@ -195,7 +194,7 @@ internal static IAsyncOperation CreateFaultedOperation(Excepti } - internal static IAsyncOperationWithProgress CreateFaultedOperation(Exception error) + public static IAsyncOperationWithProgress FromExceptionWithProgress(Exception error) { if (error == null) throw new ArgumentNullException(nameof(error)); @@ -207,7 +206,39 @@ internal static IAsyncOperationWithProgress CreateFaultedOpe return asyncInfo; } + #endregion Factory methods for creating IAsyncInfo instances that have already completed synchronously with an error + #region Factory methods for creating IAsyncInfo instances that have already been canceled synchronously + + public static IAsyncAction CanceledAction() + { + var asyncInfo = new TaskToAsyncActionAdapter(isCanceled: true); + return asyncInfo; + } + + + public static IAsyncActionWithProgress CanceledActionWithProgress() + { + var asyncInfo = new TaskToAsyncActionWithProgressAdapter(isCanceled: true); + return asyncInfo; + } + + + public static IAsyncOperation CanceledOperation() + { + var asyncInfo = new TaskToAsyncOperationAdapter(isCanceled: true); + return asyncInfo; + } + + + public static IAsyncOperationWithProgress CanceledOperationWithProgress() + { + var asyncInfo = new TaskToAsyncOperationWithProgressAdapter(isCanceled: true); + return asyncInfo; + } + + #endregion Factory methods for creating IAsyncInfo instances that have already been canceled synchronously + } // class AsyncInfo } // namespace diff --git a/src/cswinrt/strings/additions/Windows.Foundation/ExceptionDispatchHelper.cs b/src/cswinrt/strings/additions/Windows.Foundation/ExceptionDispatchHelper.cs index a083659d2..5515b915f 100644 --- a/src/cswinrt/strings/additions/Windows.Foundation/ExceptionDispatchHelper.cs +++ b/src/cswinrt/strings/additions/Windows.Foundation/ExceptionDispatchHelper.cs @@ -1,56 +1,66 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. - - -namespace System.Threading.Tasks -{ - using System; - using System.Runtime.ExceptionServices; - using System.Threading; - internal static class ExceptionDispatchHelper - { - internal static void ThrowAsync(Exception exception, SynchronizationContext targetContext) - { - if (exception == null) - return; - - // TODO - decide how to cleanly do it so it lights up if TA is defined - //if (exception is ThreadAbortException) - // return; - - - ExceptionDispatchInfo exceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception); - - if (targetContext != null) - { - try - { - targetContext.Post((edi) => ((ExceptionDispatchInfo)edi!).Throw(), exceptionDispatchInfo); - } - catch - { - // Something went wrong in the Post; let's try using the thread pool instead: - ThrowAsync(exception, null); - } - return; - } - - bool scheduled = true; - try - { - new SynchronizationContext().Post((edi) => ((ExceptionDispatchInfo)edi!).Throw(), exceptionDispatchInfo); - } - catch - { - // Something went wrong when scheduling the thrower; we do our best by throwing the exception here: - scheduled = false; - } - - if (!scheduled) - exceptionDispatchInfo.Throw(); - } - } // ExceptionDispatchHelper -} // namespace - -// ExceptionDispatchHelper.cs +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + + +namespace System.Threading.Tasks +{ + using System; + using System.Runtime.ExceptionServices; + using System.Runtime.InteropServices; + using System.Threading; + internal static class ExceptionDispatchHelper + { + private const int RPC_E_DISCONNECTED = unchecked((int)0x80010108); + private const int RPC_S_SERVER_UNAVAILABLE = unchecked((int)0x800706BA); + private const int JSCRIPT_E_CANTEXECUTE = unchecked((int)0x89020001); + + internal static void ThrowAsync(Exception exception, SynchronizationContext targetContext) + { + if (exception == null) + return; + + // If this is an error indicating the RPC called failed, + // it is most likely due to the other process is gone. + // We ignore it as it just means we weren't able to report + // the completion and not actually an error in the other process. + if (exception is COMException comException && + comException.HResult is RPC_E_DISCONNECTED or RPC_S_SERVER_UNAVAILABLE or JSCRIPT_E_CANTEXECUTE) + { + return; + } + + ExceptionDispatchInfo exceptionDispatchInfo = ExceptionDispatchInfo.Capture(exception); + + if (targetContext != null) + { + try + { + targetContext.Post((edi) => ((ExceptionDispatchInfo)edi!).Throw(), exceptionDispatchInfo); + } + catch + { + // Something went wrong in the Post; let's try using the thread pool instead: + ThrowAsync(exception, null); + } + return; + } + + bool scheduled = true; + try + { + new SynchronizationContext().Post((edi) => ((ExceptionDispatchInfo)edi!).Throw(), exceptionDispatchInfo); + } + catch + { + // Something went wrong when scheduling the thrower; we do our best by throwing the exception here: + scheduled = false; + } + + if (!scheduled) + exceptionDispatchInfo.Throw(); + } + } // ExceptionDispatchHelper +} // namespace + +// ExceptionDispatchHelper.cs diff --git a/src/cswinrt/strings/additions/Windows.Foundation/ITaskAwareAsyncInfo.cs b/src/cswinrt/strings/additions/Windows.Foundation/ITaskAwareAsyncInfo.cs new file mode 100644 index 000000000..3f4e184a3 --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.Foundation/ITaskAwareAsyncInfo.cs @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace System.Threading.Tasks +{ + internal interface ITaskAwareAsyncInfo + { + Task Task { get; } + } +} diff --git a/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncActionAdapter.cs b/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncActionAdapter.cs index 719d76571..9a3a6ed4d 100644 --- a/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncActionAdapter.cs +++ b/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncActionAdapter.cs @@ -14,7 +14,7 @@ namespace System.Threading.Tasks #if NET [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] #endif - internal sealed class TaskToAsyncActionAdapter + internal sealed partial class TaskToAsyncActionAdapter : TaskToAsyncInfoAdapter, IAsyncAction { diff --git a/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncActionWithProgressAdapter.cs b/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncActionWithProgressAdapter.cs index e0bfe7a97..d74ec3957 100644 --- a/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncActionWithProgressAdapter.cs +++ b/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncActionWithProgressAdapter.cs @@ -14,7 +14,7 @@ namespace System.Threading.Tasks #if NET [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] #endif - internal sealed class TaskToAsyncActionWithProgressAdapter + internal sealed partial class TaskToAsyncActionWithProgressAdapter : TaskToAsyncInfoAdapter, AsyncActionProgressHandler, VoidValueTypeParameter, diff --git a/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncInfoAdapter.cs b/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncInfoAdapter.cs index 46c56111b..7b7373e1f 100644 --- a/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncInfoAdapter.cs +++ b/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncInfoAdapter.cs @@ -21,8 +21,8 @@ namespace System.Threading.Tasks #if NET [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] #endif - internal class TaskToAsyncInfoAdapter - : IAsyncInfo, IProgress + internal partial class TaskToAsyncInfoAdapter + : IAsyncInfo, IProgress, ITaskAwareAsyncInfo where TCompletedHandler : class where TProgressHandler : class { @@ -367,7 +367,7 @@ private SynchronizationContext GetStartingContext() } - internal Task Task + Task ITaskAwareAsyncInfo.Task { get { diff --git a/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncOperationAdapter.cs b/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncOperationAdapter.cs index a86e1bc0c..e8c7f0b24 100644 --- a/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncOperationAdapter.cs +++ b/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncOperationAdapter.cs @@ -15,7 +15,7 @@ namespace System.Threading.Tasks #if NET [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] #endif - internal sealed class TaskToAsyncOperationAdapter + internal sealed partial class TaskToAsyncOperationAdapter : TaskToAsyncInfoAdapter, VoidReferenceTypeParameter, TResult, VoidValueTypeParameter>, IAsyncOperation { @@ -39,6 +39,13 @@ internal TaskToAsyncOperationAdapter(TResult synchronousResult) { } + internal TaskToAsyncOperationAdapter(bool isCanceled) + : base(default(TResult)) + { + if (isCanceled) + DangerousSetCanceled(); + } + public TResult GetResults() { return GetResultsInternal(); diff --git a/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncOperationWithProgressAdapter.cs b/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncOperationWithProgressAdapter.cs index 6ff130b6b..9307b67f9 100644 --- a/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncOperationWithProgressAdapter.cs +++ b/src/cswinrt/strings/additions/Windows.Foundation/TaskToAsyncOperationWithProgressAdapter.cs @@ -15,7 +15,7 @@ namespace System.Threading.Tasks #if NET [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] #endif - internal sealed class TaskToAsyncOperationWithProgressAdapter + internal sealed partial class TaskToAsyncOperationWithProgressAdapter : TaskToAsyncInfoAdapter, AsyncOperationProgressHandler, TResult, @@ -44,6 +44,13 @@ internal TaskToAsyncOperationWithProgressAdapter(TResult synchronousResult) { } + internal TaskToAsyncOperationWithProgressAdapter(bool isCanceled) + : base(default(TResult)) + { + if (isCanceled) + DangerousSetCanceled(); + } + public TResult GetResults() { return GetResultsInternal(); diff --git a/src/cswinrt/strings/additions/Windows.Foundation/Windows.Foundation.cs b/src/cswinrt/strings/additions/Windows.Foundation/Windows.Foundation.cs index f007b075a..fe9605a1a 100644 --- a/src/cswinrt/strings/additions/Windows.Foundation/Windows.Foundation.cs +++ b/src/cswinrt/strings/additions/Windows.Foundation/Windows.Foundation.cs @@ -23,10 +23,16 @@ public static Task AsTask(this IAsyncAction source, CancellationToken cancellati if (source == null) { throw new ArgumentNullException(nameof(source)); + } + +#if NET + if (source is ITaskAwareAsyncInfo asyncInfo && asyncInfo.Task is Task task) + { + return cancellationToken.CanBeCanceled ? + task.WaitAsync(cancellationToken) : + task; } - - // TODO: Handle the scenario where the 'IAsyncAction' is actually a task (i.e. originated from native code - // but projected into an IAsyncAction) +#endif switch (source.Status) { @@ -40,9 +46,8 @@ public static Task AsTask(this IAsyncAction source, CancellationToken cancellati return Task.FromCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); } - var bridge = new AsyncInfoToTaskBridge(cancellationToken); + var bridge = new AsyncInfoToTaskBridge(source, cancellationToken); source.Completed = new AsyncActionCompletedHandler(bridge.CompleteFromAsyncAction); - bridge.RegisterForCancellation(source); return bridge.Task; } @@ -56,15 +61,26 @@ public static TaskAwaiter GetAwaiter(this IAsyncAction source) return AsTask(source).GetAwaiter(); } + public static void Wait(this IAsyncAction source) + { + AsTask(source).Wait(); + } + public static Task AsTask(this IAsyncOperation source, CancellationToken cancellationToken) { if (source == null) { throw new ArgumentNullException(nameof(source)); + } + +#if NET + if (source is ITaskAwareAsyncInfo asyncInfo && asyncInfo.Task is Task task) + { + return cancellationToken.CanBeCanceled ? + task.WaitAsync(cancellationToken) : + task; } - - // TODO: Handle the scenario where the 'IAsyncOperation' is actually a task (i.e. originated from native code - // but projected into an IAsyncOperation) +#endif switch (source.Status) { @@ -78,9 +94,8 @@ public static Task AsTask(this IAsyncOperation source return Task.FromCanceled(cancellationToken.IsCancellationRequested ? cancellationToken : new CancellationToken(true)); } - var bridge = new AsyncInfoToTaskBridge(cancellationToken); + var bridge = new AsyncInfoToTaskBridge(source, cancellationToken); source.Completed = new AsyncOperationCompletedHandler(bridge.CompleteFromAsyncOperation); - bridge.RegisterForCancellation(source); return bridge.Task; } @@ -94,15 +109,32 @@ public static TaskAwaiter GetAwaiter(this IAsyncOperation(this IAsyncOperation source) + { + AsTask(source).Wait(); + } + + public static TResult Get(this IAsyncOperation source) + { + return AsTask(source).Result; + } + public static Task AsTask(this IAsyncActionWithProgress source, CancellationToken cancellationToken, IProgress progress) { if (source == null) { throw new ArgumentNullException(nameof(source)); + } + +#if NET + // fast path is underlying asyncInfo is Task and no IProgress provided + if (source is ITaskAwareAsyncInfo asyncInfo && asyncInfo.Task is Task task && progress == null) + { + return cancellationToken.CanBeCanceled ? + task.WaitAsync(cancellationToken) : + task; } - - // TODO: Handle the scenario where the 'IAsyncActionWithProgress' is actually a task (i.e. originated from native code - // but projected into an IAsyncActionWithProgress) +#endif switch (source.Status) { @@ -121,9 +153,8 @@ public static Task AsTask(this IAsyncActionWithProgress so SetProgress(source, progress); } - var bridge = new AsyncInfoToTaskBridge(cancellationToken); + var bridge = new AsyncInfoToTaskBridge(source, cancellationToken); source.Completed = new AsyncActionWithProgressCompletedHandler(bridge.CompleteFromAsyncActionWithProgress); - bridge.RegisterForCancellation(source); return bridge.Task; } @@ -153,15 +184,27 @@ public static TaskAwaiter GetAwaiter(this IAsyncActionWithProgress(this IAsyncActionWithProgress source) + { + AsTask(source).Wait(); + } + public static Task AsTask(this IAsyncOperationWithProgress source, CancellationToken cancellationToken, IProgress progress) { if (source == null) { throw new ArgumentNullException(nameof(source)); + } + +#if NET + // fast path is underlying asyncInfo is Task and no IProgress provided + if (source is ITaskAwareAsyncInfo asyncInfo && asyncInfo.Task is Task task && progress == null) + { + return cancellationToken.CanBeCanceled ? + task.WaitAsync(cancellationToken) : + task; } - - // TODO: Handle the scenario where the 'IAsyncOperationWithProgress' is actually a task (i.e. originated from native code - // but projected into an IAsyncOperationWithProgress) +#endif switch (source.Status) { @@ -180,9 +223,8 @@ public static Task AsTask(this IAsyncOperationWithP SetProgress(source, progress); } - var bridge = new AsyncInfoToTaskBridge(cancellationToken); + var bridge = new AsyncInfoToTaskBridge(source, cancellationToken); source.Completed = new AsyncOperationWithProgressCompletedHandler(bridge.CompleteFromAsyncOperationWithProgress); - bridge.RegisterForCancellation(source); return bridge.Task; } @@ -212,6 +254,16 @@ public static TaskAwaiter GetAwaiter(this IAsyncOpe return AsTask(source).GetAwaiter(); } + public static void Wait(this IAsyncOperationWithProgress source) + { + AsTask(source).Wait(); + } + + public static TResult Get(this IAsyncOperationWithProgress source) + { + return AsTask(source).Result; + } + public static IAsyncAction AsAsyncAction(this Task source) { if (source == null) @@ -243,53 +295,45 @@ internal class VoidReferenceTypeParameter { } sealed class AsyncInfoToTaskBridge : TaskCompletionSource { private readonly CancellationToken _ct; - private CancellationTokenRegistration _ctr; - private bool _completing; - - internal AsyncInfoToTaskBridge(CancellationToken cancellationToken) - { - // TODO: AsyncCausality? - _ct = cancellationToken; - } - - internal void RegisterForCancellation(IAsyncInfo asyncInfo) - { - Debug.Assert(asyncInfo != null); + private readonly CancellationTokenRegistration _asyncInfoRegistration; + private readonly CancellationTokenRegistration _registration; + + internal AsyncInfoToTaskBridge(IAsyncInfo asyncInfo, CancellationToken cancellationToken) + { + if (asyncInfo == null) + { + throw new ArgumentNullException(nameof(asyncInfo)); + } - try - { - if (_ct.CanBeCanceled && !_completing) + this._ct = cancellationToken; + if (this._ct.CanBeCanceled) + { +#if NET + _registration = this._ct.Register(static (b, ct) => ((TaskCompletionSource)b).TrySetCanceled(ct), this); +#else + _registration = this._ct.Register((b) => ((TaskCompletionSource)b).TrySetCanceled(this._ct), this); +#endif + // Handle Exception from Cancel() if the token is already canceled. + try { - var ctr = _ct.Register(ai => ((IAsyncInfo)ai).Cancel(), asyncInfo); - bool disposeOfCtr = false; - lock (this) - { - if (_completing) - { - disposeOfCtr = true; - } - else - { - _ctr = ctr; - } - } - - if (disposeOfCtr) + _asyncInfoRegistration = this._ct.Register(static ai => ((IAsyncInfo)ai).Cancel(), asyncInfo); + } + catch (Exception ex) + { + if (!base.Task.IsFaulted) { - ctr.Dispose(); + Debug.Fail($"Expected base task to already be faulted but found it in state {base.Task.Status}"); + base.TrySetException(ex); } } } - catch (Exception ex) + + // If we're already completed, unregister everything again. Unregistration is idempotent and thread-safe. + if (Task.IsCompleted) { - if (!base.Task.IsFaulted) - { - Debug.Fail($"Expected base task to already be faulted but found it in state {base.Task.Status}"); - base.TrySetException(ex); - } + this.Cleanup(); } } - internal void CompleteFromAsyncAction(IAsyncAction asyncInfo, AsyncStatus asyncStatus) { Complete(asyncInfo, null, asyncStatus); @@ -317,26 +361,9 @@ private void Complete(IAsyncInfo asyncInfo, Func getResults throw new ArgumentNullException(nameof(asyncInfo)); } - // TODO: AsyncCausality? - try { Debug.Assert(asyncInfo.Status == asyncStatus, "asyncInfo.Status does not match asyncStatus; are we dealing with a faulty IAsyncInfo implementation?"); - if (Task.IsCompleted) - { - Debug.Fail("Expected the task to not yet be completed."); - throw new InvalidOperationException("The asynchronous operation could not be completed."); - } - - // Clean up our registration with the cancellation token, noting that we're now in the process of cleaning up. - CancellationTokenRegistration ctr; - lock (this) - { - _completing = true; - ctr = _ctr; - _ctr = default; - } - ctr.Dispose(); if (asyncStatus != AsyncStatus.Completed && asyncStatus != AsyncStatus.Canceled && asyncStatus != AsyncStatus.Error) { @@ -376,7 +403,6 @@ private void Complete(IAsyncInfo asyncInfo, Func getResults switch (asyncStatus) { case AsyncStatus.Completed: - // TODO: AsyncCausality? success = base.TrySetResult(result); break; @@ -386,24 +412,35 @@ private void Complete(IAsyncInfo asyncInfo, Func getResults break; case AsyncStatus.Canceled: - success = base.TrySetCanceled(_ct.IsCancellationRequested ? _ct : new CancellationToken(true)); + success = base.TrySetCanceled(this._ct.IsCancellationRequested ? this._ct : new CancellationToken(true)); break; } - Debug.Assert(success, "Expected the outcome to be successfully transfered to the task."); + if (success) + { + Cleanup(); + } } catch (Exception exc) { Debug.Fail($"Unexpected exception in Complete: {exc}"); - // TODO: AsyncCausality - if (!base.TrySetException(exc)) { Debug.Fail("The task was already completed and thus the exception couldn't be stored."); throw; } + else + { + Cleanup(); + } } } + + private void Cleanup() + { + _registration.Dispose(); + _asyncInfoRegistration.Dispose(); + } } } diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/IBufferByteAccess.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/IBufferByteAccess.cs index 9cccb23a2..ec81830cc 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/IBufferByteAccess.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/IBufferByteAccess.cs @@ -82,6 +82,7 @@ public IntPtr Buffer { IntPtr __retval = default; Marshal.ThrowExceptionForHR(_obj.Vftbl.get_Buffer_0(ThisPtr, &__retval)); + GC.KeepAlive(_obj); return __retval; } } @@ -101,7 +102,7 @@ public struct Vftbl static unsafe Vftbl() { - AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 1); + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.Interop.IUnknownVftbl) + sizeof(IntPtr)); (*(Vftbl*)AbiToProjectionVftablePtr) = new Vftbl { IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, @@ -124,16 +125,17 @@ private static int Do_Abi_get_Buffer_0(IntPtr thisPtr, IntPtr* buffer) return 0; } } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); + internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr, global::WinRT.Interop.IID.IID_IBufferByteAccess); IntPtr global::Windows.Storage.Streams.IBufferByteAccess.Buffer { get { - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Storage.Streams.IBufferByteAccess).TypeHandle)); + var _obj = ((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Storage.Streams.IBufferByteAccess).TypeHandle); var ThisPtr = _obj.ThisPtr; IntPtr buffer = default; - Marshal.ThrowExceptionForHR(_obj.Vftbl.Get_Buffer_0(ThisPtr, &buffer)); + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)ThisPtr)[3])(ThisPtr, &buffer)); + GC.KeepAlive(_obj); return buffer; } } diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/IMarshal.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/IMarshal.cs index 6af6c0586..75b735d6f 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/IMarshal.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/IMarshal.cs @@ -1,245 +1,296 @@ -namespace Com -{ - using global::System; - - internal enum MSHCTX : int { Local = 0, NoSharedMem = 1, DifferentMachine = 2, InProc = 3, CrossCtx = 4 } - internal enum MSHLFLAGS : int { Normal = 0, TableStrong = 1, TableWeak = 2, NoPing = 4 } - - [global::WinRT.WindowsRuntimeType("Windows.Foundation.UniversalApiContract")] +namespace Com +{ + using global::System; + + internal enum MSHCTX : int { Local = 0, NoSharedMem = 1, DifferentMachine = 2, InProc = 3, CrossCtx = 4 } + internal enum MSHLFLAGS : int { Normal = 0, TableStrong = 1, TableWeak = 2, NoPing = 4 } + + [global::WinRT.WindowsRuntimeType("Windows.Foundation.UniversalApiContract")] + [Guid("00000003-0000-0000-c000-000000000046")] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Com.IMarshal))] + internal interface IMarshal + { + unsafe void GetUnmarshalClass(Guid* riid, IntPtr pv, MSHCTX dwDestContext, IntPtr pvDestContext, MSHLFLAGS mshlFlags, Guid* pCid); + + unsafe void GetMarshalSizeMax(Guid* riid, IntPtr pv, MSHCTX dwDestContext, IntPtr pvDestContext, MSHLFLAGS mshlflags, uint* pSize); + + unsafe void MarshalInterface(IntPtr pStm, Guid* riid, IntPtr pv, MSHCTX dwDestContext, IntPtr pvDestContext, MSHLFLAGS mshlflags); + + unsafe void UnmarshalInterface(IntPtr pStm, Guid* riid, IntPtr* ppv); + + void ReleaseMarshalData(IntPtr pStm); + + void DisconnectObject(uint dwReserved); + } +} + +namespace ABI.Com +{ + using global::System; + using global::System.ComponentModel; + using global::System.Diagnostics.CodeAnalysis; + using global::System.Runtime.CompilerServices; + using global::System.Runtime.InteropServices; + [Guid("00000003-0000-0000-c000-000000000046")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Com.IMarshal))] - internal interface IMarshal - { - unsafe void GetUnmarshalClass(Guid* riid, IntPtr pv, MSHCTX dwDestContext, IntPtr pvDestContext, MSHLFLAGS mshlFlags, Guid* pCid); - - unsafe void GetMarshalSizeMax(Guid* riid, IntPtr pv, MSHCTX dwDestContext, IntPtr pvDestContext, MSHLFLAGS mshlflags, uint* pSize); - - unsafe void MarshalInterface(IntPtr pStm, Guid* riid, IntPtr pv, MSHCTX dwDestContext, IntPtr pvDestContext, MSHLFLAGS mshlflags); - - unsafe void UnmarshalInterface(IntPtr pStm, Guid* riid, IntPtr* ppv); - - void ReleaseMarshalData(IntPtr pStm); - - void DisconnectObject(uint dwReserved); - } -} - -namespace ABI.Com -{ - using global::System; - using global::System.Runtime.CompilerServices; - using global::System.Runtime.InteropServices; - - [Guid("00000003-0000-0000-c000-000000000046")] - internal sealed class IMarshal : global::Com.IMarshal - { - [Guid("00000003-0000-0000-c000-000000000046")] - public unsafe struct Vftbl + internal sealed class IMarshal : global::Com.IMarshal + { + [Guid("00000003-0000-0000-c000-000000000046")] + public unsafe struct Vftbl { - internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; - -#if !NET - private void* _GetUnmarshalClass_0; - public delegate* unmanaged[Stdcall] GetUnmarshalClass_0 { get => (delegate* unmanaged[Stdcall])_GetUnmarshalClass_0; set => _GetUnmarshalClass_0 = value; } - private void* _GetMarshalSizeMax_1; - public delegate* unmanaged[Stdcall] GetMarshalSizeMax_1 { get => (delegate* unmanaged[Stdcall])_GetMarshalSizeMax_1; set => _GetMarshalSizeMax_1 = value; } - private void* _MarshalInterface_2; - public delegate* unmanaged[Stdcall] MarshalInterface_2 { get => (delegate* unmanaged[Stdcall])_MarshalInterface_2; set => _MarshalInterface_2 = value; } - private void* _UnmarshalInterface_3; - public delegate* unmanaged[Stdcall] UnmarshalInterface_3 { get => (delegate* unmanaged[Stdcall])_UnmarshalInterface_3; set => _UnmarshalInterface_3 = value; } - private void* _ReleaseMarshalData_4; - public delegate* unmanaged[Stdcall] ReleaseMarshalData_4 { get => (delegate* unmanaged[Stdcall])_ReleaseMarshalData_4; set => _ReleaseMarshalData_4 = value; } - private void* _DisconnectObject_5; - public delegate* unmanaged[Stdcall] DisconnectObject_5 { get => (delegate* unmanaged[Stdcall])_DisconnectObject_5; set => _DisconnectObject_5 = value; } - - private static readonly Delegate[] DelegateCache = new Delegate[6]; - public static readonly Vftbl AbiToProjectionVftable; -#else - public delegate* unmanaged[Stdcall] GetUnmarshalClass_0; - public delegate* unmanaged[Stdcall] GetMarshalSizeMax_1; - public delegate* unmanaged[Stdcall] MarshalInterface_2; - public delegate* unmanaged[Stdcall] UnmarshalInterface_3; - public delegate* unmanaged[Stdcall] ReleaseMarshalData_4; - public delegate* unmanaged[Stdcall] DisconnectObject_5; -#endif - - public static readonly IntPtr AbiToProjectionVftablePtr; - - static Vftbl() - { -#if !NET - AbiToProjectionVftable = new Vftbl - { - IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, - _GetUnmarshalClass_0 = Marshal.GetFunctionPointerForDelegate(DelegateCache[0] = new IMarshal_Delegates.GetUnmarshalClass_0(Do_Abi_GetUnmarshalClass_0)).ToPointer(), - _GetMarshalSizeMax_1 = Marshal.GetFunctionPointerForDelegate(DelegateCache[1] = new IMarshal_Delegates.GetMarshalSizeMax_1(Do_Abi_GetMarshalSizeMax_1)).ToPointer(), - _MarshalInterface_2 = Marshal.GetFunctionPointerForDelegate(DelegateCache[2] = new IMarshal_Delegates.MarshalInterface_2(Do_Abi_MarshalInterface_2)).ToPointer(), - _UnmarshalInterface_3 = Marshal.GetFunctionPointerForDelegate(DelegateCache[3] = new IMarshal_Delegates.UnmarshalInterface_3(Do_Abi_UnmarshalInterface_3)).ToPointer(), - _ReleaseMarshalData_4 = Marshal.GetFunctionPointerForDelegate(DelegateCache[4] = new IMarshal_Delegates.ReleaseMarshalData_4(Do_Abi_ReleaseMarshalData_4)).ToPointer(), - _DisconnectObject_5 = Marshal.GetFunctionPointerForDelegate(DelegateCache[5] = new IMarshal_Delegates.DisconnectObject_5(Do_Abi_DisconnectObject_5)).ToPointer(), - }; - AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf()); - Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false); -#else - AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), Marshal.SizeOf() + sizeof(IntPtr) * 6); - (*(Vftbl*)AbiToProjectionVftablePtr) = new Vftbl - { - IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, - GetUnmarshalClass_0 = &Do_Abi_GetUnmarshalClass_0, - GetMarshalSizeMax_1 = &Do_Abi_GetMarshalSizeMax_1, - MarshalInterface_2 = &Do_Abi_MarshalInterface_2, - UnmarshalInterface_3 = &Do_Abi_UnmarshalInterface_3, - ReleaseMarshalData_4 = &Do_Abi_ReleaseMarshalData_4, - DisconnectObject_5 = &Do_Abi_DisconnectObject_5 - }; -#endif - } - -#if NET + internal global::WinRT.Interop.IUnknownVftbl IUnknownVftbl; + +#if !NET + private void* _GetUnmarshalClass_0; + public delegate* unmanaged[Stdcall] GetUnmarshalClass_0 { get => (delegate* unmanaged[Stdcall])_GetUnmarshalClass_0; set => _GetUnmarshalClass_0 = value; } + private void* _GetMarshalSizeMax_1; + public delegate* unmanaged[Stdcall] GetMarshalSizeMax_1 { get => (delegate* unmanaged[Stdcall])_GetMarshalSizeMax_1; set => _GetMarshalSizeMax_1 = value; } + private void* _MarshalInterface_2; + public delegate* unmanaged[Stdcall] MarshalInterface_2 { get => (delegate* unmanaged[Stdcall])_MarshalInterface_2; set => _MarshalInterface_2 = value; } + private void* _UnmarshalInterface_3; + public delegate* unmanaged[Stdcall] UnmarshalInterface_3 { get => (delegate* unmanaged[Stdcall])_UnmarshalInterface_3; set => _UnmarshalInterface_3 = value; } + private void* _ReleaseMarshalData_4; + public delegate* unmanaged[Stdcall] ReleaseMarshalData_4 { get => (delegate* unmanaged[Stdcall])_ReleaseMarshalData_4; set => _ReleaseMarshalData_4 = value; } + private void* _DisconnectObject_5; + public delegate* unmanaged[Stdcall] DisconnectObject_5 { get => (delegate* unmanaged[Stdcall])_DisconnectObject_5; set => _DisconnectObject_5 = value; } + + private static readonly Delegate[] DelegateCache = new Delegate[6]; + public static readonly Vftbl AbiToProjectionVftable; +#else + public delegate* unmanaged[Stdcall] GetUnmarshalClass_0; + public delegate* unmanaged[Stdcall] GetMarshalSizeMax_1; + public delegate* unmanaged[Stdcall] MarshalInterface_2; + public delegate* unmanaged[Stdcall] UnmarshalInterface_3; + public delegate* unmanaged[Stdcall] ReleaseMarshalData_4; + public delegate* unmanaged[Stdcall] DisconnectObject_5; +#endif + + public static readonly IntPtr AbiToProjectionVftablePtr; + + static Vftbl() + { +#if !NET + AbiToProjectionVftable = new Vftbl + { + IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, + _GetUnmarshalClass_0 = Marshal.GetFunctionPointerForDelegate(DelegateCache[0] = new IMarshal_Delegates.GetUnmarshalClass_0(Do_Abi_GetUnmarshalClass_0)).ToPointer(), + _GetMarshalSizeMax_1 = Marshal.GetFunctionPointerForDelegate(DelegateCache[1] = new IMarshal_Delegates.GetMarshalSizeMax_1(Do_Abi_GetMarshalSizeMax_1)).ToPointer(), + _MarshalInterface_2 = Marshal.GetFunctionPointerForDelegate(DelegateCache[2] = new IMarshal_Delegates.MarshalInterface_2(Do_Abi_MarshalInterface_2)).ToPointer(), + _UnmarshalInterface_3 = Marshal.GetFunctionPointerForDelegate(DelegateCache[3] = new IMarshal_Delegates.UnmarshalInterface_3(Do_Abi_UnmarshalInterface_3)).ToPointer(), + _ReleaseMarshalData_4 = Marshal.GetFunctionPointerForDelegate(DelegateCache[4] = new IMarshal_Delegates.ReleaseMarshalData_4(Do_Abi_ReleaseMarshalData_4)).ToPointer(), + _DisconnectObject_5 = Marshal.GetFunctionPointerForDelegate(DelegateCache[5] = new IMarshal_Delegates.DisconnectObject_5(Do_Abi_DisconnectObject_5)).ToPointer(), + }; + AbiToProjectionVftablePtr = Marshal.AllocHGlobal(Marshal.SizeOf()); + Marshal.StructureToPtr(AbiToProjectionVftable, AbiToProjectionVftablePtr, false); +#else + AbiToProjectionVftablePtr = ComWrappersSupport.AllocateVtableMemory(typeof(Vftbl), sizeof(global::WinRT.Interop.IUnknownVftbl) + sizeof(IntPtr) * 6); + (*(Vftbl*)AbiToProjectionVftablePtr) = new Vftbl + { + IUnknownVftbl = global::WinRT.Interop.IUnknownVftbl.AbiToProjectionVftbl, + GetUnmarshalClass_0 = &Do_Abi_GetUnmarshalClass_0, + GetMarshalSizeMax_1 = &Do_Abi_GetMarshalSizeMax_1, + MarshalInterface_2 = &Do_Abi_MarshalInterface_2, + UnmarshalInterface_3 = &Do_Abi_UnmarshalInterface_3, + ReleaseMarshalData_4 = &Do_Abi_ReleaseMarshalData_4, + DisconnectObject_5 = &Do_Abi_DisconnectObject_5 + }; +#endif + } + +#if NET [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] -#endif - private static int Do_Abi_GetUnmarshalClass_0(IntPtr thisPtr, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlFlags, Guid* pCid) - { - *pCid = default; - try - { - ComWrappersSupport.FindObject(thisPtr).GetUnmarshalClass(riid, pv, dwDestContext, pvDestContext, mshlFlags, pCid); - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - return 0; +#endif + private static int Do_Abi_GetUnmarshalClass_0(IntPtr thisPtr, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlFlags, Guid* pCid) + { + *pCid = default; + try + { + ComWrappersSupport.FindObject(thisPtr).GetUnmarshalClass(riid, pv, dwDestContext, pvDestContext, mshlFlags, pCid); + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + return 0; } -#if NET +#if NET [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] -#endif - private static int Do_Abi_GetMarshalSizeMax_1(IntPtr thisPtr, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlflags, uint* pSize) - { - *pSize = default; - try - { - ComWrappersSupport.FindObject(thisPtr).GetMarshalSizeMax(riid, pv, dwDestContext, pvDestContext, mshlflags, pSize); - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - return 0; +#endif + private static int Do_Abi_GetMarshalSizeMax_1(IntPtr thisPtr, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlflags, uint* pSize) + { + *pSize = default; + try + { + ComWrappersSupport.FindObject(thisPtr).GetMarshalSizeMax(riid, pv, dwDestContext, pvDestContext, mshlflags, pSize); + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + return 0; } -#if NET +#if NET [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] -#endif - private static int Do_Abi_MarshalInterface_2(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlflags) - { - try - { - ComWrappersSupport.FindObject(thisPtr).MarshalInterface(pStm, riid, pv, dwDestContext, pvDestContext, mshlflags); - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - return 0; +#endif + private static int Do_Abi_MarshalInterface_2(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlflags) + { + try + { + ComWrappersSupport.FindObject(thisPtr).MarshalInterface(pStm, riid, pv, dwDestContext, pvDestContext, mshlflags); + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + return 0; } -#if NET +#if NET [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] -#endif - private static int Do_Abi_UnmarshalInterface_3(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr* ppv) - { - *ppv = default; - try - { - ComWrappersSupport.FindObject(thisPtr).UnmarshalInterface(pStm, riid, ppv); - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - return 0; +#endif + private static int Do_Abi_UnmarshalInterface_3(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr* ppv) + { + *ppv = default; + try + { + ComWrappersSupport.FindObject(thisPtr).UnmarshalInterface(pStm, riid, ppv); + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + return 0; } -#if NET +#if NET [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] -#endif - private static int Do_Abi_ReleaseMarshalData_4(IntPtr thisPtr, IntPtr pStm) - { - try - { - ComWrappersSupport.FindObject(thisPtr).ReleaseMarshalData(pStm); - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - return 0; +#endif + private static int Do_Abi_ReleaseMarshalData_4(IntPtr thisPtr, IntPtr pStm) + { + try + { + ComWrappersSupport.FindObject(thisPtr).ReleaseMarshalData(pStm); + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + return 0; } -#if NET +#if NET [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] -#endif - private static int Do_Abi_DisconnectObject_5(IntPtr thisPtr, uint dwReserved) - { - try - { - ComWrappersSupport.FindObject(thisPtr).DisconnectObject(dwReserved); - } - catch (Exception ex) - { - return Marshal.GetHRForException(ex); - } - return 0; - } - } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator IMarshal(IObjectReference obj) => (obj != null) ? new IMarshal(obj) : null; - private readonly ObjectReference _obj; - public IObjectReference ObjRef { get => _obj; } - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public IMarshal(IObjectReference obj) : this(obj.As()) { } - internal IMarshal(ObjectReference obj) - { - _obj = obj; +#endif + private static int Do_Abi_DisconnectObject_5(IntPtr thisPtr, uint dwReserved) + { + try + { + ComWrappersSupport.FindObject(thisPtr).DisconnectObject(dwReserved); + } + catch (Exception ex) + { + return Marshal.GetHRForException(ex); + } + return 0; + } } +#if NET + internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr, global::WinRT.Interop.IID.IID_IMarshal); +#else + internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr, global::WinRT.Interop.IID.IID_IMarshal); +#endif +#if NET + private readonly ObjectReference _obj; +#else + private readonly ObjectReference _obj; +#endif + public IObjectReference ObjRef { get => _obj; } + public IntPtr ThisPtr => _obj.ThisPtr; - public unsafe void GetUnmarshalClass(Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlFlags, Guid* pCid) - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetUnmarshalClass_0(ThisPtr, riid, pv, dwDestContext, pvDestContext, mshlFlags, pCid)); - } - - public unsafe void GetMarshalSizeMax(Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlflags, uint* pSize) - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.GetMarshalSizeMax_1(ThisPtr, riid, pv, dwDestContext, pvDestContext, mshlflags, pSize)); - } - - public unsafe void MarshalInterface(IntPtr pStm, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlflags) - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.MarshalInterface_2(ThisPtr, pStm, riid, pv, dwDestContext, pvDestContext, mshlflags)); - } - - public unsafe void UnmarshalInterface(IntPtr pStm, Guid* riid, IntPtr* ppv) - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.UnmarshalInterface_3(ThisPtr, pStm, riid, ppv)); - } - - public unsafe void ReleaseMarshalData(IntPtr pStm) - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.ReleaseMarshalData_4(ThisPtr, pStm)); - } - - public unsafe void DisconnectObject(uint dwReserved) - { - Marshal.ThrowExceptionForHR(_obj.Vftbl.DisconnectObject_5(ThisPtr, dwReserved)); - } +#if NET + public IMarshal(IObjectReference obj) + { + _obj = obj.As(global::WinRT.Interop.IID.IID_IMarshal); + } +#else + public IMarshal(IObjectReference obj) : this(obj.As()) { } + internal IMarshal(ObjectReference obj) + { + _obj = obj; + } +#endif + + public unsafe void GetUnmarshalClass(Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlFlags, Guid* pCid) + { +#if NET + IntPtr thisPtr = ThisPtr; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[3])(thisPtr, riid, pv, dwDestContext, pvDestContext, mshlFlags, pCid)); +#else + Marshal.ThrowExceptionForHR(_obj.Vftbl.GetUnmarshalClass_0(ThisPtr, riid, pv, dwDestContext, pvDestContext, mshlFlags, pCid)); +#endif + GC.KeepAlive(_obj); + } + + public unsafe void GetMarshalSizeMax(Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlflags, uint* pSize) + { +#if NET + IntPtr thisPtr = ThisPtr; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[4])(thisPtr, riid, pv, dwDestContext, pvDestContext, mshlflags, pSize)); +#else + Marshal.ThrowExceptionForHR(_obj.Vftbl.GetMarshalSizeMax_1(ThisPtr, riid, pv, dwDestContext, pvDestContext, mshlflags, pSize)); +#endif + GC.KeepAlive(_obj); + } + + public unsafe void MarshalInterface(IntPtr pStm, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlflags) + { +#if NET + IntPtr thisPtr = ThisPtr; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[5])(thisPtr, pStm, riid, pv, dwDestContext, pvDestContext, mshlflags)); +#else + Marshal.ThrowExceptionForHR(_obj.Vftbl.MarshalInterface_2(ThisPtr, pStm, riid, pv, dwDestContext, pvDestContext, mshlflags)); +#endif + GC.KeepAlive(_obj); + } + + public unsafe void UnmarshalInterface(IntPtr pStm, Guid* riid, IntPtr* ppv) + { +#if NET + IntPtr thisPtr = ThisPtr; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[6])(thisPtr, pStm, riid, ppv)); +#else + Marshal.ThrowExceptionForHR(_obj.Vftbl.UnmarshalInterface_3(ThisPtr, pStm, riid, ppv)); +#endif + GC.KeepAlive(_obj); + } + + public unsafe void ReleaseMarshalData(IntPtr pStm) + { +#if NET + IntPtr thisPtr = ThisPtr; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[7])(thisPtr, pStm)); +#else + Marshal.ThrowExceptionForHR(_obj.Vftbl.ReleaseMarshalData_4(ThisPtr, pStm)); +#endif + GC.KeepAlive(_obj); + } + + public unsafe void DisconnectObject(uint dwReserved) + { +#if NET + IntPtr thisPtr = ThisPtr; + Marshal.ThrowExceptionForHR(((delegate* unmanaged[Stdcall])(*(void***)thisPtr)[8])(thisPtr, dwReserved)); +#else + Marshal.ThrowExceptionForHR(_obj.Vftbl.DisconnectObject_5(ThisPtr, dwReserved)); +#endif + GC.KeepAlive(_obj); + } } - internal static unsafe class IMarshal_Delegates +#if !NET + internal static unsafe class IMarshal_Delegates { public delegate int GetUnmarshalClass_0(IntPtr thisPtr, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlFlags, Guid* pCid); public delegate int GetMarshalSizeMax_1(IntPtr thisPtr, Guid* riid, IntPtr pv, global::Com.MSHCTX dwDestContext, IntPtr pvDestContext, global::Com.MSHLFLAGS mshlflags, uint* pSize); @@ -247,5 +298,6 @@ internal static unsafe class IMarshal_Delegates public delegate int UnmarshalInterface_3(IntPtr thisPtr, IntPtr pStm, Guid* riid, IntPtr* ppv); public delegate int ReleaseMarshalData_4(IntPtr thisPtr, IntPtr pStm); public delegate int DisconnectObject_5(IntPtr thisPtr, uint dwReserved); - } + } +#endif } \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/NetFxToWinRtStreamAdapter.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/NetFxToWinRtStreamAdapter.cs index ab78916a3..735682be0 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/NetFxToWinRtStreamAdapter.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/NetFxToWinRtStreamAdapter.cs @@ -21,7 +21,7 @@ namespace System.IO /// the region Interface adapters to implement WinRT ifaces and create instances of those types. /// See comment in that region for technical details. /// - internal abstract class NetFxToWinRtStreamAdapter : IDisposable + internal abstract partial class NetFxToWinRtStreamAdapter : IDisposable { private const int E_ILLEGAL_METHOD_CALL = unchecked((int)0x8000000E); private const int RO_E_CLOSED = unchecked((int)0x80000013); @@ -42,7 +42,7 @@ internal abstract class NetFxToWinRtStreamAdapter : IDisposable // The latter is much more elegant, and likely also faster. - private sealed class InputStream : NetFxToWinRtStreamAdapter, IInputStream, IDisposable + private sealed partial class InputStream : NetFxToWinRtStreamAdapter, IInputStream, IDisposable { internal InputStream(Stream stream, StreamReadOperationOptimization readOptimization) : base(stream, readOptimization) @@ -51,7 +51,7 @@ internal InputStream(Stream stream, StreamReadOperationOptimization readOptimiza } - private sealed class OutputStream : NetFxToWinRtStreamAdapter, IOutputStream, IDisposable + private sealed partial class OutputStream : NetFxToWinRtStreamAdapter, IOutputStream, IDisposable { internal OutputStream(Stream stream, StreamReadOperationOptimization readOptimization) : base(stream, readOptimization) @@ -60,7 +60,7 @@ internal OutputStream(Stream stream, StreamReadOperationOptimization readOptimiz } - private sealed class RandomAccessStream : NetFxToWinRtStreamAdapter, IRandomAccessStream, IInputStream, IOutputStream, IDisposable + private sealed partial class RandomAccessStream : NetFxToWinRtStreamAdapter, IRandomAccessStream, IInputStream, IOutputStream, IDisposable { internal RandomAccessStream(Stream stream, StreamReadOperationOptimization readOptimization) : base(stream, readOptimization) @@ -69,7 +69,7 @@ internal RandomAccessStream(Stream stream, StreamReadOperationOptimization readO } - private sealed class InputOutputStream : NetFxToWinRtStreamAdapter, IInputStream, IOutputStream, IDisposable + private sealed partial class InputOutputStream : NetFxToWinRtStreamAdapter, IInputStream, IOutputStream, IDisposable { internal InputOutputStream(Stream stream, StreamReadOperationOptimization readOptimization) : base(stream, readOptimization) diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamOperationsImplementation.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamOperationsImplementation.cs index b14a652a5..018ec23e8 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamOperationsImplementation.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamOperationsImplementation.cs @@ -25,7 +25,14 @@ namespace Windows.Storage.Streams [global::System.Runtime.Versioning.SupportedOSPlatform("windows10.0.10240.0")] #endif internal static class StreamOperationsImplementation - { + { +#if NET + static StreamOperationsImplementation() + { + _ = StreamTaskAdaptersImplementation.Initialized; + } +#endif + #region ReadAsync implementations internal static IAsyncOperationWithProgress ReadAsync_MemoryStream(Stream stream, IBuffer buffer, uint count) @@ -53,11 +60,11 @@ internal static IAsyncOperationWithProgress ReadAsync_MemoryStrea if (dataBuffer.Length > 0) memStream.Seek(dataBuffer.Length, SeekOrigin.Current); - return AsyncInfo.CreateCompletedOperation(dataBuffer); + return AsyncInfo.FromResultWithProgress(dataBuffer); } catch (Exception ex) { - return AsyncInfo.CreateFaultedOperation(ex); + return AsyncInfo.FromExceptionWithProgress(ex); } } // ReadAsync_MemoryStream diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamTaskAdaptersImplementation.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamTaskAdaptersImplementation.cs new file mode 100644 index 000000000..77ea52140 --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/StreamTaskAdaptersImplementation.cs @@ -0,0 +1,610 @@ +#if NET +namespace Windows.Storage.Streams +{ + // Given we do not do automatic lookup table generation for projections, this class defines + // one for the scenarios which are used by StreamOperationsImplementations for its task adapters. + internal static class StreamTaskAdaptersImplementation + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + global::WinRT.ComWrappersSupport.RegisterTypeComInterfaceEntriesLookup(LookupVtableEntries); + global::WinRT.ComWrappersSupport.RegisterTypeRuntimeClassNameLookup(new Func(LookupRuntimeClassName)); + return true; + } + + private static ComWrappers.ComInterfaceEntry[] LookupVtableEntries(Type type) + { + var typeName = type.ToString(); + if (typeName == "System.Threading.Tasks.TaskToAsyncOperationWithProgressAdapter`2[Windows.Storage.Streams.IBuffer,System.UInt32]") + { + _ = IAsyncOperationWithProgress_IBuffer_uint.Initialized; + + return new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry[] + { + new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry + { + IID = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.IID, + Vtable = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.AbiToProjectionVftablePtr + }, + new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry + { + IID = global::ABI.Windows.Foundation.IAsyncInfoMethods.IID, + Vtable = global::ABI.Windows.Foundation.IAsyncInfoMethods.AbiToProjectionVftablePtr + }, + }; + } + else if (typeName == "System.Threading.Tasks.TaskToAsyncOperationWithProgressAdapter`2[System.UInt32,System.UInt32]") + { + _ = IAsyncOperationWithProgress_uint_uint.Initialized; + + return new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry[] + { + new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry + { + IID = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.IID, + Vtable = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.AbiToProjectionVftablePtr + }, + new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry + { + IID = global::ABI.Windows.Foundation.IAsyncInfoMethods.IID, + Vtable = global::ABI.Windows.Foundation.IAsyncInfoMethods.AbiToProjectionVftablePtr + }, + }; + } + else if (typeName == "System.Threading.Tasks.TaskToAsyncOperationAdapter`1[System.Boolean]") + { + _ = IAsyncOperation_bool.Initialized; + + return new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry[] + { + new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry + { + IID = global::ABI.Windows.Foundation.IAsyncOperationMethods.IID, + Vtable = global::ABI.Windows.Foundation.IAsyncOperationMethods.AbiToProjectionVftablePtr + }, + new global::System.Runtime.InteropServices.ComWrappers.ComInterfaceEntry + { + IID = global::ABI.Windows.Foundation.IAsyncInfoMethods.IID, + Vtable = global::ABI.Windows.Foundation.IAsyncInfoMethods.AbiToProjectionVftablePtr + }, + }; + } + + return default; + } + + private static string LookupRuntimeClassName(Type type) + { + var typeName = type.ToString(); + if (typeName == "System.Threading.Tasks.TaskToAsyncOperationWithProgressAdapter`2[Windows.Storage.Streams.IBuffer,System.UInt32]") + { + return "Windows.Foundation.IAsyncOperationWithProgress`2"; + } + else if (typeName == "System.Threading.Tasks.TaskToAsyncOperationWithProgressAdapter`2[System.UInt32,System.UInt32]") + { + return "Windows.Foundation.IAsyncOperationWithProgress`2"; + } + else if (typeName == "System.Threading.Tasks.TaskToAsyncOperationAdapter`1[System.Boolean]") + { + return "Windows.Foundation.IAsyncOperation`1"; + } + + return default; + } + + private static class IAsyncOperationWithProgress_uint_uint + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + return global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.InitCcw( + &Do_Abi_put_Progress_0, + &Do_Abi_get_Progress_1, + &Do_Abi_put_Completed_2, + &Do_Abi_get_Completed_3, + &Do_Abi_GetResults_4 + ); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_GetResults_4(IntPtr thisPtr, uint* __return_value__) + { + uint ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_GetResults_4(thisPtr); + *__return_value__ = ____return_value__; + } + catch (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_put_Progress_0(IntPtr thisPtr, IntPtr handler) + { + _ = AsyncOperationProgressHandler_uint_uint.Initialized; + try + { + global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_put_Progress_0( + thisPtr, + global::ABI.Windows.Foundation.AsyncOperationProgressHandler.FromAbi(handler)); + } + catch (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_Progress_1(IntPtr thisPtr, IntPtr* __return_value__) + { + _ = AsyncOperationProgressHandler_uint_uint.Initialized; + global::Windows.Foundation.AsyncOperationProgressHandler ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_get_Progress_1(thisPtr); + *__return_value__ = global::ABI.Windows.Foundation.AsyncOperationProgressHandler.FromManaged(____return_value__); + + } + catch (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_put_Completed_2(IntPtr thisPtr, IntPtr handler) + { + _ = AsyncOperationWithProgressCompletedHandler_uint_uint.Initialized; + try + { + global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_put_Completed_2( + thisPtr, + global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandler.FromAbi(handler)); + } + catch (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_Completed_3(IntPtr thisPtr, IntPtr* __return_value__) + { + _ = AsyncOperationWithProgressCompletedHandler_uint_uint.Initialized; + global::Windows.Foundation.AsyncOperationWithProgressCompletedHandler ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_get_Completed_3(thisPtr); + *__return_value__ = global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandler.FromManaged(____return_value__); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + + private static class AsyncOperationProgressHandler_uint_uint + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + _ = global::ABI.Windows.Foundation.AsyncOperationProgressHandlerMethods.InitCcw(&Do_Abi_Invoke); + _ = global::ABI.Windows.Foundation.AsyncOperationProgressHandlerMethods.InitRcwHelper(&Invoke); + return true; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr asyncInfo, uint progressInfo) + { + try + { + global::ABI.Windows.Foundation.AsyncOperationProgressHandlerMethods.Abi_Invoke( + thisPtr, + MarshalInterface>.FromAbi(asyncInfo), + progressInfo); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe void Invoke(IObjectReference objRef, global::Windows.Foundation.IAsyncOperationWithProgress asyncInfo, uint progressInfo) + { + IntPtr ThisPtr = objRef.ThisPtr; + ObjectReferenceValue __asyncInfo = default; + + try + { + __asyncInfo = MarshalInterface>.CreateMarshaler2( + asyncInfo, + global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.IID); + IntPtr abiAsyncInfo = MarshalInspectable.GetAbi(__asyncInfo); + + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3]( + ThisPtr, + abiAsyncInfo, + progressInfo)); + GC.KeepAlive(objRef); + } + finally + { + MarshalInterface>.DisposeMarshaler(__asyncInfo); + + } + } + } + + private static class AsyncOperationWithProgressCompletedHandler_uint_uint + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + return global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandlerMethods. + InitCcw(&Do_Abi_Invoke); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr asyncInfo, global::Windows.Foundation.AsyncStatus asyncStatus) + { + try + { + global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandlerMethods.Abi_Invoke( + thisPtr, + MarshalInterface>.FromAbi(asyncInfo), + asyncStatus); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + + private static class IAsyncOperationWithProgress_IBuffer_uint + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + return global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.InitCcw( + &Do_Abi_put_Progress_0, + &Do_Abi_get_Progress_1, + &Do_Abi_put_Completed_2, + &Do_Abi_get_Completed_3, + &Do_Abi_GetResults_4 + ); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_GetResults_4(IntPtr thisPtr, IntPtr* __return_value__) + { + Windows.Storage.Streams.IBuffer ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods. + Do_Abi_GetResults_4(thisPtr); + *__return_value__ = MarshalInterface.FromManaged(____return_value__); + } + catch (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_put_Progress_0(IntPtr thisPtr, IntPtr handler) + { + _ = AsyncOperationProgressHandler_IBuffer_uint.Initialized; + try + { + global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_put_Progress_0( + thisPtr, + global::ABI.Windows.Foundation.AsyncOperationProgressHandler.FromAbi(handler)); + } + catch (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_Progress_1(IntPtr thisPtr, IntPtr* __return_value__) + { + _ = AsyncOperationProgressHandler_IBuffer_uint.Initialized; + global::Windows.Foundation.AsyncOperationProgressHandler ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_get_Progress_1(thisPtr); + *__return_value__ = global::ABI.Windows.Foundation.AsyncOperationProgressHandler.FromManaged(____return_value__); + + } + catch (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_put_Completed_2(IntPtr thisPtr, IntPtr handler) + { + _ = AsyncOperationWithProgressCompletedHandler_IBuffer_uint.Initialized; + try + { + global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_put_Completed_2( + thisPtr, + global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandler.FromAbi(handler)); + } + catch (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_Completed_3(IntPtr thisPtr, IntPtr* __return_value__) + { + _ = AsyncOperationWithProgressCompletedHandler_IBuffer_uint.Initialized; + global::Windows.Foundation.AsyncOperationWithProgressCompletedHandler ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.Do_Abi_get_Completed_3(thisPtr); + *__return_value__ = global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandler.FromManaged(____return_value__); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + + private static class AsyncOperationProgressHandler_IBuffer_uint + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + _ = global::ABI.Windows.Foundation.AsyncOperationProgressHandlerMethods.InitCcw(&Do_Abi_Invoke); + _ = global::ABI.Windows.Foundation.AsyncOperationProgressHandlerMethods.InitRcwHelper(&Invoke); + return true; + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr asyncInfo, uint progressInfo) + { + try + { + global::ABI.Windows.Foundation.AsyncOperationProgressHandlerMethods.Abi_Invoke( + thisPtr, + MarshalInterface>.FromAbi(asyncInfo), + progressInfo); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + + private static unsafe void Invoke( + IObjectReference objRef, + global::Windows.Foundation.IAsyncOperationWithProgress asyncInfo, + uint progressInfo) + { + IntPtr ThisPtr = objRef.ThisPtr; + ObjectReferenceValue __asyncInfo = default; + + try + { + __asyncInfo = MarshalInterface>.CreateMarshaler2( + asyncInfo, + global::ABI.Windows.Foundation.IAsyncOperationWithProgressMethods.IID); + IntPtr abiAsyncInfo = MarshalInspectable.GetAbi(__asyncInfo); + + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3]( + ThisPtr, + abiAsyncInfo, + progressInfo)); + GC.KeepAlive(objRef); + } + finally + { + MarshalInterface>.DisposeMarshaler(__asyncInfo); + + } + } + } + + private static class AsyncOperationWithProgressCompletedHandler_IBuffer_uint + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + return global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandlerMethods. + InitCcw(&Do_Abi_Invoke); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr asyncInfo, global::Windows.Foundation.AsyncStatus asyncStatus) + { + try + { + global::ABI.Windows.Foundation.AsyncOperationWithProgressCompletedHandlerMethods.Abi_Invoke( + thisPtr, + MarshalInterface>.FromAbi(asyncInfo), + asyncStatus); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + + private static class IAsyncOperation_bool + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + return global::ABI.Windows.Foundation.IAsyncOperationMethods.InitCcw( + &Do_Abi_put_Completed_0, + &Do_Abi_get_Completed_1, + &Do_Abi_GetResults_2 + ); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_GetResults_2(IntPtr thisPtr, byte* __return_value__) + { + bool ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = global::ABI.Windows.Foundation.IAsyncOperationMethods.Do_Abi_GetResults_2(thisPtr); + *__return_value__ = (byte)(____return_value__ ? 1 : 0); + } + catch (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_put_Completed_0(IntPtr thisPtr, IntPtr handler) + { + _ = AsyncOperationCompletedHandler_bool.Initialized; + try + { + global::ABI.Windows.Foundation.IAsyncOperationMethods.Do_Abi_put_Completed_0(thisPtr, global::ABI.Windows.Foundation.AsyncOperationCompletedHandler.FromAbi(handler)); + } + catch (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_Completed_1(IntPtr thisPtr, IntPtr* __return_value__) + { + _ = AsyncOperationCompletedHandler_bool.Initialized; + global::Windows.Foundation.AsyncOperationCompletedHandler ____return_value__ = default; + + *__return_value__ = default; + + try + { + ____return_value__ = global::ABI.Windows.Foundation.IAsyncOperationMethods.Do_Abi_get_Completed_1(thisPtr); + *__return_value__ = global::ABI.Windows.Foundation.AsyncOperationCompletedHandler.FromManaged(____return_value__); + } + catch (Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + + private static class AsyncOperationCompletedHandler_bool + { + private static readonly bool _initialized = Init(); + internal static bool Initialized => _initialized; + + private static unsafe bool Init() + { + return global::ABI.Windows.Foundation.AsyncOperationCompletedHandlerMethods.InitCcw( + &Do_Abi_Invoke + ); + } + + [UnmanagedCallersOnly(CallConvs = new[] { typeof(CallConvStdcall) })] + private static unsafe int Do_Abi_Invoke(IntPtr thisPtr, IntPtr asyncInfo, global::Windows.Foundation.AsyncStatus asyncStatus) + { + try + { + global::ABI.Windows.Foundation.AsyncOperationCompletedHandlerMethods.Abi_Invoke( + thisPtr, + MarshalInterface>.FromAbi(asyncInfo), + asyncStatus); + } + catch (global::System.Exception __exception__) + { + global::WinRT.ExceptionHelpers.SetErrorInfo(__exception__); + return global::WinRT.ExceptionHelpers.GetHRForException(__exception__); + } + return 0; + } + } + } +} +#endif \ No newline at end of file diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs index fb448ca70..db96cf969 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBuffer.cs @@ -13,11 +13,14 @@ namespace System.Runtime.InteropServices.WindowsRuntime using System.Threading; using global::Windows.Foundation; using global::Windows.Storage.Streams; - using Com; + using Com; /// /// Contains an implementation of the WinRT IBuffer interface that conforms to all requirements on classes that implement that interface, /// such as implementing additional interfaces. /// +#if NET + [global::WinRT.WinRTExposedType(typeof(global::ABI.System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferWinRTTypeDetails))] +#endif #if EMBED internal #else @@ -26,7 +29,7 @@ namespace System.Runtime.InteropServices.WindowsRuntime sealed class WindowsRuntimeBuffer : IBuffer, IBufferByteAccess, IMarshal { [DllImport("api-ms-win-core-winrt-robuffer-l1-1-0.dll")] - private static extern int RoGetBufferMarshaler(out IntPtr bufferMarshalerPtr); + private static extern unsafe int RoGetBufferMarshaler(IntPtr* bufferMarshalerPtr); #region Constants private const string WinTypesDLL = "WinTypes.dll"; @@ -69,15 +72,16 @@ public static IBuffer Create(byte[] data, int offset, int length, int capacity) [ThreadStatic] private static IMarshal t_winRtMarshalProxy = null; - private static void EnsureHasMarshalProxy() + private static unsafe void EnsureHasMarshalProxy() { if (t_winRtMarshalProxy != null) return; try { - int hr = RoGetBufferMarshaler(out IntPtr proxyPtr); - IMarshal proxy = new ABI.Com.IMarshal(ObjectReference.Attach(ref proxyPtr)); + IntPtr proxyPtr = default; + int hr = RoGetBufferMarshaler(&proxyPtr); + IMarshal proxy = new ABI.Com.IMarshal(ObjectReference.Attach(ref proxyPtr, global::WinRT.Interop.IID.IID_IMarshal)); t_winRtMarshalProxy = proxy; if (hr != 0) @@ -309,4 +313,34 @@ unsafe void IMarshal.UnmarshalInterface(IntPtr pStm, Guid* riid, IntPtr* ppv) } // class WindowsRuntimeBuffer } // namespace +#if NET +namespace ABI.System.Runtime.InteropServices.WindowsRuntime +{ + internal sealed class WindowsRuntimeBufferWinRTTypeDetails : global::WinRT.IWinRTExposedTypeDetails + { + public ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + { + return new ComWrappers.ComInterfaceEntry[] + { + new ComWrappers.ComInterfaceEntry + { + IID = global::WinRT.Interop.IID.IID_IBuffer, + Vtable = global::ABI.Windows.Storage.Streams.IBuffer.AbiToProjectionVftablePtr + }, + new ComWrappers.ComInterfaceEntry + { + IID = global::WinRT.Interop.IID.IID_IBufferByteAccess, + Vtable = global::ABI.Windows.Storage.Streams.IBufferByteAccess.Vftbl.AbiToProjectionVftablePtr + }, + new ComWrappers.ComInterfaceEntry + { + IID = global::WinRT.Interop.IID.IID_IMarshal, + Vtable = global::ABI.Com.IMarshal.Vftbl.AbiToProjectionVftablePtr + } + }; + } + } +} +#endif + // WindowsRuntimeBuffer.cs diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs index e38804e07..defb5ff75 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeBufferExtensions.cs @@ -8,8 +8,11 @@ namespace System.Runtime.InteropServices.WindowsRuntime using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.IO; + using System.Threading; + using System.Threading.Tasks; using global::Windows.Foundation; - using global::Windows.Storage.Streams; + using global::Windows.Storage.Streams; + using WinRT; /// /// Contains extension methods that expose operations on WinRT Windows.Foundation.IBuffer. /// @@ -57,6 +60,58 @@ public static IBuffer AsBuffer(this byte[] source, int offset, int length, int c #endregion (Byte []).AsBuffer extensions +#region (Span).CopyTo extensions for copying to an (IBuffer) + + /// + /// Copies the contents of source to destination starting at offset 0. + /// This method does NOT update destination.Length. + /// + /// Span to copy data from. + /// The buffer to copy to. + public static void CopyTo(this Span source, IBuffer destination) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (destination == null) throw new ArgumentNullException(nameof(destination)); + + CopyTo(source, destination, 0); + } + + + /// + /// Copies count bytes from source starting at offset sourceIndex + /// to destination starting at destinationIndex. + /// This method does NOT update destination.Length. + /// + /// Span to copy data from. + /// Position in the span from where to start copying. + /// The buffer to copy to. + /// Position in the buffer to where to start copying. + /// The number of bytes to copy. + public static void CopyTo(this Span source, IBuffer destination, uint destinationIndex) + { + if (destination == null) throw new ArgumentNullException(nameof(destination)); + if (destination.Capacity < destinationIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferIndexExceedsCapacity); + if (destination.Capacity - destinationIndex < source.Length) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInTargetBuffer); + if (source.Length == 0) return; + + Debug.Assert(destinationIndex <= int.MaxValue); + + // If destination is backed by a managed memory, use the memory instead of the pointer as it does not require pinning: + Span destSpan = destination.TryGetUnderlyingData(out byte[] destDataArr, out int destOffset) ? destDataArr.AsSpan(destOffset + (int)destinationIndex) : destination.GetSpanForCapacityUnsafe(destinationIndex); + source.CopyTo(destSpan); + + // Ensure destination stays alive for the copy operation + GC.KeepAlive(destination); + + // Update Length last to make sure the data is valid + if (destinationIndex + source.Length > destination.Length) + { + destination.Length = destinationIndex + (uint)source.Length; + } + } + +#endregion (Span).CopyTo extensions for copying to an (IBuffer) + #region (Byte []).CopyTo extensions for copying to an (IBuffer) /// @@ -68,9 +123,8 @@ public static IBuffer AsBuffer(this byte[] source, int offset, int length, int c public static void CopyTo(this byte[] source, IBuffer destination) { if (source == null) throw new ArgumentNullException(nameof(source)); - if (destination == null) throw new ArgumentNullException(nameof(destination)); - CopyTo(source, 0, destination, 0, source.Length); + CopyTo(source.AsSpan(), destination, 0); } @@ -87,27 +141,9 @@ public static void CopyTo(this byte[] source, IBuffer destination) public static void CopyTo(this byte[] source, int sourceIndex, IBuffer destination, uint destinationIndex, int count) { if (source == null) throw new ArgumentNullException(nameof(source)); - if (destination == null) throw new ArgumentNullException(nameof(destination)); - if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); - if (sourceIndex < 0) throw new ArgumentOutOfRangeException(nameof(sourceIndex)); - if (source.Length < sourceIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_IndexOutOfArrayBounds, nameof(sourceIndex)); - if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); - if (destination.Capacity < destinationIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_BufferIndexExceedsCapacity); - if (destination.Capacity - destinationIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInTargetBuffer); - if (count == 0) return; - // If destination is backed by a managed array, use the array instead of the pointer as it does not require pinning: - byte[] destDataArr; - int destDataOffs; - if (destination.TryGetUnderlyingData(out destDataArr, out destDataOffs)) - { - global::System.Buffer.BlockCopy(source, sourceIndex, destDataArr, (int)(destDataOffs + destinationIndex), count); - return; - } - - IntPtr destPtr = destination.GetPointerAtOffset(destinationIndex); - Marshal.Copy(source, sourceIndex, destPtr, count); - } + CopyTo(source.AsSpan(sourceIndex, count), destination, destinationIndex); + } #endregion (Byte []).CopyTo extensions for copying to an (IBuffer) @@ -140,40 +176,51 @@ public static byte[] ToArray(this IBuffer source, uint sourceIndex, int count) #endregion (IBuffer).ToArray extensions for copying to a new (Byte []) -#region (IBuffer).CopyTo extensions for copying to a (Byte []) +#region (IBuffer).CopyTo extensions for copying to a (Span) - public static void CopyTo(this IBuffer source, byte[] destination) + public static void CopyTo(this IBuffer source, Span destination) { if (source == null) throw new ArgumentNullException(nameof(source)); - if (destination == null) throw new ArgumentNullException(nameof(destination)); - CopyTo(source, 0, destination, 0, checked((int)source.Length)); + CopyTo(source, 0, destination, checked((int)source.Length)); } - - public static void CopyTo(this IBuffer source, uint sourceIndex, byte[] destination, int destinationIndex, int count) + public static void CopyTo(this IBuffer source, uint sourceIndex, Span destination, int count) { if (source == null) throw new ArgumentNullException(nameof(source)); - if (destination == null) throw new ArgumentNullException(nameof(destination)); if (count < 0) throw new ArgumentOutOfRangeException(nameof(count)); - if (destinationIndex < 0) throw new ArgumentOutOfRangeException(nameof(destinationIndex)); if (source.Length < sourceIndex) throw new ArgumentException("The specified buffer index is not within the buffer length."); if (source.Length - sourceIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInSourceBuffer); - if (destination.Length < destinationIndex) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_IndexOutOfArrayBounds); - if (destination.Length - destinationIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); + if (destination.Length < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientArrayElementsAfterOffset); if (count == 0) return; - // If source is backed by a managed array, use the array instead of the pointer as it does not require pinning: - byte[] srcDataArr; - int srcDataOffs; - if (source.TryGetUnderlyingData(out srcDataArr, out srcDataOffs)) - { - global::System.Buffer.BlockCopy(srcDataArr, (int)(srcDataOffs + sourceIndex), destination, destinationIndex, count); - return; - } - - IntPtr srcPtr = source.GetPointerAtOffset(sourceIndex); - Marshal.Copy(srcPtr, destination, destinationIndex, count); + Debug.Assert(sourceIndex <= int.MaxValue); + + Span srcSpan = source.TryGetUnderlyingData(out byte[] srcDataArr, out int srcOffset) ? srcDataArr.AsSpan(srcOffset + (int)sourceIndex, count) : source.GetSpanForCapacityUnsafe(sourceIndex).Slice(0, (int)count); + srcSpan.CopyTo(destination); + + // Ensure source and destination stay alive for the copy operation + GC.KeepAlive(source); + } + +#endregion (IBuffer).CopyTo extensions for copying to a (Span) + +#region (IBuffer).CopyTo extensions for copying to a (Byte []) + + public static void CopyTo(this IBuffer source, byte[] destination) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (destination == null) throw new ArgumentNullException(nameof(destination)); + + CopyTo(source, destination.AsSpan()); + } + + public static void CopyTo(this IBuffer source, uint sourceIndex, byte[] destination, int destinationIndex, int count) + { + if (source == null) throw new ArgumentNullException(nameof(source)); + if (destination == null) throw new ArgumentNullException(nameof(destination)); + + CopyTo(source, sourceIndex, destination.AsSpan(destinationIndex, count), count); } #endregion (IBuffer).CopyTo extensions for copying to a (Byte []) @@ -200,48 +247,25 @@ public static void CopyTo(this IBuffer source, uint sourceIndex, IBuffer destina if (destination.Capacity - destinationIndex < count) throw new ArgumentException(global::Windows.Storage.Streams.SR.Argument_InsufficientSpaceInTargetBuffer); if (count == 0) return; - // If source are destination are backed by managed arrays, use the arrays instead of the pointers as it does not require pinning: - byte[] srcDataArr, destDataArr; - int srcDataOffs, destDataOffs; - - bool srcIsManaged = source.TryGetUnderlyingData(out srcDataArr, out srcDataOffs); - bool destIsManaged = destination.TryGetUnderlyingData(out destDataArr, out destDataOffs); - - if (srcIsManaged && destIsManaged) - { - Debug.Assert(count <= int.MaxValue); - Debug.Assert(sourceIndex <= int.MaxValue); - Debug.Assert(destinationIndex <= int.MaxValue); + Debug.Assert(count <= int.MaxValue); + Debug.Assert(sourceIndex <= int.MaxValue); + Debug.Assert(destinationIndex <= int.MaxValue); - global::System.Buffer.BlockCopy(srcDataArr!, srcDataOffs + (int)sourceIndex, destDataArr!, destDataOffs + (int)destinationIndex, (int)count); - return; - } + // If source are destination are backed by managed arrays, use the arrays instead of the pointers as it does not require pinning: + Span srcSpan = source.TryGetUnderlyingData(out byte[] srcDataArr, out int srcOffset) ? srcDataArr.AsSpan(srcOffset + (int)sourceIndex, (int)count) : source.GetSpanForCapacityUnsafe(sourceIndex).Slice(0, (int)count); + Span destSpan = destination.TryGetUnderlyingData(out byte[] destDataArr, out int destOffset) ? destDataArr.AsSpan(destOffset + (int)destinationIndex) : destination.GetSpanForCapacityUnsafe(destinationIndex).Slice(0, (int)count); - IntPtr srcPtr, destPtr; + srcSpan.CopyTo(destSpan); - if (srcIsManaged) - { - Debug.Assert(count <= int.MaxValue); - Debug.Assert(sourceIndex <= int.MaxValue); + // Ensure source and destination stay alive for the copy operation + GC.KeepAlive(source); + GC.KeepAlive(destination); - destPtr = destination.GetPointerAtOffset(destinationIndex); - Marshal.Copy(srcDataArr!, srcDataOffs + (int)sourceIndex, destPtr, (int)count); - return; - } - - if (destIsManaged) + // Update Length last to make sure the data is valid + if (destinationIndex + count > destination.Length) { - Debug.Assert(count <= int.MaxValue); - Debug.Assert(destinationIndex <= int.MaxValue); - - srcPtr = source.GetPointerAtOffset(sourceIndex); - Marshal.Copy(srcPtr, destDataArr!, destDataOffs + (int)destinationIndex, (int)count); - return; + destination.Length = destinationIndex + count; } - - srcPtr = source.GetPointerAtOffset(sourceIndex); - destPtr = destination.GetPointerAtOffset(destinationIndex); - MemCopy(srcPtr, destPtr, count); } #endregion (IBuffer).CopyTo extensions for copying to an (IBuffer) @@ -313,13 +337,13 @@ public static bool IsSameData(this IBuffer buffer, IBuffer otherBuffer) if (thisIsManaged) return (thisDataArr == otherDataArr) && (thisDataOffs == otherDataOffs); - IBufferByteAccess thisBuff = buffer.As(); - IBufferByteAccess otherBuff = otherBuffer.As(); - - unsafe + if (!WindowsRuntimeMarshal.TryGetDataUnsafe(buffer, out IntPtr thisBuff) || + !WindowsRuntimeMarshal.TryGetDataUnsafe(otherBuffer, out IntPtr otherBuff)) { - return (thisBuff.Buffer == otherBuff.Buffer); + return false; } + + return thisBuff == otherBuff; } #endregion Access to underlying array optimised for IBuffers backed by managed arrays (to avoid pinning) @@ -411,14 +435,15 @@ public static Stream AsStream(this IBuffer source) if (source.TryGetUnderlyingData(out dataArr, out dataOffs)) { Debug.Assert(source.Capacity < int.MaxValue); - return new MemoryStream(dataArr, dataOffs, (int)source.Capacity, true); + return new WindowsRuntimeBufferMemoryStream(source, dataArr, dataOffs); } - unsafe + if (!WindowsRuntimeMarshal.TryGetDataUnsafe(source, out IntPtr sourceBuff)) { - IBufferByteAccess bufferByteAccess = source.As(); - return new WindowsRuntimeBufferUnmanagedMemoryStream(source, (byte*)bufferByteAccess.Buffer); + throw new InvalidCastException(); } + + return new WindowsRuntimeBufferUnmanagedMemoryStream(source, sourceBuff); } #endregion Extensions for co-operation with memory streams (share mem stream data; expose data as managed/unmanaged mem stream) @@ -438,20 +463,112 @@ public static byte GetByte(this IBuffer source, uint byteOffset) return srcDataArr[srcDataOffs + byteOffset]; } - IntPtr srcPtr = source.GetPointerAtOffset(byteOffset); - unsafe - { - // Let's avoid an unnesecary call to Marshal.ReadByte(): - byte* ptr = (byte*)srcPtr; - return *ptr; - } + Span srcSpan = source.GetSpanForCapacityUnsafe(byteOffset); + byte value = srcSpan[0]; + + // Ensure source stays alive while we read values. + GC.KeepAlive(source); + return value; + } + + #endregion Extensions for direct by-offset access to buffer data elements + + + #region Private plumbing + +#if NET + private sealed class StreamWinRTTypeDetails : global::WinRT.IWinRTExposedTypeDetails + { + public ComWrappers.ComInterfaceEntry[] GetExposedInterfaces() + { + return new ComWrappers.ComInterfaceEntry[] + { + new ComWrappers.ComInterfaceEntry + { + IID = global::ABI.System.IDisposableMethods.IID, + Vtable = global::ABI.System.IDisposableMethods.AbiToProjectionVftablePtr + }, + }; + } } +#endif + +#if NET + [global::WinRT.WinRTExposedType(typeof(StreamWinRTTypeDetails))] +#endif + private sealed class WindowsRuntimeBufferMemoryStream : MemoryStream + { + private readonly IBuffer _sourceBuffer; -#endregion Extensions for direct by-offset access to buffer data elements + internal WindowsRuntimeBufferMemoryStream(IBuffer sourceBuffer, byte[] dataArr, int dataOffs) + : base(dataArr, dataOffs, (int)sourceBuffer.Capacity, writable: true) + { + _sourceBuffer = sourceBuffer; + SetLength((long)sourceBuffer.Length); + } -#region Private plumbing + public override void SetLength(long value) + { + base.SetLength(value); + + // Length is limited by Capacity which should be a valid value. + // Therefore this cast is safe. + _sourceBuffer.Length = (uint)Length; + } + public override void Write(byte[] buffer, int offset, int count) + { + base.Write(buffer, offset, count); + + // Length is limited by Capacity which should be a valid value. + // Therefore this cast is safe. + _sourceBuffer.Length = (uint)Length; + } + +#if NET + public override void Write(ReadOnlySpan buffer) + { + base.Write(buffer); + + // Length is limited by Capacity which should be a valid value. + // Therefore this cast is safe. + _sourceBuffer.Length = (uint)Length; + } +#endif + + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + await base.WriteAsync(buffer, offset, count, cancellationToken); + // Length is limited by Capacity which should be a valid value. + // Therefore this cast is safe. + _sourceBuffer.Length = (uint)Length; + } + +#if NET + public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + await base.WriteAsync(buffer, cancellationToken); + + // Length is limited by Capacity which should be a valid value. + // Therefore this cast is safe. + _sourceBuffer.Length = (uint)Length; + } +#endif + + public override void WriteByte(byte value) + { + base.WriteByte(value); + + // Length is limited by Capacity which should be a valid value. + // Therefore this cast is safe. + _sourceBuffer.Length = (uint)Length; + } + } // class WindowsRuntimeBufferMemoryStream + +#if NET + [global::WinRT.WinRTExposedType(typeof(StreamWinRTTypeDetails))] +#endif private sealed class WindowsRuntimeBufferUnmanagedMemoryStream : UnmanagedMemoryStream { // We need this class because if we construct an UnmanagedMemoryStream on an IBuffer backed by native memory, @@ -460,46 +577,84 @@ private sealed class WindowsRuntimeBufferUnmanagedMemoryStream : UnmanagedMemory private readonly IBuffer _sourceBuffer; - internal unsafe WindowsRuntimeBufferUnmanagedMemoryStream(IBuffer sourceBuffer, byte* dataPtr) + internal unsafe WindowsRuntimeBufferUnmanagedMemoryStream(IBuffer sourceBuffer, IntPtr dataPtr) - : base(dataPtr, (long)sourceBuffer.Length, (long)sourceBuffer.Capacity, FileAccess.ReadWrite) + : base((byte*)dataPtr, (long)sourceBuffer.Length, (long)sourceBuffer.Capacity, FileAccess.ReadWrite) { _sourceBuffer = sourceBuffer; } - } // class WindowsRuntimeBufferUnmanagedMemoryStream - - private static IntPtr GetPointerAtOffset(this IBuffer buffer, uint offset) - { - Debug.Assert(0 <= offset); - Debug.Assert(offset < buffer.Capacity); - unsafe - { - IntPtr buffPtr = buffer.As().Buffer; - return new IntPtr((byte*)buffPtr + offset); + public override void SetLength(long value) + { + base.SetLength(value); + + // Length is limited by Capacity which should be a valid value. + // Therefore this cast is safe. + _sourceBuffer.Length = (uint)Length; } - } - private static unsafe void MemCopy(IntPtr src, IntPtr dst, uint count) - { - if (count > int.MaxValue) - { - MemCopy(src, dst, int.MaxValue); - MemCopy(src + int.MaxValue, dst + int.MaxValue, count - int.MaxValue); - return; + public override void Write(byte[] buffer, int offset, int count) + { + base.Write(buffer, offset, count); + + // Length is limited by Capacity which should be a valid value. + // Therefore this cast is safe. + _sourceBuffer.Length = (uint)Length; + } + +#if NET + public override void Write(ReadOnlySpan buffer) + { + base.Write(buffer); + + // Length is limited by Capacity which should be a valid value. + // Therefore this cast is safe. + _sourceBuffer.Length = (uint)Length; } +#endif + + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + await base.WriteAsync(buffer, offset, count, cancellationToken); + // Length is limited by Capacity which should be a valid value. + // Therefore this cast is safe. + _sourceBuffer.Length = (uint)Length; + } + +#if NET + public override async ValueTask WriteAsync(ReadOnlyMemory buffer, CancellationToken cancellationToken = default) + { + await base.WriteAsync(buffer, cancellationToken); + + // Length is limited by Capacity which should be a valid value. + // Therefore this cast is safe. + _sourceBuffer.Length = (uint)Length; + } +#endif + + public override void WriteByte(byte value) + { + base.WriteByte(value); + + // Length is limited by Capacity which should be a valid value. + // Therefore this cast is safe. + _sourceBuffer.Length = (uint)Length; + } + } // class WindowsRuntimeBufferUnmanagedMemoryStream - Debug.Assert(count <= int.MaxValue); - int bCount = (int)count; - - - // Copy via buffer. - // Note: if becomes perf critical, we will port the routine that - // copies the data without using Marshal (and byte[]) - byte[] tmp = new byte[bCount]; - Marshal.Copy(src, tmp, 0, bCount); - Marshal.Copy(tmp, 0, dst, bCount); - return; + private static unsafe Span GetSpanForCapacityUnsafe(this IBuffer buffer, uint offset) + { + Debug.Assert(0 <= offset); + Debug.Assert(offset < buffer.Capacity); + + if (!WindowsRuntimeMarshal.TryGetDataUnsafe(buffer, out IntPtr buffPtr)) + { + throw new InvalidCastException(); + } + + var span = new Span((byte*)buffPtr + offset, (int)(buffer.Capacity - offset)); + GC.KeepAlive(buffer); + return span; } #endregion Private plumbing } // class WindowsRuntimeBufferExtensions diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeMarshal.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeMarshal.cs new file mode 100644 index 000000000..6324350db --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeMarshal.cs @@ -0,0 +1,146 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace System.Runtime.InteropServices.WindowsRuntime +{ + using System; + using System.Diagnostics; + using System.Diagnostics.CodeAnalysis; + using global::Windows.Foundation; + using global::Windows.Storage.Streams; + using WinRT; + +#nullable enable + /// + /// An unsafe class that provides a set of methods to access the underlying data representations of WinRT types. + /// +#if EMBED || !NET // for netstandard this type conflicts with the type in the BCL so make it internal + internal +#else + public +#endif + static partial class WindowsRuntimeMarshal + { + /// + /// Returns a pointer to the underlying data representation of the . + /// Callers are responsible for ensuring that the buffer is kept alive while the pointer is in use. + /// + /// The buffer to get the data pointer for. + /// The pointer to the underlying data representation of the buffer. + /// Whether the data was successfully retrieved. + /// Thrown if invoking IBufferByteAccess::Buffer on the input buffer fails. + public static unsafe bool TryGetDataUnsafe( +#if NET + [NotNullWhen(true)] +#endif + IBuffer? buffer, out IntPtr dataPtr) + { + if (buffer == null) + { + dataPtr = IntPtr.Zero; + return false; + } + + if (ComWrappersSupport.TryUnwrapObject(buffer, out var unwrapped) && + unwrapped.TryAs(global::WinRT.Interop.IID.IID_IBufferByteAccess, out IntPtr ThisPtr) >= 0) + { + try + { + IntPtr __retval = default; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3](ThisPtr, &__retval)); + dataPtr = __retval; + return true; + } + finally + { + Marshal.Release(ThisPtr); + } + } + + if (buffer is IBufferByteAccess managedBuffer) + { + dataPtr = managedBuffer.Buffer; + return true; + } + + dataPtr = IntPtr.Zero; + return false; + } + + /// + /// Returns a pointer to the underlying data representation of the . + /// Callers are responsible for ensuring that the buffer is kept alive while the pointer is in use. + /// + /// The buffer to get the data pointer for. + /// The pointer to the underlying data representation of the buffer. + /// The capacity of the buffer. + /// Whether the data was successfully retrieved. + /// Thrown if invoking IMemoryBufferByteAccess::Buffer on the input buffer fails. + public static unsafe bool TryGetDataUnsafe( +#if NET + [NotNullWhen(true)] +#endif + IMemoryBufferReference? buffer, out IntPtr dataPtr, out uint capacity) + { + if (buffer == null) + { + dataPtr = IntPtr.Zero; + capacity = 0; + return false; + } + + if (ComWrappersSupport.TryUnwrapObject(buffer, out var unwrapped) && + unwrapped.TryAs(global::WinRT.Interop.IID.IID_IMemoryBufferByteAccess, out IntPtr ThisPtr) >= 0) + { + try + { + IntPtr __retval = default; + uint __capacity = 0; + global::WinRT.ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3](ThisPtr, &__retval, &__capacity)); + dataPtr = __retval; + capacity = __capacity; + return true; + } + finally + { + Marshal.Release(ThisPtr); + } + } + + dataPtr = IntPtr.Zero; + capacity = 0; + return false; + } + + /// + /// Tries to get an array segment from the underlying buffer. The return value indicates the success of the operation. + /// + /// The buffer to get the array segment for. + /// When this method returns, contains the array segment retrieved from the underlying buffer. If the method fails, the method returns a default array segment. + public static bool TryGetArray( +#if NET + [NotNullWhen(true)] +# endif + IBuffer? buffer, out ArraySegment array) + { + if (buffer == null) + { + array = default; + return false; + } + + // If buffer is backed by a managed array, return it + if (buffer.TryGetUnderlyingData(out byte[]? srcDataArr, out int srcDataOffs)) + { + array = new ArraySegment(srcDataArr, offset: srcDataOffs, count: (int)buffer.Length); + return true; + } + + array = default; + return false; + } + } // class WindowsRuntimeMarshal +#nullable restore +} // namespace + +// WindowsRuntimeMarshal.cs diff --git a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeStreamExtensions.cs b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeStreamExtensions.cs index e13d067d1..ed99aca1a 100644 --- a/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeStreamExtensions.cs +++ b/src/cswinrt/strings/additions/Windows.Storage.Streams/WindowsRuntimeStreamExtensions.cs @@ -7,6 +7,9 @@ namespace System.IO { using System.ComponentModel; using System.Diagnostics; +#if NET + using System.Diagnostics.CodeAnalysis; +#endif using System.IO; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -42,10 +45,13 @@ private static readonly ConditionalWeakTable #region Helpers #if DEBUG - private static void AssertMapContains(ConditionalWeakTable map, TKey key, TValue value, - bool valueMayBeWrappedInBufferedStream) - where TKey : class - where TValue : class + private static void AssertMapContains( + ConditionalWeakTable map, + TKey key, + TValue value, + bool valueMayBeWrappedInBufferedStream) + where TKey : class + where TValue : class { TValue valueInMap; diff --git a/src/cswinrt/strings/additions/Windows.Storage/IStorageFolderHandleAccess.cs b/src/cswinrt/strings/additions/Windows.Storage/IStorageFolderHandleAccess.cs index 2b9f841a1..c3f5d9540 100644 --- a/src/cswinrt/strings/additions/Windows.Storage/IStorageFolderHandleAccess.cs +++ b/src/cswinrt/strings/additions/Windows.Storage/IStorageFolderHandleAccess.cs @@ -2,122 +2,56 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. - -namespace Windows.Storage -{ - using Microsoft.Win32.SafeHandles; - // Available in 14393 (RS1) and later - [Guid("DF19938F-5462-48A0-BE65-D2A3271A08D6")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.Storage.IStorageFolderHandleAccess))] - internal interface IStorageFolderHandleAccess - { - SafeFileHandle Create( - [MarshalAs(UnmanagedType.LPWStr)] string fileName, - HANDLE_CREATION_OPTIONS creationOptions, - HANDLE_ACCESS_OPTIONS accessOptions, - HANDLE_SHARING_OPTIONS sharingOptions, - HANDLE_OPTIONS options, - IntPtr oplockBreakingHandler); - } -} - namespace ABI.Windows.Storage { using global::Microsoft.Win32.SafeHandles; - using global::System; - using global::System.ComponentModel; - -#if !NET - [global::WinRT.ObjectReferenceWrapper(nameof(_obj)), EditorBrowsable(EditorBrowsableState.Never)] - [Guid("DF19938F-5462-48A0-BE65-D2A3271A08D6")] - internal unsafe class IStorageFolderHandleAccess : global::Windows.Storage.IStorageFolderHandleAccess - { - [Guid("DF19938F-5462-48A0-BE65-D2A3271A08D6")] - public struct Vftbl - { - public IUnknownVftbl IUnknownVftbl; - private void* _Create_0; - public delegate* unmanaged[Stdcall] Create_0 => (delegate* unmanaged[Stdcall])_Create_0; - } - - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator IStorageFolderHandleAccess(IObjectReference obj) => (obj != null) ? new IStorageFolderHandleAccess(obj) : null; - protected readonly ObjectReference _obj; - public IObjectReference ObjRef { get => _obj; } - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public IStorageFolderHandleAccess(IObjectReference obj) : this(obj.As()) { } - internal IStorageFolderHandleAccess(ObjectReference obj) - { - _obj = obj; - } - - public unsafe SafeFileHandle Create( - string fileName, - global::Windows.Storage.HANDLE_CREATION_OPTIONS creationOptions, - global::Windows.Storage.HANDLE_ACCESS_OPTIONS accessOptions, - global::Windows.Storage.HANDLE_SHARING_OPTIONS sharingOptions, - global::Windows.Storage.HANDLE_OPTIONS options, + using global::System; + + // Available in 14393 (RS1) and later + internal static class IStorageFolderHandleAccessMethods + { +#if NET + public static global::System.Guid IID { get; } = new(new global::System.ReadOnlySpan(new byte[] { 0x8F, 0x93, 0x19, 0xDF, 0x62, 0x54, 0xA0, 0x48, 0xBE, 0x65, 0xD2, 0xA3, 0x27, 0x1A, 0x08, 0xD6 })); +#else + public static global::System.Guid IID { get; } = new(0xDF19938F, 0x5462, 0x48A0, 0xBE, 0x65, 0xD2, 0xA3, 0x27, 0x1A, 0x08, 0xD6); +#endif + + public static unsafe SafeFileHandle Create( + global::Windows.Storage.IStorageFolder storageFolder, + string fileName, + global::Windows.Storage.HANDLE_CREATION_OPTIONS creationOptions, + global::Windows.Storage.HANDLE_ACCESS_OPTIONS accessOptions, + global::Windows.Storage.HANDLE_SHARING_OPTIONS sharingOptions, + global::Windows.Storage.HANDLE_OPTIONS options, IntPtr oplockBreakingHandler) - { - SafeFileHandle interopHandle = default; - IntPtr _interopHandle = default; - try - { - fixed (char* fileNamePtr = fileName) - { - ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Create_0(ThisPtr, (IntPtr)fileNamePtr, creationOptions, accessOptions, sharingOptions, options, oplockBreakingHandler, out _interopHandle)); - } - } - finally - { - interopHandle = new SafeFileHandle(_interopHandle, true); - } - return interopHandle; - } - } -#else - [DynamicInterfaceCastableImplementation] - [Guid("DF19938F-5462-48A0-BE65-D2A3271A08D6")] - internal unsafe interface IStorageFolderHandleAccess : global::Windows.Storage.IStorageFolderHandleAccess - { - [Guid("DF19938F-5462-48A0-BE65-D2A3271A08D6")] - public struct Vftbl - { - public IUnknownVftbl IUnknownVftbl; - private void* _Create_0; - public delegate* unmanaged[Stdcall] Create_0 => (delegate* unmanaged[Stdcall])_Create_0; - } + { + global::WinRT.ObjectReferenceValue obj = default; + try + { + obj = global::WinRT.MarshalInspectable.CreateMarshaler2(storageFolder, IID); + } + catch (Exception) + { + return null; + } - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - unsafe SafeFileHandle global::Windows.Storage.IStorageFolderHandleAccess.Create( - string fileName, - global::Windows.Storage.HANDLE_CREATION_OPTIONS creationOptions, - global::Windows.Storage.HANDLE_ACCESS_OPTIONS accessOptions, - global::Windows.Storage.HANDLE_SHARING_OPTIONS sharingOptions, - global::Windows.Storage.HANDLE_OPTIONS options, - IntPtr oplockBreakingHandler) - { SafeFileHandle interopHandle = default; IntPtr _interopHandle = default; - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Storage.IStorageFolderHandleAccess).TypeHandle)); - var ThisPtr = _obj.ThisPtr; try - { + { + var ThisPtr = obj.GetAbi(); fixed (char* fileNamePtr = fileName) { - ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Create_0(ThisPtr, (IntPtr)fileNamePtr, creationOptions, accessOptions, sharingOptions, options, oplockBreakingHandler, out _interopHandle)); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3] + (ThisPtr, (IntPtr)fileNamePtr, creationOptions, accessOptions, sharingOptions, options, oplockBreakingHandler, &_interopHandle)); } } finally { - interopHandle = new SafeFileHandle(_interopHandle, true); + interopHandle = new SafeFileHandle(_interopHandle, true); + obj.Dispose(); } return interopHandle; } } -#endif } diff --git a/src/cswinrt/strings/additions/Windows.Storage/IStorageItemHandleAccess.cs b/src/cswinrt/strings/additions/Windows.Storage/IStorageItemHandleAccess.cs index ecd1bab08..7946e0186 100644 --- a/src/cswinrt/strings/additions/Windows.Storage/IStorageItemHandleAccess.cs +++ b/src/cswinrt/strings/additions/Windows.Storage/IStorageItemHandleAccess.cs @@ -2,109 +2,50 @@ // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. -namespace Windows.Storage -{ - using Microsoft.Win32.SafeHandles; - // Available in 14393 (RS1) and later - [Guid("5CA296B2-2C25-4D22-B785-B885C8201E6A")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.Storage.IStorageItemHandleAccess))] - internal interface IStorageItemHandleAccess - { - SafeFileHandle Create( - HANDLE_ACCESS_OPTIONS accessOptions, - HANDLE_SHARING_OPTIONS sharingOptions, - HANDLE_OPTIONS options, - IntPtr oplockBreakingHandler); - } -} - namespace ABI.Windows.Storage { using global::Microsoft.Win32.SafeHandles; - using global::System.ComponentModel; - -#if !NET - [global::WinRT.ObjectReferenceWrapper(nameof(_obj)), EditorBrowsable(EditorBrowsableState.Never)] - [Guid("5CA296B2-2C25-4D22-B785-B885C8201E6A")] - internal unsafe class IStorageItemHandleAccess : global::Windows.Storage.IStorageItemHandleAccess - { - [Guid("5CA296B2-2C25-4D22-B785-B885C8201E6A")] - public struct Vftbl - { - public IUnknownVftbl IUnknownVftbl; - private void* _Create_0; - public delegate* unmanaged[Stdcall] Create_0 => (delegate* unmanaged[Stdcall])_Create_0; - } - - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - public static implicit operator IStorageItemHandleAccess(IObjectReference obj) => (obj != null) ? new IStorageItemHandleAccess(obj) : null; - protected readonly ObjectReference _obj; - public IObjectReference ObjRef { get => _obj; } - public IntPtr ThisPtr => _obj.ThisPtr; - public ObjectReference AsInterface() => _obj.As(); - public A As() => _obj.AsType(); - public IStorageItemHandleAccess(IObjectReference obj) : this(obj.As()) { } - internal IStorageItemHandleAccess(ObjectReference obj) - { - _obj = obj; - } - - public SafeFileHandle Create( - global::Windows.Storage.HANDLE_ACCESS_OPTIONS accessOptions, - global::Windows.Storage.HANDLE_SHARING_OPTIONS sharingOptions, - global::Windows.Storage.HANDLE_OPTIONS options, - IntPtr oplockBreakingHandler) - { - SafeFileHandle interopHandle = default; - IntPtr _interopHandle = default; - try - { - ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Create_0(ThisPtr, accessOptions, sharingOptions, options, oplockBreakingHandler, out _interopHandle)); - } - finally - { - interopHandle = new SafeFileHandle(_interopHandle, true); - } - return interopHandle; - } - } -#else - - [DynamicInterfaceCastableImplementation] - [Guid("5CA296B2-2C25-4D22-B785-B885C8201E6A")] - internal unsafe interface IStorageItemHandleAccess : global::Windows.Storage.IStorageItemHandleAccess - { - [Guid("5CA296B2-2C25-4D22-B785-B885C8201E6A")] - public struct Vftbl - { - public IUnknownVftbl IUnknownVftbl; - private void* _Create_0; - public delegate* unmanaged[Stdcall] Create_0 => (delegate* unmanaged[Stdcall])_Create_0; - } - - internal static ObjectReference FromAbi(IntPtr thisPtr) => ObjectReference.FromAbi(thisPtr); - - SafeFileHandle global::Windows.Storage.IStorageItemHandleAccess.Create( + + // Available in 14393 (RS1) and later + internal static class IStorageItemHandleAccessMethods + { +#if NET + public static global::System.Guid IID { get; } = new(new global::System.ReadOnlySpan(new byte[] { 0xB2, 0x96, 0xA2, 0x5C, 0x25, 0x2C, 0x22, 0x4D, 0xB7, 0x85, 0xB8, 0x85, 0xC8, 0x20, 0x1E, 0x6A })); +#else + public static global::System.Guid IID { get; } = new(0x5CA296B2, 0x2C25, 0x4D22, 0xB7, 0x85, 0xB8, 0x85, 0xC8, 0x20, 0x1E, 0x6A); +#endif + + public static unsafe SafeFileHandle Create( + global::Windows.Storage.IStorageFile storageFile, global::Windows.Storage.HANDLE_ACCESS_OPTIONS accessOptions, global::Windows.Storage.HANDLE_SHARING_OPTIONS sharingOptions, global::Windows.Storage.HANDLE_OPTIONS options, IntPtr oplockBreakingHandler) { + global::WinRT.ObjectReferenceValue obj = default; + try + { + obj = global::WinRT.MarshalInspectable.CreateMarshaler2(storageFile, IID); + } + catch (Exception) + { + return null; + } + SafeFileHandle interopHandle = default; IntPtr _interopHandle = default; - var _obj = ((ObjectReference)((IWinRTObject)this).GetObjectReferenceForType(typeof(global::Windows.Storage.IStorageItemHandleAccess).TypeHandle)); - var ThisPtr = _obj.ThisPtr; try - { - ExceptionHelpers.ThrowExceptionForHR(_obj.Vftbl.Create_0(ThisPtr, accessOptions, sharingOptions, options, oplockBreakingHandler, out _interopHandle)); + { + var ThisPtr = obj.GetAbi(); + ExceptionHelpers.ThrowExceptionForHR((*(delegate* unmanaged[Stdcall]**)ThisPtr)[3] + (ThisPtr, accessOptions, sharingOptions, options, oplockBreakingHandler, &_interopHandle)); } finally { interopHandle = new SafeFileHandle(_interopHandle, true); + obj.Dispose(); } return interopHandle; } } -#endif } diff --git a/src/cswinrt/strings/additions/Windows.Storage/WindowsRuntimeStorageExtensions.cs b/src/cswinrt/strings/additions/Windows.Storage/WindowsRuntimeStorageExtensions.cs index eb6837849..6bb942a8d 100644 --- a/src/cswinrt/strings/additions/Windows.Storage/WindowsRuntimeStorageExtensions.cs +++ b/src/cswinrt/strings/additions/Windows.Storage/WindowsRuntimeStorageExtensions.cs @@ -200,12 +200,8 @@ public static SafeFileHandle CreateSafeFileHandle( HANDLE_SHARING_OPTIONS sharingOptions = FileShareToHandleSharingOptions(share); HANDLE_OPTIONS handleOptions = FileOptionsToHandleOptions(options); - IStorageItemHandleAccess handleAccess = windowsRuntimeFile.As(); - - if (handleAccess == null) - return null; - - return handleAccess.Create( + return global::ABI.Windows.Storage.IStorageItemHandleAccessMethods.Create( + windowsRuntimeFile, accessOptions, sharingOptions, handleOptions, @@ -238,12 +234,8 @@ public static SafeFileHandle CreateSafeFileHandle( HANDLE_SHARING_OPTIONS sharingOptions = FileShareToHandleSharingOptions(share); HANDLE_OPTIONS handleOptions = FileOptionsToHandleOptions(options); - IStorageFolderHandleAccess handleAccess = rootDirectory.As(); - - if (handleAccess == null) - return null; - - return handleAccess.Create( + return global::ABI.Windows.Storage.IStorageFolderHandleAccessMethods.Create( + rootDirectory, relativePath, creationOptions, accessOptions, diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Controls/Windows.UI.Xaml.Controls.Primitives.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Controls/Windows.UI.Xaml.Controls.Primitives.cs new file mode 100644 index 000000000..194a509a7 --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Controls/Windows.UI.Xaml.Controls.Primitives.cs @@ -0,0 +1,76 @@ + +namespace Windows.UI.Xaml.Controls.Primitives +{ + using global::Windows.Foundation; + + [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.Controls.Primitives.GeneratorPosition))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif + [StructLayout(LayoutKind.Sequential)] +#if EMBED + internal +#else + public +#endif + struct GeneratorPosition + { + private int _index; + private int _offset; + + public int Index { get { return _index; } set { _index = value; } } + public int Offset { get { return _offset; } set { _offset = value; } } + + public GeneratorPosition(int index, int offset) + { + _index = index; + _offset = offset; + } + + public override int GetHashCode() + { + return _index.GetHashCode() + _offset.GetHashCode(); + } + + public override string ToString() + { + return string.Concat("GeneratorPosition (", _index.ToString(global::System.Globalization.CultureInfo.InvariantCulture), ",", _offset.ToString(global::System.Globalization.CultureInfo.InvariantCulture), ")"); + } + + public override bool Equals(object o) + { + if (o is GeneratorPosition) + { + GeneratorPosition that = (GeneratorPosition)o; + return _index == that._index && + _offset == that._offset; + } + return false; + } + + public static bool operator ==(GeneratorPosition gp1, GeneratorPosition gp2) + { + return gp1._index == gp2._index && + gp1._offset == gp2._offset; + } + + public static bool operator !=(GeneratorPosition gp1, GeneratorPosition gp2) + { + return !(gp1 == gp2); + } + } +} + +namespace ABI.Windows.UI.Xaml.Controls.Primitives +{ +#if EMBED + internal +#else + public +#endif + static class GeneratorPosition + { + public static string GetGuidSignature() => $"struct(Windows.UI.Xaml.Controls.Primitives.GeneratorPosition;i4;i4)"; + } +} diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.cs new file mode 100644 index 000000000..3170b4fb7 --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Animation/Windows.UI.Xaml.Media.Animation.cs @@ -0,0 +1,335 @@ + +namespace Windows.UI.Xaml.Media.Animation +{ + using global::Windows.Foundation; + + [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.Media.Animation.KeyTime))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif + [StructLayout(LayoutKind.Sequential)] +#if EMBED + internal +#else + public +#endif + struct KeyTime + { + private TimeSpan _timeSpan; + + public static KeyTime FromTimeSpan(TimeSpan timeSpan) + { + if (timeSpan < TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException(nameof(timeSpan)); + } + + KeyTime keyTime = default; + + keyTime._timeSpan = timeSpan; + + return keyTime; + } + + public static bool Equals(KeyTime keyTime1, KeyTime keyTime2) + { + return (keyTime1._timeSpan == keyTime2._timeSpan); + } + + public static bool operator ==(KeyTime keyTime1, KeyTime keyTime2) + { + return KeyTime.Equals(keyTime1, keyTime2); + } + + public static bool operator !=(KeyTime keyTime1, KeyTime keyTime2) + { + return !KeyTime.Equals(keyTime1, keyTime2); + } + + public bool Equals(KeyTime value) + { + return KeyTime.Equals(this, value); + } + + public override bool Equals(object value) + { + return value is KeyTime && this == (KeyTime)value; + } + + public override int GetHashCode() + { + return _timeSpan.GetHashCode(); + } + + public override string ToString() + { + return _timeSpan.ToString(); + } + + public static implicit operator KeyTime(TimeSpan timeSpan) + { + return KeyTime.FromTimeSpan(timeSpan); + } + + public TimeSpan TimeSpan + { + get + { + return _timeSpan; + } + } + } + + [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.EnumTypeDetails))] +#endif +#if EMBED + internal +#else + public +#endif + enum RepeatBehaviorType + { + Count, + Duration, + Forever + } + + [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.Media.Animation.RepeatBehavior))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif + [StructLayout(LayoutKind.Sequential)] +#if EMBED + internal +#else + public +#endif + struct RepeatBehavior : IFormattable + { + private double _Count; + private TimeSpan _Duration; + private RepeatBehaviorType _Type; + + internal static bool IsFinite(double value) + { + return !(double.IsNaN(value) || double.IsInfinity(value)); + } + + public RepeatBehavior(double count) + { + if (!IsFinite(count) || count < 0.0) + { + throw new ArgumentOutOfRangeException(nameof(count)); + } + + _Duration = new TimeSpan(0); + _Count = count; + _Type = RepeatBehaviorType.Count; + } + + public RepeatBehavior(TimeSpan duration) + { + if (duration < new TimeSpan(0)) + { + throw new ArgumentOutOfRangeException(nameof(duration)); + } + + _Duration = duration; + _Count = 0.0; + _Type = RepeatBehaviorType.Duration; + } + + public static RepeatBehavior Forever + { + get + { + RepeatBehavior forever = default; + forever.Type = RepeatBehaviorType.Forever; + + return forever; + } + } + + public bool HasCount + { + get + { + return Type == RepeatBehaviorType.Count; + } + } + + public bool HasDuration + { + get + { + return Type == RepeatBehaviorType.Duration; + } + } + + public double Count + { + get { return _Count; } + set { _Count = value; } + } + + public TimeSpan Duration + { + get { return _Duration; } + set { _Duration = value; } + } + + public RepeatBehaviorType Type + { + get { return _Type; } + set { _Type = value; } + } + + public override string ToString() + { + return InternalToString(null, null); + } + + public string ToString(IFormatProvider formatProvider) + { + return InternalToString(null, formatProvider); + } + + string IFormattable.ToString(string format, IFormatProvider formatProvider) + { + return InternalToString(format, formatProvider); + } + + internal string InternalToString(string format, IFormatProvider formatProvider) + { + switch (_Type) + { + case RepeatBehaviorType.Forever: + + return "Forever"; + + case RepeatBehaviorType.Count: + + global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(); + + sb.AppendFormat( + formatProvider, + "{0:" + format + "}x", + _Count); + + return sb.ToString(); + + case RepeatBehaviorType.Duration: + + return _Duration.ToString(); + + default: + return string.Empty; + } + } + + public override bool Equals(object value) + { + if (value is RepeatBehavior) + { + return this.Equals((RepeatBehavior)value); + } + else + { + return false; + } + } + + public bool Equals(RepeatBehavior repeatBehavior) + { + if (_Type == repeatBehavior._Type) + { + return _Type switch + { + RepeatBehaviorType.Forever => true, + RepeatBehaviorType.Count => _Count == repeatBehavior._Count, + RepeatBehaviorType.Duration => _Duration == repeatBehavior._Duration, + _ => false, + }; + } + else + { + return false; + } + } + + public static bool Equals(RepeatBehavior repeatBehavior1, RepeatBehavior repeatBehavior2) + { + return repeatBehavior1.Equals(repeatBehavior2); + } + + public override int GetHashCode() + { + return _Type switch + { + RepeatBehaviorType.Count => _Count.GetHashCode(), + RepeatBehaviorType.Duration => _Duration.GetHashCode(), + + // We try to choose an unlikely hash code value for Forever. + // All Forevers need to return the same hash code value. + RepeatBehaviorType.Forever => int.MaxValue - 42, + + _ => base.GetHashCode(), + }; + } + + public static bool operator ==(RepeatBehavior repeatBehavior1, RepeatBehavior repeatBehavior2) + { + return repeatBehavior1.Equals(repeatBehavior2); + } + + public static bool operator !=(RepeatBehavior repeatBehavior1, RepeatBehavior repeatBehavior2) + { + return !repeatBehavior1.Equals(repeatBehavior2); + } + } +} + +namespace ABI.Windows.UI.Xaml.Media.Animation +{ +#if EMBED + internal +#else + public +#endif + static class KeyTime + { + public static string GetGuidSignature() + { + string timeSpanSignature = global::WinRT.GuidGenerator.GetSignature(typeof(global::System.TimeSpan)); + return $"struct(Windows.UI.Xaml.Media.Animation.KeyTime;{timeSpanSignature})"; + } + } + +#if EMBED + internal +#else + public +#endif + static class RepeatBehavior + { + public static string GetGuidSignature() + { + string timeSpanSignature = global::WinRT.GuidGenerator.GetSignature(typeof(global::System.TimeSpan)); + return $"struct(Windows.UI.Xaml.Media.Animation.RepeatBehavior;f8;{timeSpanSignature};{RepeatBehaviorType.GetGuidSignature()})"; + } + } + +#if EMBED + internal +#else + public +#endif + static class RepeatBehaviorType + { + public static string GetGuidSignature() => "enum(Windows.UI.Xaml.Media.Animation.RepeatBehaviorType;i4)"; + } +} diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.cs new file mode 100644 index 000000000..df02ccd76 --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media.Media3D/Windows.UI.Xaml.Media.Media3D.cs @@ -0,0 +1,714 @@ + +namespace Windows.UI.Xaml.Media.Media3D +{ + using global::Windows.Foundation; + + [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.Media.Media3D.Matrix3D))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif + [StructLayout(LayoutKind.Sequential)] +#if EMBED + internal +#else + public +#endif + struct Matrix3D : IFormattable + { + // Assuming this matrix has fourth column of 0,0,0,1 and isn't identity this function: + // Returns false if HasInverse is false, otherwise inverts the matrix. + private bool NormalizedAffineInvert() + { + double z20 = _m12 * _m23 - _m22 * _m13; + double z10 = _m32 * _m13 - _m12 * _m33; + double z00 = _m22 * _m33 - _m32 * _m23; + double det = _m31 * z20 + _m21 * z10 + _m11 * z00; + + if (IsZero(det)) + { + return false; + } + + // Compute 3x3 non-zero cofactors for the 2nd column + double z21 = _m21 * _m13 - _m11 * _m23; + double z11 = _m11 * _m33 - _m31 * _m13; + double z01 = _m31 * _m23 - _m21 * _m33; + + // Compute all six 2x2 determinants of 1st two columns + double y01 = _m11 * _m22 - _m21 * _m12; + double y02 = _m11 * _m32 - _m31 * _m12; + double y03 = _m11 * _offsetY - _offsetX * _m12; + double y12 = _m21 * _m32 - _m31 * _m22; + double y13 = _m21 * _offsetY - _offsetX * _m22; + double y23 = _m31 * _offsetY - _offsetX * _m32; + + // Compute all non-zero and non-one 3x3 cofactors for 2nd + // two columns + double z23 = _m23 * y03 - _offsetZ * y01 - _m13 * y13; + double z13 = _m13 * y23 - _m33 * y03 + _offsetZ * y02; + double z03 = _m33 * y13 - _offsetZ * y12 - _m23 * y23; + double z22 = y01; + double z12 = -y02; + double z02 = y12; + + double rcp = 1.0 / det; + + // Multiply all 3x3 cofactors by reciprocal & transpose + _m11 = (z00 * rcp); + _m12 = (z10 * rcp); + _m13 = (z20 * rcp); + + _m21 = (z01 * rcp); + _m22 = (z11 * rcp); + _m23 = (z21 * rcp); + + _m31 = (z02 * rcp); + _m32 = (z12 * rcp); + _m33 = (z22 * rcp); + + _offsetX = (z03 * rcp); + _offsetY = (z13 * rcp); + _offsetZ = (z23 * rcp); + + return true; + } + + // RETURNS true if has inverse & invert was done. Otherwise returns false & leaves matrix unchanged. + private bool InvertCore() + { + if (IsAffine) + { + return NormalizedAffineInvert(); + } + + // compute all six 2x2 determinants of 2nd two columns + double y01 = _m13 * _m24 - _m23 * _m14; + double y02 = _m13 * _m34 - _m33 * _m14; + double y03 = _m13 * _m44 - _offsetZ * _m14; + double y12 = _m23 * _m34 - _m33 * _m24; + double y13 = _m23 * _m44 - _offsetZ * _m24; + double y23 = _m33 * _m44 - _offsetZ * _m34; + + // Compute 3x3 cofactors for 1st the column + double z30 = _m22 * y02 - _m32 * y01 - _m12 * y12; + double z20 = _m12 * y13 - _m22 * y03 + _offsetY * y01; + double z10 = _m32 * y03 - _offsetY * y02 - _m12 * y23; + double z00 = _m22 * y23 - _m32 * y13 + _offsetY * y12; + + // Compute 4x4 determinant + double det = _offsetX * z30 + _m31 * z20 + _m21 * z10 + _m11 * z00; + + if (IsZero(det)) + { + return false; + } + + // Compute 3x3 cofactors for the 2nd column + double z31 = _m11 * y12 - _m21 * y02 + _m31 * y01; + double z21 = _m21 * y03 - _offsetX * y01 - _m11 * y13; + double z11 = _m11 * y23 - _m31 * y03 + _offsetX * y02; + double z01 = _m31 * y13 - _offsetX * y12 - _m21 * y23; + + // Compute all six 2x2 determinants of 1st two columns + y01 = _m11 * _m22 - _m21 * _m12; + y02 = _m11 * _m32 - _m31 * _m12; + y03 = _m11 * _offsetY - _offsetX * _m12; + y12 = _m21 * _m32 - _m31 * _m22; + y13 = _m21 * _offsetY - _offsetX * _m22; + y23 = _m31 * _offsetY - _offsetX * _m32; + + // Compute all 3x3 cofactors for 2nd two columns + double z33 = _m13 * y12 - _m23 * y02 + _m33 * y01; + double z23 = _m23 * y03 - _offsetZ * y01 - _m13 * y13; + double z13 = _m13 * y23 - _m33 * y03 + _offsetZ * y02; + double z03 = _m33 * y13 - _offsetZ * y12 - _m23 * y23; + double z32 = _m24 * y02 - _m34 * y01 - _m14 * y12; + double z22 = _m14 * y13 - _m24 * y03 + _m44 * y01; + double z12 = _m34 * y03 - _m44 * y02 - _m14 * y23; + double z02 = _m24 * y23 - _m34 * y13 + _m44 * y12; + + double rcp = 1.0 / det; + + // Multiply all 3x3 cofactors by reciprocal & transpose + _m11 = (z00 * rcp); + _m12 = (z10 * rcp); + _m13 = (z20 * rcp); + _m14 = (z30 * rcp); + + _m21 = (z01 * rcp); + _m22 = (z11 * rcp); + _m23 = (z21 * rcp); + _m24 = (z31 * rcp); + + _m31 = (z02 * rcp); + _m32 = (z12 * rcp); + _m33 = (z22 * rcp); + _m34 = (z32 * rcp); + + _offsetX = (z03 * rcp); + _offsetY = (z13 * rcp); + _offsetZ = (z23 * rcp); + _m44 = (z33 * rcp); + + return true; + } + + public Matrix3D(double m11, double m12, double m13, double m14, + double m21, double m22, double m23, double m24, + double m31, double m32, double m33, double m34, + double offsetX, double offsetY, double offsetZ, double m44) + { + _m11 = m11; + _m12 = m12; + _m13 = m13; + _m14 = m14; + _m21 = m21; + _m22 = m22; + _m23 = m23; + _m24 = m24; + _m31 = m31; + _m32 = m32; + _m33 = m33; + _m34 = m34; + _offsetX = offsetX; + _offsetY = offsetY; + _offsetZ = offsetZ; + _m44 = m44; + } + + // the transform is identity by default + // Actually fill in the fields - some (internal) code uses the fields directly for perf. + private static Matrix3D s_identity = CreateIdentity(); + + public double M11 + { + get + { + return _m11; + } + set + { + _m11 = value; + } + } + + public double M12 + { + get + { + return _m12; + } + set + { + _m12 = value; + } + } + + public double M13 + { + get + { + return _m13; + } + set + { + _m13 = value; + } + } + + public double M14 + { + get + { + return _m14; + } + set + { + _m14 = value; + } + } + + public double M21 + { + get + { + return _m21; + } + set + { + _m21 = value; + } + } + + public double M22 + { + get + { + return _m22; + } + set + { + _m22 = value; + } + } + + public double M23 + { + get + { + return _m23; + } + set + { + _m23 = value; + } + } + + public double M24 + { + get + { + return _m24; + } + set + { + _m24 = value; + } + } + + public double M31 + { + get + { + return _m31; + } + set + { + _m31 = value; + } + } + + public double M32 + { + get + { + return _m32; + } + set + { + _m32 = value; + } + } + + public double M33 + { + get + { + return _m33; + } + set + { + _m33 = value; + } + } + + public double M34 + { + get + { + return _m34; + } + set + { + _m34 = value; + } + } + + public double OffsetX + { + get + { + return _offsetX; + } + set + { + _offsetX = value; + } + } + + public double OffsetY + { + get + { + return _offsetY; + } + set + { + _offsetY = value; + } + } + + public double OffsetZ + { + get + { + return _offsetZ; + } + set + { + _offsetZ = value; + } + } + + public double M44 + { + get + { + return _m44; + } + set + { + _m44 = value; + } + } + + public static Matrix3D Identity + { + get + { + return s_identity; + } + } + + public bool IsIdentity + { + get + { + return (_m11 == 1 && _m12 == 0 && _m13 == 0 && _m14 == 0 && + _m21 == 0 && _m22 == 1 && _m23 == 0 && _m24 == 0 && + _m31 == 0 && _m32 == 0 && _m33 == 1 && _m34 == 0 && + _offsetX == 0 && _offsetY == 0 && _offsetZ == 0 && _m44 == 1); + } + } + + public override string ToString() + { + // Delegate to the internal method which implements all ToString calls. + return ConvertToString(null /* format string */, null /* format provider */); + } + + public string ToString(IFormatProvider provider) + { + // Delegate to the internal method which implements all ToString calls. + return ConvertToString(null /* format string */, provider); + } + + string IFormattable.ToString(string format, IFormatProvider provider) + { + // Delegate to the internal method which implements all ToString calls. + return ConvertToString(format, provider); + } + + private string ConvertToString(string format, IFormatProvider provider) + { + if (IsIdentity) + { + return "Identity"; + } + + // Helper to get the numeric list separator for a given culture. + char separator = TokenizerHelper.GetNumericListSeparator(provider); + return string.Format(provider, + "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}{0}{5:" + format + + "}{0}{6:" + format + "}{0}{7:" + format + "}{0}{8:" + format + "}{0}{9:" + format + "}{0}{10:" + format + + "}{0}{11:" + format + "}{0}{12:" + format + "}{0}{13:" + format + "}{0}{14:" + format + "}{0}{15:" + format + "}{0}{16:" + format + "}", + separator, + _m11, _m12, _m13, _m14, + _m21, _m22, _m23, _m24, + _m31, _m32, _m33, _m34, + _offsetX, _offsetY, _offsetZ, _m44); + } + + public override int GetHashCode() + { + // Perform field-by-field XOR of HashCodes + return M11.GetHashCode() ^ + M12.GetHashCode() ^ + M13.GetHashCode() ^ + M14.GetHashCode() ^ + M21.GetHashCode() ^ + M22.GetHashCode() ^ + M23.GetHashCode() ^ + M24.GetHashCode() ^ + M31.GetHashCode() ^ + M32.GetHashCode() ^ + M33.GetHashCode() ^ + M34.GetHashCode() ^ + OffsetX.GetHashCode() ^ + OffsetY.GetHashCode() ^ + OffsetZ.GetHashCode() ^ + M44.GetHashCode(); + } + + public override bool Equals(object o) + { + return o is Matrix3D && Matrix3D.Equals(this, (Matrix3D)o); + } + + public bool Equals(Matrix3D value) + { + return Matrix3D.Equals(this, value); + } + + public static bool operator ==(Matrix3D matrix1, Matrix3D matrix2) + { + return matrix1.M11 == matrix2.M11 && + matrix1.M12 == matrix2.M12 && + matrix1.M13 == matrix2.M13 && + matrix1.M14 == matrix2.M14 && + matrix1.M21 == matrix2.M21 && + matrix1.M22 == matrix2.M22 && + matrix1.M23 == matrix2.M23 && + matrix1.M24 == matrix2.M24 && + matrix1.M31 == matrix2.M31 && + matrix1.M32 == matrix2.M32 && + matrix1.M33 == matrix2.M33 && + matrix1.M34 == matrix2.M34 && + matrix1.OffsetX == matrix2.OffsetX && + matrix1.OffsetY == matrix2.OffsetY && + matrix1.OffsetZ == matrix2.OffsetZ && + matrix1.M44 == matrix2.M44; + } + + public static bool operator !=(Matrix3D matrix1, Matrix3D matrix2) + { + return !(matrix1 == matrix2); + } + + public static Matrix3D operator *(Matrix3D matrix1, Matrix3D matrix2) + { + Matrix3D matrix3D = default; + + matrix3D.M11 = matrix1.M11 * matrix2.M11 + + matrix1.M12 * matrix2.M21 + + matrix1.M13 * matrix2.M31 + + matrix1.M14 * matrix2.OffsetX; + matrix3D.M12 = matrix1.M11 * matrix2.M12 + + matrix1.M12 * matrix2.M22 + + matrix1.M13 * matrix2.M32 + + matrix1.M14 * matrix2.OffsetY; + matrix3D.M13 = matrix1.M11 * matrix2.M13 + + matrix1.M12 * matrix2.M23 + + matrix1.M13 * matrix2.M33 + + matrix1.M14 * matrix2.OffsetZ; + matrix3D.M14 = matrix1.M11 * matrix2.M14 + + matrix1.M12 * matrix2.M24 + + matrix1.M13 * matrix2.M34 + + matrix1.M14 * matrix2.M44; + matrix3D.M21 = matrix1.M21 * matrix2.M11 + + matrix1.M22 * matrix2.M21 + + matrix1.M23 * matrix2.M31 + + matrix1.M24 * matrix2.OffsetX; + matrix3D.M22 = matrix1.M21 * matrix2.M12 + + matrix1.M22 * matrix2.M22 + + matrix1.M23 * matrix2.M32 + + matrix1.M24 * matrix2.OffsetY; + matrix3D.M23 = matrix1.M21 * matrix2.M13 + + matrix1.M22 * matrix2.M23 + + matrix1.M23 * matrix2.M33 + + matrix1.M24 * matrix2.OffsetZ; + matrix3D.M24 = matrix1.M21 * matrix2.M14 + + matrix1.M22 * matrix2.M24 + + matrix1.M23 * matrix2.M34 + + matrix1.M24 * matrix2.M44; + matrix3D.M31 = matrix1.M31 * matrix2.M11 + + matrix1.M32 * matrix2.M21 + + matrix1.M33 * matrix2.M31 + + matrix1.M34 * matrix2.OffsetX; + matrix3D.M32 = matrix1.M31 * matrix2.M12 + + matrix1.M32 * matrix2.M22 + + matrix1.M33 * matrix2.M32 + + matrix1.M34 * matrix2.OffsetY; + matrix3D.M33 = matrix1.M31 * matrix2.M13 + + matrix1.M32 * matrix2.M23 + + matrix1.M33 * matrix2.M33 + + matrix1.M34 * matrix2.OffsetZ; + matrix3D.M34 = matrix1.M31 * matrix2.M14 + + matrix1.M32 * matrix2.M24 + + matrix1.M33 * matrix2.M34 + + matrix1.M34 * matrix2.M44; + matrix3D.OffsetX = matrix1.OffsetX * matrix2.M11 + + matrix1.OffsetY * matrix2.M21 + + matrix1.OffsetZ * matrix2.M31 + + matrix1.M44 * matrix2.OffsetX; + matrix3D.OffsetY = matrix1.OffsetX * matrix2.M12 + + matrix1.OffsetY * matrix2.M22 + + matrix1.OffsetZ * matrix2.M32 + + matrix1.M44 * matrix2.OffsetY; + matrix3D.OffsetZ = matrix1.OffsetX * matrix2.M13 + + matrix1.OffsetY * matrix2.M23 + + matrix1.OffsetZ * matrix2.M33 + + matrix1.M44 * matrix2.OffsetZ; + matrix3D.M44 = matrix1.OffsetX * matrix2.M14 + + matrix1.OffsetY * matrix2.M24 + + matrix1.OffsetZ * matrix2.M34 + + matrix1.M44 * matrix2.M44; + + // matrix3D._type is not set. + + return matrix3D; + } + + public bool HasInverse + { + get + { + return !IsZero(Determinant); + } + } + + public void Invert() + { + if (!InvertCore()) + { + throw new InvalidOperationException(); + } + } + + private static Matrix3D CreateIdentity() + { + Matrix3D matrix3D = default; + matrix3D.SetMatrix(1, 0, 0, 0, + 0, 1, 0, 0, + 0, 0, 1, 0, + 0, 0, 0, 1); + return matrix3D; + } + + private void SetMatrix(double m11, double m12, double m13, double m14, + double m21, double m22, double m23, double m24, + double m31, double m32, double m33, double m34, + double offsetX, double offsetY, double offsetZ, double m44) + { + _m11 = m11; + _m12 = m12; + _m13 = m13; + _m14 = m14; + _m21 = m21; + _m22 = m22; + _m23 = m23; + _m24 = m24; + _m31 = m31; + _m32 = m32; + _m33 = m33; + _m34 = m34; + _offsetX = offsetX; + _offsetY = offsetY; + _offsetZ = offsetZ; + _m44 = m44; + } + + private static bool Equals(Matrix3D matrix1, Matrix3D matrix2) + { + return matrix1.M11.Equals(matrix2.M11) && + matrix1.M12.Equals(matrix2.M12) && + matrix1.M13.Equals(matrix2.M13) && + matrix1.M14.Equals(matrix2.M14) && + matrix1.M21.Equals(matrix2.M21) && + matrix1.M22.Equals(matrix2.M22) && + matrix1.M23.Equals(matrix2.M23) && + matrix1.M24.Equals(matrix2.M24) && + matrix1.M31.Equals(matrix2.M31) && + matrix1.M32.Equals(matrix2.M32) && + matrix1.M33.Equals(matrix2.M33) && + matrix1.M34.Equals(matrix2.M34) && + matrix1.OffsetX.Equals(matrix2.OffsetX) && + matrix1.OffsetY.Equals(matrix2.OffsetY) && + matrix1.OffsetZ.Equals(matrix2.OffsetZ) && + matrix1.M44.Equals(matrix2.M44); + } + + private double GetNormalizedAffineDeterminant() + { + double z20 = _m12 * _m23 - _m22 * _m13; + double z10 = _m32 * _m13 - _m12 * _m33; + double z00 = _m22 * _m33 - _m32 * _m23; + + return _m31 * z20 + _m21 * z10 + _m11 * z00; + } + + private bool IsAffine + { + get + { + return (_m14 == 0.0 && _m24 == 0.0 && _m34 == 0.0 && _m44 == 1.0); + } + } + + private double Determinant + { + get + { + if (IsAffine) + { + return GetNormalizedAffineDeterminant(); + } + + // compute all six 2x2 determinants of 2nd two columns + double y01 = _m13 * _m24 - _m23 * _m14; + double y02 = _m13 * _m34 - _m33 * _m14; + double y03 = _m13 * _m44 - _offsetZ * _m14; + double y12 = _m23 * _m34 - _m33 * _m24; + double y13 = _m23 * _m44 - _offsetZ * _m24; + double y23 = _m33 * _m44 - _offsetZ * _m34; + + // Compute 3x3 cofactors for 1st the column + double z30 = _m22 * y02 - _m32 * y01 - _m12 * y12; + double z20 = _m12 * y13 - _m22 * y03 + _offsetY * y01; + double z10 = _m32 * y03 - _offsetY * y02 - _m12 * y23; + double z00 = _m22 * y23 - _m32 * y13 + _offsetY * y12; + + return _offsetX * z30 + _m31 * z20 + _m21 * z10 + _m11 * z00; + } + } + + private static bool IsZero(double value) + { + return Math.Abs(value) < 10.0 * DBL_EPSILON_RELATIVE_1; + } + + private const double DBL_EPSILON_RELATIVE_1 = 1.1102230246251567e-016; /* smallest such that 1.0+DBL_EPSILON != 1.0 */ + + private double _m11; + private double _m12; + private double _m13; + private double _m14; + private double _m21; + private double _m22; + private double _m23; + private double _m24; + private double _m31; + private double _m32; + private double _m33; + private double _m34; + private double _offsetX; + private double _offsetY; + private double _offsetZ; + private double _m44; + } +} + +namespace ABI.Windows.UI.Xaml.Media.Media3D +{ +#if EMBED + internal +#else + public +#endif + static class Matrix3D + { + public static string GetGuidSignature() => + $"struct(Windows.UI.Xaml.Media.Media3D.Matrix3D;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8;f8)"; + } +} diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.cs new file mode 100644 index 000000000..92a20c4d3 --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml.Media/Windows.UI.Xaml.Media.cs @@ -0,0 +1,266 @@ + +namespace Windows.UI.Xaml.Media +{ + using global::Windows.Foundation; + + [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.Media.Matrix))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif + [StructLayout(LayoutKind.Sequential)] +#if EMBED + internal +#else + public +#endif + struct Matrix : IFormattable + { + public Matrix(double m11, double m12, + double m21, double m22, + double offsetX, double offsetY) + { + _m11 = m11; + _m12 = m12; + _m21 = m21; + _m22 = m22; + _offsetX = offsetX; + _offsetY = offsetY; + } + + // the transform is identity by default + private static Matrix s_identity = CreateIdentity(); + + public double M11 + { + get + { + return _m11; + } + set + { + _m11 = value; + } + } + + public double M12 + { + get + { + return _m12; + } + set + { + _m12 = value; + } + } + + public double M21 + { + get + { + return _m21; + } + set + { + _m21 = value; + } + } + + public double M22 + { + get + { + return _m22; + } + set + { + _m22 = value; + } + } + + public double OffsetX + { + get + { + return _offsetX; + } + set + { + _offsetX = value; + } + } + + public double OffsetY + { + get + { + return _offsetY; + } + set + { + _offsetY = value; + } + } + + public static Matrix Identity + { + get + { + return s_identity; + } + } + + public bool IsIdentity + { + get + { + return (_m11 == 1 && _m12 == 0 && _m21 == 0 && _m22 == 1 && _offsetX == 0 && _offsetY == 0); + } + } + + public override string ToString() + { + // Delegate to the internal method which implements all ToString calls. + return ConvertToString(null /* format string */, null /* format provider */); + } + + public string ToString(IFormatProvider provider) + { + // Delegate to the internal method which implements all ToString calls. + return ConvertToString(null /* format string */, provider); + } + + string IFormattable.ToString(string format, IFormatProvider provider) + { + // Delegate to the internal method which implements all ToString calls. + return ConvertToString(format, provider); + } + + private string ConvertToString(string format, IFormatProvider provider) + { + if (IsIdentity) + { + return "Identity"; + } + + // Helper to get the numeric list separator for a given culture. + char separator = TokenizerHelper.GetNumericListSeparator(provider); + return string.Format(provider, + "{1:" + format + "}{0}{2:" + format + "}{0}{3:" + format + "}{0}{4:" + format + "}{0}{5:" + format + "}{0}{6:" + format + "}", + separator, + _m11, + _m12, + _m21, + _m22, + _offsetX, + _offsetY); + } + + public Point Transform(Point point) + { + float x = (float)point.X; + float y = (float)point.Y; + this.MultiplyPoint(ref x, ref y); + Point point2 = new Point(x, y); + return point2; + } + + public override int GetHashCode() + { + // Perform field-by-field XOR of HashCodes + return M11.GetHashCode() ^ + M12.GetHashCode() ^ + M21.GetHashCode() ^ + M22.GetHashCode() ^ + OffsetX.GetHashCode() ^ + OffsetY.GetHashCode(); + } + + public override bool Equals(object o) + { + return o is Matrix && Matrix.Equals(this, (Matrix)o); + } + + public bool Equals(Matrix value) + { + return Matrix.Equals(this, value); + } + + public static bool operator ==(Matrix matrix1, Matrix matrix2) + { + return matrix1.M11 == matrix2.M11 && + matrix1.M12 == matrix2.M12 && + matrix1.M21 == matrix2.M21 && + matrix1.M22 == matrix2.M22 && + matrix1.OffsetX == matrix2.OffsetX && + matrix1.OffsetY == matrix2.OffsetY; + } + + public static bool operator !=(Matrix matrix1, Matrix matrix2) + { + return !(matrix1 == matrix2); + } + + private static Matrix CreateIdentity() + { + Matrix matrix = default; + matrix.SetMatrix(1, 0, + 0, 1, + 0, 0); + return matrix; + } + + private void SetMatrix(double m11, double m12, + double m21, double m22, + double offsetX, double offsetY) + { + _m11 = m11; + _m12 = m12; + _m21 = m21; + _m22 = m22; + _offsetX = offsetX; + _offsetY = offsetY; + } + + private void MultiplyPoint(ref float x, ref float y) + { + double num = (y * _m21) + _offsetX; + double num2 = (x * _m12) + _offsetY; + x *= (float)_m11; + x += (float)num; + y *= (float)_m22; + y += (float)num2; + } + + private static bool Equals(Matrix matrix1, Matrix matrix2) + { + return matrix1.M11.Equals(matrix2.M11) && + matrix1.M12.Equals(matrix2.M12) && + matrix1.M21.Equals(matrix2.M21) && + matrix1.M22.Equals(matrix2.M22) && + matrix1.OffsetX.Equals(matrix2.OffsetX) && + matrix1.OffsetY.Equals(matrix2.OffsetY); + } + + private double _m11; + private double _m12; + private double _m21; + private double _m22; + private double _offsetX; + private double _offsetY; + } +} + +namespace ABI.Windows.UI.Xaml.Media +{ +#if EMBED + internal +#else + public +#endif + static class Matrix + { + public static string GetGuidSignature() => $"struct(Windows.UI.Xaml.Media.Matrix;f8;f8;f8;f8;f8;f8)"; + } +} diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.SR.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.SR.cs new file mode 100644 index 000000000..cbb4596bb --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.SR.cs @@ -0,0 +1,17 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Windows.UI.Xaml +{ + static class SR + { + public static string DirectUI_CornerRadius_InvalidMember = "Invalid value for {0} property on CornerRadius."; + public static string DirectUI_InvalidArgument = "Invalid argument."; + public static string ElementNotAvailable_Default = "The element is not available."; + public static string ElementNotEnabled_Default = "The element is not enabled."; + public static string XamlParse_Default = "XAML parsing failed."; + public static string LayoutCycle_Default = "A cycle occurred while laying out the GUI."; + public static string PlatformNotSupported_WindowsRuntime = "Windows Runtime (WinRT) is not supported on this platform."; + } +} diff --git a/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.cs b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.cs new file mode 100644 index 000000000..3fb1fabeb --- /dev/null +++ b/src/cswinrt/strings/additions/Windows.UI.Xaml/Windows.UI.Xaml.cs @@ -0,0 +1,792 @@ + +namespace Windows.UI.Xaml +{ + using global::Windows.Foundation; + + [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.CornerRadius))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif + [StructLayout(LayoutKind.Sequential)] +#if EMBED + internal +#else + public +#endif + struct CornerRadius + { + private double _TopLeft; + private double _TopRight; + private double _BottomRight; + private double _BottomLeft; + + public CornerRadius(double uniformRadius) + { + Validate(uniformRadius, uniformRadius, uniformRadius, uniformRadius); + _TopLeft = _TopRight = _BottomRight = _BottomLeft = uniformRadius; + } + + public CornerRadius(double topLeft, double topRight, double bottomRight, double bottomLeft) + { + Validate(topLeft, topRight, bottomRight, bottomLeft); + + _TopLeft = topLeft; + _TopRight = topRight; + _BottomRight = bottomRight; + _BottomLeft = bottomLeft; + } + + private static void Validate(double topLeft, double topRight, double bottomRight, double bottomLeft) + { + if (topLeft < 0.0 || double.IsNaN(topLeft)) + throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "TopLeft")); + + if (topRight < 0.0 || double.IsNaN(topRight)) + throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "TopRight")); + + if (bottomRight < 0.0 || double.IsNaN(bottomRight)) + throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "BottomRight")); + + if (bottomLeft < 0.0 || double.IsNaN(bottomLeft)) + throw new ArgumentException(string.Format(SR.DirectUI_CornerRadius_InvalidMember, "BottomLeft")); + } + + public override string ToString() + { + return ToString(global::System.Globalization.CultureInfo.InvariantCulture); + } + + internal string ToString(global::System.Globalization.CultureInfo cultureInfo) + { + char listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo); + + // Initial capacity [64] is an estimate based on a sum of: + // 48 = 4x double (twelve digits is generous for the range of values likely) + // 8 = 4x Unit Type string (approx two characters) + // 4 = 4x separator characters + global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(64); + + sb.Append(InternalToString(_TopLeft, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(_TopRight, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(_BottomRight, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(_BottomLeft, cultureInfo)); + return sb.ToString(); + } + + internal string InternalToString(double l, global::System.Globalization.CultureInfo cultureInfo) + { + if (double.IsNaN(l)) return "Auto"; + return Convert.ToString(l, cultureInfo); + } + + public override bool Equals(object obj) + { + if (obj is CornerRadius) + { + CornerRadius otherObj = (CornerRadius)obj; + return (this == otherObj); + } + return (false); + } + + public bool Equals(CornerRadius cornerRadius) + { + return (this == cornerRadius); + } + + public override int GetHashCode() + { + return _TopLeft.GetHashCode() ^ _TopRight.GetHashCode() ^ _BottomLeft.GetHashCode() ^ _BottomRight.GetHashCode(); + } + + public static bool operator ==(CornerRadius cr1, CornerRadius cr2) + { + return cr1._TopLeft == cr2._TopLeft && cr1._TopRight == cr2._TopRight && cr1._BottomRight == cr2._BottomRight && cr1._BottomLeft == cr2._BottomLeft; + } + + public static bool operator !=(CornerRadius cr1, CornerRadius cr2) + { + return (!(cr1 == cr2)); + } + + public double TopLeft + { + get { return _TopLeft; } + set + { + Validate(value, 0, 0, 0); + _TopLeft = value; + } + } + + public double TopRight + { + get { return _TopRight; } + set + { + Validate(0, value, 0, 0); + _TopRight = value; + } + } + + public double BottomRight + { + get { return _BottomRight; } + set + { + Validate(0, 0, value, 0); + _BottomRight = value; + } + } + + public double BottomLeft + { + get { return _BottomLeft; } + set + { + Validate(0, 0, 0, value); + _BottomLeft = value; + } + } + } + + [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.EnumTypeDetails))] +#endif +#if EMBED + internal +#else + public +#endif + enum GridUnitType + { + Auto = 0, + Pixel, + Star, + } + + [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.GridLength))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif + [StructLayout(LayoutKind.Sequential)] +#if EMBED + internal +#else + public +#endif + struct GridLength + { + private readonly double _unitValue; + private readonly GridUnitType _unitType; + + private const double Default = 1.0; + private static readonly GridLength s_auto = new GridLength(Default, GridUnitType.Auto); + + public GridLength(double pixels) + : this(pixels, GridUnitType.Pixel) + { + } + + internal static bool IsFinite(double value) + { + return !(double.IsNaN(value) || double.IsInfinity(value)); + } + + public GridLength(double value, GridUnitType type) + { + if (!IsFinite(value) || value < 0.0) + { + throw new ArgumentException(SR.DirectUI_InvalidArgument, nameof(value)); + } + if (type != GridUnitType.Auto && type != GridUnitType.Pixel && type != GridUnitType.Star) + { + throw new ArgumentException(SR.DirectUI_InvalidArgument, nameof(type)); + } + + _unitValue = (type == GridUnitType.Auto) ? Default : value; + _unitType = type; + } + + + public double Value { get { return ((_unitType == GridUnitType.Auto) ? s_auto._unitValue : _unitValue); } } + public GridUnitType GridUnitType { get { return (_unitType); } } + + + public bool IsAbsolute { get { return (_unitType == GridUnitType.Pixel); } } + public bool IsAuto { get { return (_unitType == GridUnitType.Auto); } } + public bool IsStar { get { return (_unitType == GridUnitType.Star); } } + + public static GridLength Auto + { + get { return (s_auto); } + } + + + public static bool operator ==(GridLength gl1, GridLength gl2) + { + return (gl1.GridUnitType == gl2.GridUnitType + && gl1.Value == gl2.Value); + } + + public static bool operator !=(GridLength gl1, GridLength gl2) + { + return (gl1.GridUnitType != gl2.GridUnitType + || gl1.Value != gl2.Value); + } + + public override bool Equals(object oCompare) + { + if (oCompare is GridLength) + { + GridLength l = (GridLength)oCompare; + return (this == l); + } + else + return false; + } + + public bool Equals(GridLength gridLength) + { + return (this == gridLength); + } + + public override int GetHashCode() + { + return ((int)_unitValue + (int)_unitType); + } + + public override string ToString() + { + return this.ToString(global::System.Globalization.CultureInfo.InvariantCulture); + } + + internal string ToString(global::System.Globalization.CultureInfo cultureInfo) + { + char listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo); + + // Initial capacity [64] is an estimate based on a sum of: + // 12 = 1x double (twelve digits is generous for the range of values likely) + // 8 = 4x Unit Type string (approx two characters) + // 2 = 2x separator characters + + if (_unitType == GridUnitType.Auto) + { + return "Auto"; + } + else if (_unitType == GridUnitType.Pixel) + { + return Convert.ToString(_unitValue, cultureInfo); + } + else + { + return Convert.ToString(_unitValue, cultureInfo) + "*"; + } + } + } + + [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.Thickness))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif + [StructLayout(LayoutKind.Sequential)] +#if EMBED + internal +#else + public +#endif + struct Thickness + { + private double _Left; + private double _Top; + private double _Right; + private double _Bottom; + + public Thickness(double uniformLength) + { + _Left = _Top = _Right = _Bottom = uniformLength; + } + + public Thickness(double left, double top, double right, double bottom) + { + _Left = left; + _Top = top; + _Right = right; + _Bottom = bottom; + } + + public double Left + { + get { return _Left; } + set { _Left = value; } + } + + public double Top + { + get { return _Top; } + set { _Top = value; } + } + + public double Right + { + get { return _Right; } + set { _Right = value; } + } + + public double Bottom + { + get { return _Bottom; } + set { _Bottom = value; } + } + + public override string ToString() + { + return ToString(global::System.Globalization.CultureInfo.InvariantCulture); + } + + internal string ToString(global::System.Globalization.CultureInfo cultureInfo) + { + char listSeparator = TokenizerHelper.GetNumericListSeparator(cultureInfo); + + // Initial capacity [64] is an estimate based on a sum of: + // 48 = 4x double (twelve digits is generous for the range of values likely) + // 8 = 4x Unit Type string (approx two characters) + // 4 = 4x separator characters + global::System.Text.StringBuilder sb = new global::System.Text.StringBuilder(64); + + sb.Append(InternalToString(_Left, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(_Top, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(_Right, cultureInfo)); + sb.Append(listSeparator); + sb.Append(InternalToString(_Bottom, cultureInfo)); + return sb.ToString(); + } + + internal string InternalToString(double l, global::System.Globalization.CultureInfo cultureInfo) + { + if (double.IsNaN(l)) return "Auto"; + return Convert.ToString(l, cultureInfo); + } + + public override bool Equals(object obj) + { + if (obj is Thickness) + { + Thickness otherObj = (Thickness)obj; + return (this == otherObj); + } + return (false); + } + + public bool Equals(Thickness thickness) + { + return (this == thickness); + } + + public override int GetHashCode() + { + return _Left.GetHashCode() ^ _Top.GetHashCode() ^ _Right.GetHashCode() ^ _Bottom.GetHashCode(); + } + + public static bool operator ==(Thickness t1, Thickness t2) + { + return t1._Left == t2._Left && t1._Top == t2._Top && t1._Right == t2._Right && t1._Bottom == t2._Bottom; + } + + public static bool operator !=(Thickness t1, Thickness t2) + { + return (!(t1 == t2)); + } + } + + [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.EnumTypeDetails))] +#endif +#if EMBED + internal +#else + public +#endif + enum DurationType + { + Automatic, + TimeSpan, + Forever + } + + [global::WinRT.WindowsRuntimeType("Windows.UI.Xaml")] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Xaml.Duration))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif + [StructLayout(LayoutKind.Sequential)] +#if EMBED + internal +#else + public +#endif + struct Duration + { + private readonly TimeSpan _timeSpan; + private DurationType _durationType; + + public Duration(TimeSpan timeSpan) + { + _durationType = DurationType.TimeSpan; + _timeSpan = timeSpan; + } + + public static implicit operator Duration(TimeSpan timeSpan) + { + return new Duration(timeSpan); + } + + public static Duration operator +(Duration t1, Duration t2) + { + if (t1.HasTimeSpan && t2.HasTimeSpan) + { + return new Duration(t1._timeSpan + t2._timeSpan); + } + else if (t1._durationType != DurationType.Automatic && t2._durationType != DurationType.Automatic) + { + return Duration.Forever; + } + else + { + // Automatic + anything is Automatic + return Duration.Automatic; + } + } + + public static Duration operator -(Duration t1, Duration t2) + { + if (t1.HasTimeSpan && t2.HasTimeSpan) + { + return new Duration(t1._timeSpan - t2._timeSpan); + } + else if (t1._durationType == DurationType.Forever && t2.HasTimeSpan) + { + return Duration.Forever; + } + else + { + return Duration.Automatic; + } + } + + public static bool operator ==(Duration t1, Duration t2) + { + return t1.Equals(t2); + } + + public static bool operator !=(Duration t1, Duration t2) + { + return !(t1.Equals(t2)); + } + + public static bool operator >(Duration t1, Duration t2) + { + if (t1.HasTimeSpan && t2.HasTimeSpan) + { + return t1._timeSpan > t2._timeSpan; + } + else if (t1.HasTimeSpan && t2._durationType == DurationType.Forever) + { + return false; + } + else if (t1._durationType == DurationType.Forever && t2.HasTimeSpan) + { + return true; + } + else + { + return false; + } + } + + public static bool operator >=(Duration t1, Duration t2) + { + if (t1._durationType == DurationType.Automatic && t2._durationType == DurationType.Automatic) + { + return true; + } + else if (t1._durationType == DurationType.Automatic || t2._durationType == DurationType.Automatic) + { + return false; + } + else + { + return !(t1 < t2); + } + } + + public static bool operator <(Duration t1, Duration t2) + { + if (t1.HasTimeSpan && t2.HasTimeSpan) + { + return t1._timeSpan < t2._timeSpan; + } + else if (t1.HasTimeSpan && t2._durationType == DurationType.Forever) + { + return true; + } + else if (t1._durationType == DurationType.Forever && t2.HasTimeSpan) + { + return false; + } + else + { + return false; + } + } + + public static bool operator <=(Duration t1, Duration t2) + { + if (t1._durationType == DurationType.Automatic && t2._durationType == DurationType.Automatic) + { + return true; + } + else if (t1._durationType == DurationType.Automatic || t2._durationType == DurationType.Automatic) + { + return false; + } + else + { + return !(t1 > t2); + } + } + + public static int Compare(Duration t1, Duration t2) + { + if (t1._durationType == DurationType.Automatic) + { + if (t2._durationType == DurationType.Automatic) + { + return 0; + } + else + { + return -1; + } + } + else if (t2._durationType == DurationType.Automatic) + { + return 1; + } + else + { + if (t1 < t2) + { + return -1; + } + else if (t1 > t2) + { + return 1; + } + else + { + return 0; + } + } + } + + public static Duration operator +(Duration duration) + { + return duration; + } + + public bool HasTimeSpan + { + get + { + return _durationType == DurationType.TimeSpan; + } + } + + public static Duration Automatic + { + get + { + Duration duration = default; + duration._durationType = DurationType.Automatic; + + return duration; + } + } + + public static Duration Forever + { + get + { + Duration duration = default; + duration._durationType = DurationType.Forever; + + return duration; + } + } + + public TimeSpan TimeSpan + { + get + { + if (HasTimeSpan) + { + return _timeSpan; + } + else + { + throw new InvalidOperationException(); + } + } + } + + public Duration Add(Duration duration) + { + return this + duration; + } + + public override bool Equals(object value) + { + return value is Duration && Equals((Duration)value); + } + + public bool Equals(Duration duration) + { + if (HasTimeSpan) + { + if (duration.HasTimeSpan) + { + return _timeSpan == duration._timeSpan; + } + else + { + return false; + } + } + else + { + return _durationType == duration._durationType; + } + } + + public static bool Equals(Duration t1, Duration t2) + { + return t1.Equals(t2); + } + + public override int GetHashCode() + { + if (HasTimeSpan) + { + return _timeSpan.GetHashCode(); + } + else + { + return _durationType.GetHashCode() + 17; + } + } + + public Duration Subtract(Duration duration) + { + return this - duration; + } + + public override string ToString() + { + if (HasTimeSpan) + { + return _timeSpan.ToString(); // "00"; //TypeDescriptor.GetConverter(_timeSpan).ConvertToString(_timeSpan); + } + else if (_durationType == DurationType.Forever) + { + return "Forever"; + } + else // IsAutomatic + { + return "Automatic"; + } + } + } +} + +namespace ABI.Windows.UI.Xaml +{ +#if EMBED + internal +#else + public +#endif + static class CornerRadius + { + public static string GetGuidSignature() => $"struct(Windows.UI.Xaml.CornerRadius;f8;f8;f8;f8)"; + } + +#if EMBED + internal +#else + public +#endif + static class Duration + { + public static string GetGuidSignature() + { + string timeSpanSignature = global::WinRT.GuidGenerator.GetSignature(typeof(global::System.TimeSpan)); + string durationTypeSignature = global::WinRT.GuidGenerator.GetSignature(typeof(global::Windows.UI.Xaml.DurationType)); + return $"struct(Windows.UI.Xaml.Duration;{timeSpanSignature};{durationTypeSignature})"; + } + } + +#if EMBED + internal +#else + public +#endif + static class DurationType + { + public static string GetGuidSignature() => "enum(Windows.UI.Xaml.DurationType;i4)"; + } + +#if EMBED + internal +#else + public +#endif + static class GridLength + { + public static string GetGuidSignature() + { + string unitTypeSignature = global::WinRT.GuidGenerator.GetSignature(typeof(global::Windows.UI.Xaml.GridUnitType)); + return $"struct(Windows.UI.Xaml.GridLength;f8;{unitTypeSignature})"; + } + } + +#if EMBED + internal +#else + public +#endif + static class GridUnitType + { + public static string GetGuidSignature() => "enum(Windows.UI.Xaml.GridUnitType;i4)"; + } + +#if EMBED + internal +#else + public +#endif + static class Thickness + { + public static string GetGuidSignature() => $"struct(Windows.UI.Xaml.Thickness;f8;f8;f8;f8)"; + } +} diff --git a/src/cswinrt/strings/additions/Windows.UI/Windows.UI.cs b/src/cswinrt/strings/additions/Windows.UI/Windows.UI.cs index 001598028..5987fac9a 100644 --- a/src/cswinrt/strings/additions/Windows.UI/Windows.UI.cs +++ b/src/cswinrt/strings/additions/Windows.UI/Windows.UI.cs @@ -5,7 +5,10 @@ namespace Windows.UI using global::System.Globalization; [global::WinRT.WindowsRuntimeType("Windows.Foundation.UniversalApiContract")] - [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Color))] + [global::WinRT.WindowsRuntimeHelperType(typeof(global::ABI.Windows.UI.Color))] +#if NET + [global::WinRT.WinRTExposedType(typeof(global::WinRT.StructTypeDetails))] +#endif [StructLayout(LayoutKind.Sequential)] #if EMBED internal diff --git a/src/cswinrt/text_writer.h b/src/cswinrt/text_writer.h index ce7fee38a..56b7ed5f0 100644 --- a/src/cswinrt/text_writer.h +++ b/src/cswinrt/text_writer.h @@ -126,7 +126,7 @@ namespace cswinrt template void write_printf(char const* format, Args const&... args) { - char buffer[128]; + char buffer[256]; size_t const size = sprintf_s(buffer, format, args...); write(std::string_view{ buffer, size }); } @@ -153,6 +153,14 @@ namespace cswinrt m_second.clear(); } + void flush_to_console_error() noexcept + { + fprintf(stderr, "%.*s", static_cast(m_first.size()), m_first.data()); + fprintf(stderr, "%.*s", static_cast(m_second.size()), m_second.data()); + m_first.clear(); + m_second.clear(); + } + void flush_to_file(std::string const& filename) { if (!file_equal(filename)) diff --git a/src/cswinrt/type_writers.h b/src/cswinrt/type_writers.h index 83b66b4ac..ad9d4b6cf 100644 --- a/src/cswinrt/type_writers.h +++ b/src/cswinrt/type_writers.h @@ -47,6 +47,7 @@ using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.Linq.Expressions; using System.Reflection; using System.Runtime.CompilerServices; diff --git a/src/get_testwinrt.cmd b/src/get_testwinrt.cmd index 8ab31a505..7f0d3ef4f 100644 --- a/src/get_testwinrt.cmd +++ b/src/get_testwinrt.cmd @@ -14,9 +14,9 @@ git checkout -f master if ErrorLevel 1 popd & exit /b !ErrorLevel! git fetch -f if ErrorLevel 1 popd & exit /b !ErrorLevel! -git reset -q --hard 1bdde092ca6031f86b659212db1fa00bb1d5c9af +git reset -q --hard aa4edcd52542cfe036473d008d3f66c8a220d59d if ErrorLevel 1 popd & exit /b !ErrorLevel! echo Restoring Nuget %this_dir%.nuget\nuget.exe restore popd -exit /b 0 \ No newline at end of file +exit /b 0