diff --git a/.config/1espt/PipelineAutobaseliningConfig.yml b/.config/1espt/PipelineAutobaseliningConfig.yml new file mode 100644 index 000000000..4b9969c37 --- /dev/null +++ b/.config/1espt/PipelineAutobaseliningConfig.yml @@ -0,0 +1,14 @@ +## 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: + 98194: + retail: + source: + credscan: + lastModifiedDate: 2023-12-06 + eslint: + lastModifiedDate: 2023-12-06 + psscriptanalyzer: + lastModifiedDate: 2023-12-06 + armory: + lastModifiedDate: 2023-12-06 diff --git a/.config/tsaoptions.json b/.config/tsaoptions.json new file mode 100644 index 000000000..38fe35171 --- /dev/null +++ b/.config/tsaoptions.json @@ -0,0 +1,8 @@ +{ + "instanceUrl": "https://microsoft.visualstudio.com", + "projectName": "os", + "areaPath": "OS\\Windows Client and Services\\WinPD\\DEEP-Developer Experience, Ecosystem and Partnerships\\AmUse- App Metadata and User Setup Experience\\Projections\\Cppwinrt", + "notificationAliases": [ "cpp4uwpt@microsoft.com" ], + "ignoreBranchName": true, + "codebaseName": "cppwinrt" +} diff --git a/.gdn/.gdnsettings b/.gdn/.gdnsettings new file mode 100644 index 000000000..156408362 --- /dev/null +++ b/.gdn/.gdnsettings @@ -0,0 +1,7 @@ +{ + "files": { }, + "folders": { }, + "overwriteLogs": true, + "telemetryFlushTimeout": 10, + "variables": { } +} \ No newline at end of file diff --git a/.gdn/.gitignore b/.gdn/.gitignore new file mode 100644 index 000000000..decb1055f --- /dev/null +++ b/.gdn/.gitignore @@ -0,0 +1,11 @@ +## Ignore Guardian internal files +.r/ +rc/ +rs/ +i/ +p/ +c/ +o/ + +## Ignore Guardian Local settings +LocalSettings.gdn.json \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 000000000..748763028 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,54 @@ +name: Bug report +description: File a bug report about cppwinrt +title: "Bug:" +labels: [bug] +assignees: [] +body: + - type: markdown + attributes: + value: "Thank you for filing a bug report! 🐛" + - type: input + attributes: + label: Version + description: What is the version of cppwinrt you're using? + placeholder: You can find the exact version by running `cppwinrt.exe`. + - type: textarea + attributes: + label: Summary + description: > + Please provide a short summary of the bug, along with any information + you feel relevant to replicating the bug. + - type: textarea + attributes: + label: Reproducible example + description: > + Please provide all code needed to reproduce the issue. + placeholder: | + #include "winrt/Windows.Foundation.h" + + using namespace winrt; + using namespace Windows::Foundation; + + int main() + { + Uri uri(L"https://kennykerr.ca"); + printf("%ls\n", uri.ToString().c_str()); + } + render: cpp + - type: textarea + attributes: + label: Expected behavior + description: "I expected to see this happen:" + - type: textarea + attributes: + label: Actual behavior + description: "Instead, this happened:" + - type: textarea + attributes: + label: Additional comments + description: Is there anything else you'd like to share? + validations: + required: false + - type: markdown + attributes: + value: "Thank you! ©️➕➕" diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 000000000..d32d2339d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Visual Studio or C++ question + url: https://developercommunity.visualstudio.com/cpp + about: Your open channel to Microsoft engineering teams + - name: Windows API question + url: https://docs.microsoft.com/en-us/answers/topics/windows-api.html + about: Please ask questions about the Windows API on Microsoft Q&A diff --git a/.github/actions/setup-llvm-mingw/action.yml b/.github/actions/setup-llvm-mingw/action.yml new file mode 100644 index 000000000..d36b48a75 --- /dev/null +++ b/.github/actions/setup-llvm-mingw/action.yml @@ -0,0 +1,79 @@ +name: 'Set up llvm-mingw toolchain' +description: 'Set up llvm-mingw toolchain' +inputs: + llvm-mingw-version: + description: 'llvm-mingw version' + required: true + default: '20220906' + host-arch: + description: 'llvm-mingw toolchain host architecture (e.g. i686, x86_64)' + required: true + default: 'x86_64' +outputs: + llvm-path: + description: "The path in which llvm-mingw is installed to" + value: ${{ ((runner.os == 'Windows') && steps.setup-llvm.outputs.llvm-path) || steps.setup-llvm-linux.outputs.llvm-path }} +runs: + using: "composite" + steps: + - name: Cache llvm-mingw (Windows) + id: cache-llvm + if: runner.os == 'Windows' + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: .llvm-mingw + key: llvm-mingw-${{ runner.os }}-${{ inputs.llvm-mingw-version }}-${{ inputs.host-arch }} + + - name: Install llvm-mingw ${{ inputs.llvm-mingw-version }} (Windows ${{ inputs.host-arch }}) + if: runner.os == 'Windows' && steps.cache-llvm.outputs.cache-hit != 'true' + shell: pwsh + run: | + $llvm_mingw_version = "${{ inputs.llvm-mingw-version }}" + $llvm_arch = "${{ inputs.host-arch }}" + Invoke-WebRequest "https://github.com/mstorsjo/llvm-mingw/releases/download/${llvm_mingw_version}/llvm-mingw-${llvm_mingw_version}-ucrt-${llvm_arch}.zip" -OutFile llvm-mingw.zip + 7z x llvm-mingw.zip -o"$pwd\.llvm-mingw" + rm llvm-mingw.zip + + - name: Set up llvm-mingw (Windows) + id: setup-llvm + if: runner.os == 'Windows' + shell: pwsh + run: | + $llvm_mingw_version = "${{ inputs.llvm-mingw-version }}" + $llvm_arch = "${{ inputs.host-arch }}" + if (!(Test-Path "$pwd\.llvm-mingw\llvm-mingw-${llvm_mingw_version}-ucrt-${llvm_arch}\bin\clang++.exe")) { return 1 } + Add-Content $env:GITHUB_OUTPUT "llvm-path=$pwd\.llvm-mingw\llvm-mingw-${llvm_mingw_version}-ucrt-${llvm_arch}" + Add-Content $env:GITHUB_PATH "$pwd\.llvm-mingw\llvm-mingw-${llvm_mingw_version}-ucrt-${llvm_arch}\bin" + # for the ASAN runtime DLL: + Add-Content $env:GITHUB_PATH "$pwd\.llvm-mingw\llvm-mingw-${llvm_mingw_version}-ucrt-${llvm_arch}\${llvm_arch}-w64-mingw32\bin" + + - name: Cache llvm-mingw (Linux) + id: cache-llvm-linux + if: runner.os == 'Linux' + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: /opt/llvm-mingw + key: llvm-mingw-${{ runner.os }}-${{ inputs.llvm-mingw-version }}-${{ inputs.host-arch }} + + - name: Install llvm-mingw ${{ inputs.llvm-mingw-version }} (Linux ${{ inputs.host-arch }}) + if: runner.os == 'Linux' && steps.cache-llvm-linux.outputs.cache-hit != 'true' + shell: bash + run: | + llvm_mingw_version="${{ inputs.llvm-mingw-version }}" + llvm_arch="${{ inputs.host-arch }}" + mkdir -p /opt/llvm-mingw + cd /opt/llvm-mingw + curl -L https://github.com/mstorsjo/llvm-mingw/releases/download/20220906/llvm-mingw-${llvm_mingw_version}-ucrt-ubuntu-18.04-${llvm_arch}.tar.xz | tar xJ --strip-components=1 + + - name: Set up llvm-mingw (Linux) + id: setup-llvm-linux + if: runner.os == 'Linux' + shell: bash + run: | + cd /opt/llvm-mingw + if [ ! -x bin/clang++ ]; then + echo "$PWD/bin/clang++ not found or not executable!" + exit 1 + fi + echo "llvm-path=$PWD/llvm-mingw" >> $GITHUB_OUTPUT + echo "$PWD/bin" >> $GITHUB_PATH diff --git a/.github/actions/setup-llvm-msvc/action.yml b/.github/actions/setup-llvm-msvc/action.yml new file mode 100644 index 000000000..e5a696a9f --- /dev/null +++ b/.github/actions/setup-llvm-msvc/action.yml @@ -0,0 +1,48 @@ +name: 'Set up LLVM for MSVC' +description: 'Set up upstream LLVM for targeting MSVC ABI' +inputs: + llvm-version: + description: 'LLVM version' + required: true + default: '17.0.5' +outputs: + llvm-path: + description: "The path in which LLVM is installed to" + value: ${{ steps.setup-llvm.outputs.llvm-path }} +runs: + using: "composite" + steps: + - name: Cache LLVM and tools + id: cache-llvm + uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 + with: + path: | + .LLVM + .llvm-utils + key: llvm-msvc-${{ runner.os }}-${{ inputs.llvm-version }} + + - name: Install LLVM ${{ inputs.llvm-version }} + if: steps.cache-llvm.outputs.cache-hit != 'true' + shell: pwsh + run: | + Invoke-WebRequest "https://github.com/llvm/llvm-project/releases/download/llvmorg-${{ inputs.llvm-version }}/LLVM-${{ inputs.llvm-version }}-win64.exe" -OutFile LLVM-installer.exe + .\LLVM-installer.exe /S "/D=$pwd\.LLVM" | Out-Null + rm LLVM-installer.exe + + # Not using the LLVM tools that comes with MSVC. + - name: Download LLVM build tools for msbuild + if: steps.cache-llvm.outputs.cache-hit != 'true' + shell: pwsh + run: | + Invoke-WebRequest "https://github.com/zufuliu/llvm-utils/releases/download/v22.09/LLVM_VS2017.zip" -OutFile LLVM_VS2017.zip + 7z x -y "LLVM_VS2017.zip" -o"$pwd\.llvm-utils\" + rm LLVM_VS2017.zip + + - name: Set up LLVM build tools for msbuild + id: setup-llvm + shell: pwsh + run: | + if (!(Test-Path "$pwd\.LLVM\bin\clang-cl.exe")) { exit 1 } + Add-Content $env:GITHUB_PATH "$pwd\.LLVM\bin" + Add-Content $env:GITHUB_OUTPUT "llvm-path=$pwd\.LLVM" + cmd /c ".llvm-utils\LLVM_VS2017\install.bat" 1 diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 000000000..9557baa7a --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,12 @@ +version: 2 +updates: + - package-ecosystem: "cargo" + directory: "/" + schedule: + interval: "daily" + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + cooldown: + default-days: 7 diff --git a/.github/instructions/cppwinrt.instructions.md b/.github/instructions/cppwinrt.instructions.md new file mode 100644 index 000000000..eae2fc3c6 --- /dev/null +++ b/.github/instructions/cppwinrt.instructions.md @@ -0,0 +1,58 @@ +# C++/WinRT Codebase — Agent Instructions + +## Repository Structure + +- `cppwinrt/` — The cppwinrt.exe code generator (C++ source) + - `main.cpp` — CLI parsing, namespace iteration, SCC detection, .ixx orchestration + - `file_writers.h` — All file generation functions (headers, .ixx modules, component stubs) + - `code_writers.h` — Code-level writing utilities (guards, namespace wrappers, type writers) + - `type_writers.h` — Type formatting (ABI signatures, names, GUIDs) + - `component_writers.h` — Component authoring code generation + - `helpers.h` — Metadata reading helpers + - `settings.h` — Global settings populated from CLI args + - `text_writer.h` — Core text writer infrastructure +- `strings/` — String literal `.h` files embedded by the prebuild step. Changes require: delete prebuild.exe → rebuild solution +- `nuget/` — MSBuild targets, props, and NuGet packaging + - `Microsoft.Windows.CppWinRT.targets` — Main MSBuild integration (projections, module support) +- `test/` — Test projects + - `test/test_cpp20_module/` — Standalone module test (in main solution) + - `test/nuget/` — NuGet integration tests (multi-project module chain) +- `docs/` — Documentation +- `natvis/` — Visual Studio debug visualizer (includes strings/*.h in its pch.h — add new files there too) + +## Build Process + +- Use VS Developer Shell for correct toolset environment +- `cmake --build build --config Release --target cppwinrt` for cppwinrt.exe (or MSBuild: `msbuild cppwinrt\cppwinrt.vcxproj /p:Configuration=Release /p:Platform=x64`) +- NuGet tests: `msbuild test\nuget\NuGetTest.sln /p:Configuration=Release /p:Platform=x64` +- Module test projects require v145 toolset (VS 2026). Directory.Build.Props selects v145 when VisualStudioVersion >= 18.0 (VS 2026) and falls back to v143 otherwise; override `` in the Configuration PropertyGroup to force a specific toolset + +## Key Patterns + +### Prebuild Embedding +The `strings/*.h` files are embedded as string literals by the prebuild step. If you modify any `strings/*.h` file, you must delete `prebuild.exe` and rebuild the entire solution for changes to take effect. + +### Module Guard Macros +- `WINRT_IMPL_BUILD_MODULE` — Defined in .ixx global fragment. Makes `WINRT_EXPORT` expand to `export extern "C++"` and suppresses `#include` of dependencies +- `WINRT_IMPORT_MODULE` — Defined by consumers who import modules. Makes namespace headers and base.h no-op (types come from module import) +- `WINRT_EXPORT` — Empty in header mode, `export extern "C++"` in module mode. Defined in `winrt/base_macros.h` +- `WINRT_IMPL_STD_EXPORT` — Empty in header mode, `extern "C++"` (without export) in module mode. Used for `namespace std` specializations + +### Generated Header Structure +Each namespace produces four header files: +- `impl/.0.h` — Forward declarations, ABIs, GUIDs, categories +- `impl/.1.h` — Interface definitions +- `impl/.2.h` — Delegates, structs, class implementations +- `.h` — Public API surface (consume definitions, class wrappers, operators) + +### Dependency Collection +When generating headers with `-modules`, writer.depends is inspected after each header to build a namespace dependency graph. This graph drives SCC detection and module import lists. + +## Common Gotchas + +- Module IFCs are NOT compatible across toolset versions — always clean rebuild when switching +- PCH and modules can coexist but PCH should NOT include winrt headers when using modules +- `/ifcSearchDir` works for the module dependency scanner to find IFCs, but cross-component modules may need explicit `/reference "name=path.ifc"` flags +- `import std;` requires `BuildStlModules=true` +- `strings/base_macros.h` is the single source of truth for shared macros (generated as `winrt/base_macros.h`). New macros go in `base_macros.h` only +- When adding, removing, or heavily refactoring `strings/*.h` files, always rebuild the natvis project (`natvis/cppwinrtvisualizer.sln`) to verify — it includes strings/*.h directly in its pch.h diff --git a/.github/instructions/modules.instructions.md b/.github/instructions/modules.instructions.md new file mode 100644 index 000000000..3deee2955 --- /dev/null +++ b/.github/instructions/modules.instructions.md @@ -0,0 +1,40 @@ +# C++/WinRT Modules — Agent Instructions + +## Module Architecture (v2 — Per-Namespace) + +Each WinRT namespace gets its own C++20 named module (`winrt.`). Base infrastructure is in `winrt_base` and `winrt_numerics`. + +### Code Generator Flow + +1. `-modules` flag enables .ixx generation in cppwinrt.exe +2. `-module_include`/`-module_exclude` filter which namespaces get modules +3. Headers are generated with dependency tracking (deps_ptr parameter) +4. Tarjan's SCC algorithm detects cyclic namespace groups +5. Standalone namespaces get individual .ixx; cyclic groups get consolidated SCC owner + re-export stubs + +### MSBuild Flow + +1. `CppWinRTBuildModule=true` adds `-modules` to cppwinrt.exe invocations +2. `CppWinRTAddModuleInterfaces` discovers `$(GeneratedFilesDir)winrt\*.ixx` and adds to ClCompile +3. `CppWinRTConsumeModule` metadata on ProjectReference controls per-reference IFC sharing +4. `CppWinRTResolveModuleReferences` calls `CppWinRTGetModuleOutputs` on tagged references +5. Platform projection suppresses `-modules` when consuming pre-built IFCs + +### Critical Invariants + +- Module guards are unconditional in codegen — `-modules` controls .ixx generation and component codegen (module.g.cpp, stub .cpp) +- SCC owner is alphabetically first namespace in the cycle +- All .ixx filenames use `winrt` prefix: `winrt.Windows.Foundation.ixx`, `winrt_base.ixx` +- Shared macros live in `strings/base_module.h` → generates `winrt/macros.h`. `base_macros.h` includes it via `#include "winrt/macros.h"` + +### Testing Changes + +After modifying cppwinrt.exe code: +1. Rebuild cppwinrt.exe: `msbuild cppwinrt\cppwinrt.vcxproj /p:Configuration=Release /p:Platform=x64` +2. Run standalone test: build `test_cpp20_module` in main solution +3. Run NuGet tests: `msbuild test\nuget\NuGetTest.sln /p:Configuration=Release /p:Platform=x64` + +After modifying targets: +1. Clean NuGet test obj dirs +2. Build with `/v:normal` and check "Module providers:" diagnostic messages +3. Inspect `.rsp` files in `obj/` to verify correct `-modules` flag placement diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 000000000..88999e247 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,3 @@ +What's this all about? + +Fixes: #0000 ⬅️ Be sure to refer to an existing issue here! diff --git a/.github/workflows/check-line-endings.yml b/.github/workflows/check-line-endings.yml new file mode 100644 index 000000000..3891aec09 --- /dev/null +++ b/.github/workflows/check-line-endings.yml @@ -0,0 +1,32 @@ +name: Check Line Endings + +on: + pull_request: + push: + branches: + - master + +jobs: + check-line-endings: + name: Enforce .gitattributes line endings + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Check for line ending violations + run: | + # Re-normalize all files according to .gitattributes + git add --renormalize . + + # Check if renormalization changed anything + if git diff --cached --name-only | grep -q .; then + echo "::error::The following files have line endings that don't match .gitattributes settings:" + git diff --cached --name-only + echo "" + echo "To fix, run:" + echo " git add --renormalize ." + echo " git commit -m 'Normalize line endings'" + exit 1 + fi + + echo "All files have correct line endings." diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..5e2677fed --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,405 @@ +name: CI Tests + +on: + pull_request: + push: + branches: + - master + +jobs: + test-msvc-cppwinrt-build: + name: '${{ matrix.compiler }}: Build (${{ matrix.arch }}, ${{ matrix.config }}, ${{ matrix.toolchain.platform_toolset }})' + strategy: + matrix: + compiler: [MSVC, clang-cl] + arch: [x86, x64, arm64] + config: [Debug, Release] + toolchain: + - image: windows-2025-vs2026 + platform_toolset: v145 + exclude: + - arch: arm64 + config: Debug + - compiler: clang-cl + arch: arm64 + - compiler: clang-cl + config: Release + runs-on: ${{ matrix.toolchain.image }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Download nuget + run: | + mkdir ".\.nuget" + Invoke-WebRequest "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile ".\.nuget\nuget.exe" + + - name: Find VsDevCmd.bat + run: | + $VSDevCmd = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere" -latest -find Common7\tools\VSDevCmd.bat + if (!$VSDevCmd) { exit 1 } + echo "Using VSDevCmd: ${VSDevCmd}" + Add-Content $env:GITHUB_ENV "VSDevCmd=$VSDevCmd" + + - name: Prepare build flags + run: | + $target_configuration = "${{ matrix.config }}" + $target_platform = "${{ matrix.arch }}" + $target_version = "999.999.999.999" + $props = "Configuration=$target_configuration,Platform=$target_platform,CppWinRTBuildVersion=$target_version" + if ("${{ matrix.compiler }}" -eq "clang-cl") { + $props += ",Clang=1,PlatformToolset=ClangCl" + } else { + $props += ",PlatformToolset=${{ matrix.toolchain.platform_toolset }}" + } + Add-Content $env:GITHUB_ENV "msbuild_config_props=/p:$props" + + - name: Restore nuget packages + run: | + cmd /c "$env:VSDevCmd" "&" nuget restore cppwinrt.sln + + - name: Build fast_fwd + run: | + cmd /c "$env:VSDevCmd" "&" msbuild /m /clp:ForceConsoleColor "$env:msbuild_config_props" cppwinrt.sln /t:fast_fwd + + - name: Build x86 prebuild tool + if: matrix.arch == 'arm64' + run: | + cmd /c "$env:VSDevCmd" "&" msbuild /m /clp:ForceConsoleColor "$env:msbuild_config_props" /p:Platform=x86 cppwinrt.sln /t:cppwinrt + + - name: Build cppwinrt + run: | + cmd /c "$env:VSDevCmd" "&" msbuild /m /clp:ForceConsoleColor "$env:msbuild_config_props" cppwinrt.sln /t:cppwinrt + + - name: Upload built executables + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: msvc-build-${{ matrix.compiler}}-${{ matrix.arch }}-${{ matrix.config }}-${{ matrix.toolchain.platform_toolset }}-bin + path: | + _build/${{ matrix.arch }}/${{ matrix.config }}/*.exe + _build/${{ matrix.arch }}/${{ matrix.config }}/*.dll + _build/${{ matrix.arch }}/${{ matrix.config }}/*.winmd + _build/${{ matrix.arch }}/${{ matrix.config }}/*.lib + _build/${{ matrix.arch }}/${{ matrix.config }}/*.pdb + + - name: Run cppwinrt to build projection + if: matrix.arch != 'arm64' + run: | + $target_configuration = "${{ matrix.config }}" + $target_platform = "${{ matrix.arch }}" + & "_build\$target_platform\$target_configuration\cppwinrt.exe" -in local -out _build\$target_platform\$target_configuration -verbose + + test-msvc-cppwinrt-test: + name: '${{ matrix.compiler }}: Test [${{ matrix.test_exe }}] (${{ matrix.arch }}, ${{ matrix.config }}, ${{ matrix.toolchain.platform_toolset }})' + needs: test-msvc-cppwinrt-build + strategy: + fail-fast: false + matrix: + compiler: [MSVC, clang-cl] + arch: [x86, x64, arm64] + config: [Debug, Release] + test_exe: [test, test_nocoro, test_cpp20, test_cpp20_no_sourcelocation, test_cpp20_module, test_fast, test_slow, test_old, test_module_lock_custom, test_module_lock_none] + toolchain: + - image: windows-2025-vs2026 + platform_toolset: v145 + exclude: + - arch: arm64 + config: Debug + - compiler: clang-cl + arch: arm64 + - compiler: clang-cl + config: Release + # C++20 named modules require the MSVC v145 toolset; clang-cl is not supported. + - compiler: clang-cl + test_exe: test_cpp20_module + runs-on: ${{ matrix.toolchain.image }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Fetch cppwinrt executables + if: matrix.arch != 'arm64' + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: msvc-build-${{ matrix.compiler}}-${{ matrix.arch }}-${{ matrix.config }}-${{ matrix.toolchain.platform_toolset }}-bin + path: _build/${{ matrix.arch }}/${{ matrix.config }}/ + + - name: Fetch x86 cppwinrt executables (arm64 only) + if: matrix.arch == 'arm64' + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: msvc-build-${{ matrix.compiler}}-x86-Release-${{ matrix.toolchain.platform_toolset }}-bin + path: _build/x86/Release/ + + - name: Download nuget + run: | + mkdir ".\.nuget" + Invoke-WebRequest "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile ".\.nuget\nuget.exe" + + - name: Find VsDevCmd.bat + run: | + $VSDevCmd = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere" -latest -find Common7\tools\VSDevCmd.bat + if (!$VSDevCmd) { exit 1 } + echo "Using VSDevCmd: ${VSDevCmd}" + Add-Content $env:GITHUB_ENV "VSDevCmd=$VSDevCmd" + + - name: Prepare build flags + run: | + $target_configuration = "${{ matrix.config }}" + $target_platform = "${{ matrix.arch }}" + $target_version = "999.999.999.999" + $props = "Configuration=$target_configuration,Platform=$target_platform,CppWinRTBuildVersion=$target_version" + if ("${{ matrix.compiler }}" -eq "clang-cl") { + $props += ",Clang=1,PlatformToolset=ClangCl" + } else { + $props += ",PlatformToolset=${{ matrix.toolchain.platform_toolset }}" + } + Add-Content $env:GITHUB_ENV "msbuild_config_props=/p:$props" + + - name: Restore nuget packages + run: | + cmd /c "$env:VSDevCmd" "&" nuget restore cppwinrt.sln + + - name: Remove cppwinrt dependency from all test projects + run: | + # HACK: We already have a built exe, so we want to avoid rebuilding cppwinrt + mv cppwinrt.sln cppwinrt.sln.orig + Get-Content cppwinrt.sln.orig | + Where-Object { -not $_.Contains("{D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4}") } | + Set-Content cppwinrt.sln + + - name: Patch catch.hpp to make it build with ANSI colour + run: | + # HACK: Remove include and the isatty call in catch.hpp to make ANSI colour build work + mv test/catch.hpp test/catch.hpp.orig + Get-Content test/catch.hpp.orig | + Where-Object { -not $_.Contains("#include ") } | + ForEach-Object { + $_.Replace("isatty(STDOUT_FILENO)", "false") + } | + Set-Content test/catch.hpp + + - name: Run cppwinrt to build projection + run: | + $target_configuration = "${{ matrix.config }}" + $target_platform = "${{ matrix.arch }}" + $cppwinrt_path = "_build\$target_platform\$target_configuration\cppwinrt.exe" + if ($target_platform -eq "arm64") { + $cppwinrt_path = "_build\x86\Release\cppwinrt.exe" + } + & $cppwinrt_path -in local -out _build\$target_platform\$target_configuration -verbose + + - name: Build test '${{ matrix.test_exe }}' + run: | + $test_proj = "${{ matrix.test_exe }}" + if ($test_proj -eq "test_old") { + $test_proj = "old_tests\test_old" + } + + cmd /c "$env:VSDevCmd" "&" msbuild /m /clp:ForceConsoleColor /p:TestsUseAnsiColor=1 "$env:msbuild_config_props" cppwinrt.sln /t:test\$test_proj + + - name: Run tests in '${{ matrix.test_exe }}' one by one + if: matrix.compiler == 'clang-cl' + run: | + $target_configuration = "${{ matrix.config }}" + $target_platform = "${{ matrix.arch }}" + $test_exe = "${{ matrix.test_exe }}" + $test_path = "_build\$target_platform\$target_configuration\$test_exe.exe" + if (!(Test-Path $test_path)) { + echo "::error::Test $test_exe is missing." + exit 1 + } + $test_names = @( & $test_path --list-test-names-only ) + $failed_tests = 0 + foreach ($test_name in $test_names) { + echo "::group::Running test $test_name" + $escaped = $test_name.Replace("\", "\\").Replace(",", "\,").Replace("[", "\[") + & $test_path --use-colour yes --warn NoTests "$escaped" + echo "::endgroup::" + if ($LastExitCode -ne 0) { + $failed_tests += 1 + # Add a newline in case test exited abnormally without newline + echo "" + echo "::error::Test $test_exe/'$test_name' failed with exit code $LastExitCode!" + } + } + if ($failed_tests -eq 0) { + echo "All tests in $test_exe passed." + } else { + echo "$failed_tests failed in $test_exe." + exit 1 + } + + - name: Run test '${{ matrix.test_exe }}' + if: matrix.arch != 'arm64' + run: | + $target_configuration = "${{ matrix.config }}" + $target_platform = "${{ matrix.arch }}" + $test_exe = "${{ matrix.test_exe }}" + $test_path = "_build\$target_platform\$target_configuration\$test_exe.exe" + if (!(Test-Path $test_path)) { + echo "::error::Test $test_exe is missing." + exit 1 + } + & $test_path --use-colour yes + + - name: Upload arm64 test executables + if: matrix.arch == 'arm64' + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: msvc-tests-${{ matrix.test_exe }}-${{ matrix.compiler }}-${{ matrix.arch }}-${{ matrix.config }}-${{ matrix.toolchain.platform_toolset }}-bin + path: | + _build/${{ matrix.arch }}/${{ matrix.config }}/*.exe + _build/${{ matrix.arch }}/${{ matrix.config }}/*.dll + _build/${{ matrix.arch }}/${{ matrix.config }}/*.winmd + _build/${{ matrix.arch }}/${{ matrix.config }}/*.lib + _build/${{ matrix.arch }}/${{ matrix.config }}/*.pdb + + build-linux-cross-cppwinrt: + name: 'cross: Cross-build from Linux' + strategy: + matrix: + arch: [i686, x86_64] + runs-on: ubuntu-22.04 + env: + CMAKE_COLOR_DIAGNOSTICS: 1 + CLICOLOR_FORCE: 1 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Install cross compiler + run: | + arch=${{ matrix.arch }} + sudo apt-get install g++-mingw-w64-${arch/_/-} + sudo update-alternatives --set "${{ matrix.arch }}-w64-mingw32-gcc" "/usr/bin/${{ matrix.arch }}-w64-mingw32-gcc-posix" + sudo update-alternatives --set "${{ matrix.arch }}-w64-mingw32-g++" "/usr/bin/${{ matrix.arch }}-w64-mingw32-g++-posix" + + - name: Cross-build cppwinrt + run: | + cmake -S . -B build/cross_x64/ --toolchain cross-mingw-toolchain.cmake \ + -DCMAKE_SYSTEM_PROCESSOR=${{ matrix.arch }} \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ + -DCMAKE_CXX_FLAGS="-static" \ + -DCMAKE_INSTALL_PREFIX=$PWD/install/ + cmake --build build/cross_x64/ --target install -j2 + + - name: Upload cppwinrt.exe + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: cross-build-${{ matrix.arch }}-bin + path: install/bin/cppwinrt.exe + + build-msvc-natvis: + name: 'Build natvis' + strategy: + matrix: + arch: [x86, x64, arm64] + config: [Release] + Deployment: [Component, Standalone] + runs-on: windows-2025-vs2026 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Download nuget + run: | + mkdir ".\.nuget" + Invoke-WebRequest "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile ".\.nuget\nuget.exe" + + - name: Find VsDevCmd.bat + run: | + $VSDevCmd = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere" -latest -find Common7\tools\VSDevCmd.bat + if (!$VSDevCmd) { exit 1 } + echo "Using VSDevCmd: ${VSDevCmd}" + Add-Content $env:GITHUB_ENV "VSDevCmd=$VSDevCmd" + + - name: Prepare build flags + run: | + $target_configuration = "${{ matrix.config }}" + $target_platform = "${{ matrix.arch }}" + $target_version = "999.999.999.999" + Add-Content $env:GITHUB_ENV "msbuild_config_props=/p:Configuration=$target_configuration,Platform=$target_platform,CppWinRTBuildVersion=$target_version,PlatformToolset=v145" + + - name: Restore nuget packages + run: | + cmd /c "$env:VSDevCmd" "&" nuget restore natvis\cppwinrtvisualizer.sln + + - name: Build natvis + run: | + cmd /c "$env:VSDevCmd" "&" msbuild /m /clp:ForceConsoleColor "$env:msbuild_config_props" /p:Deployment=${{ matrix.Deployment }} natvis\cppwinrtvisualizer.sln + + build-msvc-nuget-test: + name: 'Build nuget test (${{ matrix.arch }}, ${{ matrix.toolchain.platform_toolset }})' + needs: test-msvc-cppwinrt-build + strategy: + matrix: + compiler: + - MSVC + arch: [x86, x64] + config: [Release] + toolchain: + - image: windows-2025-vs2026 + platform_toolset: v145 + runs-on: ${{ matrix.toolchain.image }} + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Fetch cppwinrt executables + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + name: msvc-build-${{ matrix.compiler}}-${{ matrix.arch }}-${{ matrix.config }}-${{ matrix.toolchain.platform_toolset }}-bin + path: _build/${{ matrix.arch }}/${{ matrix.config }}/ + + - name: Download nuget + run: | + mkdir ".\.nuget" + Invoke-WebRequest "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe" -OutFile ".\.nuget\nuget.exe" + + - name: Find VsDevCmd.bat + run: | + $VSDevCmd = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere" -latest -find Common7\tools\VSDevCmd.bat + if (!$VSDevCmd) { exit 1 } + echo "Using VSDevCmd: ${VSDevCmd}" + Add-Content $env:GITHUB_ENV "VSDevCmd=$VSDevCmd" + + - name: Prepare build flags + run: | + $target_configuration = "${{ matrix.config }}" + $target_platform = "${{ matrix.arch }}" + $target_version = "999.999.999.999" + Add-Content $env:GITHUB_ENV "msbuild_config_props=/p:Configuration=$target_configuration,Platform=$target_platform,CppWinRTBuildVersion=$target_version,PlatformToolset=${{ matrix.toolchain.platform_toolset }}" + + - name: Restore nuget packages + run: | + cmd /c "$env:VSDevCmd" "&" nuget restore test\nuget\NugetTest.sln + + - name: Run cppwinrt to build projection + run: | + $target_configuration = "${{ matrix.config }}" + $target_platform = "${{ matrix.arch }}" + & "_build\$target_platform\$target_configuration\cppwinrt.exe" -in local -out _build\$target_platform\$target_configuration -verbose + + - name: Run nuget test + run: | + cmd /c "$env:VSDevCmd" "&" msbuild /m /clp:ForceConsoleColor "$env:msbuild_config_props" test\nuget\NugetTest.sln + + build-nuget: + name: Build nuget package with MSVC + runs-on: windows-2025-vs2026 + steps: + - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + + - name: Package + run: | + $VSDevCmd = & "${env:ProgramFiles(x86)}\Microsoft Visual Studio\Installer\vswhere" -latest -find Common7\tools\VSDevCmd.bat + if (!$VSDevCmd) { exit 1 } + echo "Using VSDevCmd: ${VSDevCmd}" + cmd /c "${VSDevCmd}" "&" nuget.exe restore cppwinrt.sln + cmd /c "${VSDevCmd}" "&" build_nuget.cmd + if (!(Test-Path "*.nupkg")) { + echo "::error::Output nuget package not found!" + exit 1 + } + + - name: Upload nuget package artifact + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: package + path: "*.nupkg" diff --git a/.gitignore b/.gitignore index 4a1238c64..9493fdad5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,12 @@ *.c *.nupkg test*.xml +test*_results.txt +test_failures.txt build packages Debug Release Generated Files obj +vsix/LICENSE diff --git a/.pipelines/OneBranch.Official.yml b/.pipelines/OneBranch.Official.yml new file mode 100644 index 000000000..158d3bf4e --- /dev/null +++ b/.pipelines/OneBranch.Official.yml @@ -0,0 +1,157 @@ +parameters: # parameters are shown up in ADO UI in a build queue time +- name: 'debug' + displayName: 'Enable debug output' + type: boolean + default: false + +variables: +- template: variables/version.yml + parameters: + OfficialBuild: true +- template: variables/OneBranchVariables.yml + parameters: + debug: ${{ parameters.debug }} + +name: 3.0.$(date:yyMMdd)$(rev:.r) + +trigger: none + +resources: + repositories: + - repository: templates + type: git + name: OneBranch.Pipelines/GovernedTemplates + ref: refs/heads/main + +extends: + template: v2/Microsoft.Official.yml@templates + parameters: + platform: + name: 'windows_undocked' + product: 'build_tools' + + featureFlags: + WindowsHostVersion: + Version: 2022 + + cloudvault: + enabled: false + + globalSdl: + isNativeCode: true + asyncSdl: + enabled: true + tsa: + enabled: true + codeql: + compiled: + enabled: true + tsaEnabled: true + + stages: + - stage: build + jobs: + - template: .pipelines/jobs/OneBranchBuild.yml@self + parameters: + BuildConfiguration: $(BuildConfiguration) + BuildVersion: $(BuildVersion) + OfficialBuild: true + + - stage: vpack + dependsOn: build + jobs: + - job: Compiler_vpack + pool: + type: windows + variables: + ob_outputDirectory: '$(Build.SourcesDirectory)\out' + ob_createvpack_enabled: true + ob_createvpack_packagename: CppWinRT.Compiler + ob_createvpack_owneralias: cpp4uwpt + ob_createvpack_description: C++/WinRT Compiler + ob_createvpack_provData: true + ob_createvpack_versionAs: parts + ob_createvpack_majorVer: $(MajorVersion) + ob_createvpack_minorVer: $(MinorVersion) + ob_createvpack_patchVer: $(PatchVersion) + ob_createvpack_metadata: $(Build.SourceBranchName).x86.$(Build.BuildNumber).$(Build.SourceVersion) + ob_createvpack_target: $(OSBuildToolsRoot)\cppwinrt + + steps: + - task: UseDotNet@2 + continueOnError: true + inputs: + packageType: 'runtime' + version: '6.x' + performMultiLevelLookup: true + + - task: DownloadPipelineArtifact@2 + displayName: 'Download x86 artifacts' + inputs: + artifactName: 'drop_build_x86' + targetPath: '$(Build.SourcesDirectory)/x86' + + - task: DownloadPipelineArtifact@2 + displayName: 'Download x64 artifacts' + inputs: + artifactName: 'drop_build_x64' + targetPath: '$(Build.SourcesDirectory)/x64' + + - task: DownloadPipelineArtifact@2 + displayName: 'Download arm64 artifacts' + inputs: + artifactName: 'drop_build_arm64' + targetPath: '$(Build.SourcesDirectory)/arm64' + + - task: CopyFiles@2 + displayName: 'Stage compiler vpack contents' + inputs: + SourceFolder: $(Build.SourcesDirectory)/x86/cppwinrt + Contents: | + cppwinrt.exe + cppwinrt.pdb + TargetFolder: $(ob_outputDirectory) + + - task: CmdLine@2 + displayName: 'Stage MSBuild vpack contents' + inputs: + script: | + set TargetDir=$(ob_outputDirectory) + cd %TargetDir% + + copy $(Build.SourcesDirectory)\vsix\Microsoft.Cpp.CppWinRT.props + copy $(Build.SourcesDirectory)\nuget\Microsoft.Windows.CppWinRT.props Microsoft.Cpp.CppWinRTEnabled.props + copy $(Build.SourcesDirectory)\nuget\Microsoft.Windows.CppWinRT.targets Microsoft.Cpp.CppWinRTEnabled.targets + copy $(Build.SourcesDirectory)\nuget\CppWinrtRules.Project.xml CppWinrtRules.Project.xml + echo d | xcopy $(Build.SourcesDirectory)\x86\cppwinrt_fast_forwarder.lib build\native\lib\i386 + echo d | xcopy $(Build.SourcesDirectory)\x86\cppwinrt_fast_forwarder.lib build\native\lib\Win32 + echo d | xcopy $(Build.SourcesDirectory)\x64\cppwinrt_fast_forwarder.lib build\native\lib\amd64 + echo d | xcopy $(Build.SourcesDirectory)\x64\cppwinrt_fast_forwarder.lib build\native\lib\x64 + echo d | xcopy $(Build.SourcesDirectory)\arm64\cppwinrt_fast_forwarder.lib build\native\lib\arm64 + + - stage: NuGet + dependsOn: build + jobs: + - template: .pipelines/jobs/OneBranchNuGet.yml@self + parameters: + BuildConfiguration: $(BuildConfiguration) + NugetPackageVersion: $(NugetPackageVersion) + OfficialBuild: true + + - stage: Test + dependsOn: build + jobs: + - template: .pipelines/jobs/OneBranchTest.yml@self + parameters: + BuildConfiguration: $(BuildConfiguration) + BuildVersion: $(BuildVersion) + + - stage: Vsix + dependsOn: NuGet + jobs: + - template: .pipelines/jobs/OneBranchVsix.yml@self + parameters: + BuildConfiguration: $(BuildConfiguration) + BuildVersion: $(BuildVersion) + NugetPackageVersion: $(NugetPackageVersion) + OfficialBuild: true diff --git a/.pipelines/OneBranch.PullRequest.yml b/.pipelines/OneBranch.PullRequest.yml new file mode 100644 index 000000000..d92dccaa2 --- /dev/null +++ b/.pipelines/OneBranch.PullRequest.yml @@ -0,0 +1,76 @@ +parameters: +- name: 'debug' + displayName: 'Enable debug output' + type: boolean + default: false + +variables: +- template: variables/version.yml +- template: variables/OneBranchVariables.yml + parameters: + debug: ${{ parameters.debug }} + +name: PullRequest_3.0.$(date:yyMMdd)$(rev:.r) + +trigger: none + +pool: + type: windows + +resources: + repositories: + - repository: templates + type: git + name: OneBranch.Pipelines/GovernedTemplates + ref: refs/heads/main + +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 + sbom: + enabled: true + + stages: + - stage: build + jobs: + - template: .pipelines/jobs/OneBranchBuild.yml@self + parameters: + BuildConfiguration: $(BuildConfiguration) + BuildVersion: $(BuildVersion) + + - stage: NuGet + dependsOn: build + jobs: + - template: .pipelines/jobs/OneBranchNuGet.yml@self + parameters: + BuildConfiguration: $(BuildConfiguration) + NugetPackageVersion: $(NugetPackageVersion) + + - stage: Test + dependsOn: build + jobs: + - template: .pipelines/jobs/OneBranchTest.yml@self + parameters: + BuildConfiguration: $(BuildConfiguration) + BuildVersion: $(BuildVersion) + + - stage: Vsix + dependsOn: NuGet + jobs: + - template: .pipelines/jobs/OneBranchVsix.yml@self + parameters: + BuildConfiguration: $(BuildConfiguration) + BuildVersion: $(BuildVersion) + NugetPackageVersion: $(NugetPackageVersion) diff --git a/.pipelines/build.yml b/.pipelines/build.yml new file mode 100644 index 000000000..ebe8ad041 --- /dev/null +++ b/.pipelines/build.yml @@ -0,0 +1,640 @@ +# 'Allow scripts to access the OAuth token' was selected in pipeline. Add the following YAML to any steps requiring access: +# env: +# MY_ACCESS_TOKEN: $(System.AccessToken) +# Variable 'MajorVersion' was defined in the Variables tab +# Variable 'MinorVersion' was defined in the Variables tab +trigger: + branches: + include: + - refs/heads/master + batch: True +schedules: +- cron: 0 18 * * * + branches: + include: + - refs/heads/master +name: $(MajorVersion).$(MinorVersion).$(date:yyMMdd)$(rev:.r) + +variables: + manualRelease: $[and(eq(variables['BuildConfiguration'], 'release'), in(variables['Build.Reason'], 'Manual'))] + +jobs: +- job: BuildBinaries + pool: + name: Azure Pipelines + vmImage: 'windows-2022' + demands: + - msbuild + strategy: + matrix: + x86: + buildPlatform: 'x86' + x64: + buildPlatform: 'x64' + arm64: + buildPlatform: 'arm64' + + steps: + - checkout: self + clean: true + persistCredentials: True + + - task: NuGetToolInstaller@1 + displayName: Use NuGet 6.0.2 + continueOnError: True + inputs: + versionSpec: 6.0.2 + + - task: NuGetCommand@2 + displayName: NuGet restore + inputs: + command: 'restore' + feedsToUse: config + nugetConfigPath: NuGet.config + + - task: CmdLine@2 + displayName: Build Tools + inputs: + script: | + if "%VSCMD_VER%"=="" ( + pushd c: + call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\Common7\Tools\VsDevCmd.bat" >nul 2>&1 + popd + ) + + build_test_all.cmd $(BuildPlatform) $(BuildConfiguration) $(Build.BuildNumber) true + failOnStderr: true + + - task: ComponentGovernanceComponentDetection@0 + displayName: Component Detection + condition: eq(variables['BuildPlatform'], 'x64') + + - task: PublishTestResults@2 + displayName: Publish Test Results + enabled: False + + - task: CopyFiles@2 + displayName: Stage cppwinrt.* + condition: and(eq(variables['BuildPlatform'], 'x86'), eq(variables.manualRelease, 'true')) + inputs: + SourceFolder: $(Build.SourcesDirectory)\_build\$(BuildPlatform)\$(BuildConfiguration) + Contents: | + cppwinrt.exe + cppwinrt.pdb + TargetFolder: $(Build.ArtifactStagingDirectory)\cppwinrt + + - task: CopyFiles@2 + displayName: Stage Component cppwinrtvisualizer.* + condition: and(or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'arm64')), eq(variables.manualRelease, 'true')) + inputs: + SourceFolder: $(Build.SourcesDirectory)\natvis\$(BuildPlatform)\$(BuildConfiguration)\Component + Contents: | + cppwinrtvisualizer.dll + cppwinrtvisualizer.pdb + cppwinrtvisualizer.vsdconfig + TargetFolder: $(Build.ArtifactStagingDirectory)\Component + + - task: CopyFiles@2 + displayName: Stage Standalone cppwinrtvisualizer.* + condition: and(or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'arm64')), eq(variables.manualRelease, 'true')) + inputs: + SourceFolder: $(Build.SourcesDirectory)\natvis\$(BuildPlatform)\$(BuildConfiguration)\Standalone + Contents: | + cppwinrtvisualizer.dll + cppwinrtvisualizer.pdb + cppwinrtvisualizer.vsdconfig + TargetFolder: $(Build.ArtifactStagingDirectory)\Standalone + + - task: CopyFiles@2 + displayName: Stage cppwinrt_fast_forwarder.lib + condition: eq(variables.manualRelease, 'true') + inputs: + SourceFolder: $(Build.SourcesDirectory)\_build\$(BuildPlatform)\$(BuildConfiguration) + Contents: cppwinrt_fast_forwarder.lib + TargetFolder: $(Build.ArtifactStagingDirectory) + + - task: ManifestGeneratorTask@0 + displayName: 'Manifest Generator ' + condition: and(eq(variables['BuildPlatform'], 'x86'), eq(variables.manualRelease, 'true')) + inputs: + BuildDropPath: $(Build.ArtifactStagingDirectory)\cppwinrt + + - task: PublishPipelineArtifact@0 + displayName: Publish Artifacts + condition: eq(variables.manualRelease, 'true') + inputs: + artifactName: $(BuildConfiguration)_$(BuildPlatform) + targetPath: $(Build.ArtifactStagingDirectory) + + - task: PublishSymbols@2 + displayName: Publish symbols + condition: eq(variables.manualRelease, 'true') + inputs: + SymbolsFolder: $(Build.ArtifactStagingDirectory) + SearchPattern: '**/*.pdb' + SymbolServerType: TeamServices + SymbolsProduct: CppWinRT + +- job: BuildInternal + displayName: Build Internal Packages (VPacks) + dependsOn: BuildBinaries + condition: and(succeeded(), ne(variables['SkipInternalPackages'], 'true'), eq(variables.manualRelease, 'true')) + pool: + name: Azure Pipelines + vmImage: 'windows-2022' + steps: + - checkout: self + clean: true + persistCredentials: True + + - task: PkgESSetupBuild@12 + displayName: Package ES - Setup Build + inputs: + branchVersionExcludeBranch: master + disableWorkspace: true + disableMsbuildVersion: true + disableBuildTools: true + + - task: DownloadPipelineArtifact@1 + displayName: Download x86 Artifacts + inputs: + artifactName: $(BuildConfiguration)_x86 + downloadPath: $(Build.SourcesDirectory)\x86 + + - task: DownloadPipelineArtifact@1 + displayName: Download x64 Artifacts + inputs: + artifactName: $(BuildConfiguration)_x64 + downloadPath: $(Build.SourcesDirectory)\x64 + + - task: DownloadPipelineArtifact@1 + displayName: Download arm64 Artifacts + inputs: + artifactName: $(BuildConfiguration)_arm64 + downloadPath: $(Build.SourcesDirectory)\arm64 + + - task: CmdLine@2 + displayName: Parse PatchVersion + inputs: + script: 'for /f "tokens=3,4 delims=." %%i in ("$(Build.BuildNumber)") do @echo ##vso[task.setvariable variable=PatchVersion;]%%i%%j ' + failOnStderr: true + + - task: CmdLine@2 + displayName: Copy compiler contents for internal signing + inputs: + script: | + md $(Build.SourcesDirectory)\x86\tempsign + echo Build Sources Directory: + dir $(Build.SourcesDirectory) + echo x86 + dir $(Build.SourcesDirectory)\x86 + echo cppwinrt + dir $(Build.SourcesDirectory)\x86\cppwinrt + xcopy $(Build.SourcesDirectory)\x86\cppwinrt\*.* $(Build.SourcesDirectory)\x86\tempsign /icefzy + + - task: EsrpCodeSigning@2 + displayName: Sign Compiler vPack for internal use + inputs: + ConnectedServiceName: bf601a97-455d-4977-b248-07b90c96eed9 + FolderPath: $(Build.SourcesDirectory)\x86\tempsign + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "KeyCode" : "CP-458204", + "OperationCode" : "SigntoolSign", + "Parameters" : { + "OpusName" : "Windows Build Tools Internal", + "OpusInfo" : "http://www.microsoft.com", + "FileDigest" : "/fd \"SHA256\"", + "PageHash" : "/NPH", + "TimeStamp" : "/tr \"http://rfc3161.gtm.corp.microsoft.com/TSS/HttpTspServer\" /td sha256" + }, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-458204", + "OperationCode" : "SigntoolVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + + - task: PkgESVPack@12 + displayName: 'Publish Compiler VPack ' + inputs: + serviceType: drop + versionAs: parts + sourceDirectory: $(Build.SourcesDirectory)\x86\tempsign + description: C++/WinRT Compiler + pushPkgName: CppWinRT.Compiler + target: $(OSBuildToolsRoot)\cppwinrt + provData: false + majorVer: $(MajorVersion) + minorVer: $(MinorVersion) + patchVer: $(PatchVersion) + prereleaseVer: $(Build.SourceBranchName).x86.$(BuildConfiguration).$(Build.BuildNumber).$(Build.SourceVersion) + signSbom: false + + - task: CmdLine@2 + displayName: Delete internal compiler copy + inputs: + script: | + rd $(Build.SourcesDirectory)\x86\tempsign /q /s + + - task: CopyFiles@2 + displayName: Stage CppWinRT.Compiler.man + inputs: + SourceFolder: $(XES_VPACKMANIFESTDIRECTORY) + Contents: $(XES_VPACKMANIFESTNAME) + TargetFolder: $(Build.ArtifactStagingDirectory) + + - task: CmdLine@2 + displayName: Stage MSBuild vpack + inputs: + script: "set TargetDir=$(Build.SourcesDirectory)\\msbuild\nrd /s /q %TargetDir% >nul 2>&1\nmd %TargetDir%\ncd %TargetDir%\n\ncopy $(Build.SourcesDirectory)\\vsix\\Microsoft.Cpp.CppWinRT.props\ncopy $(Build.SourcesDirectory)\\nuget\\Microsoft.Windows.CppWinRT.props Microsoft.Cpp.CppWinRTEnabled.props \ncopy $(Build.SourcesDirectory)\\nuget\\Microsoft.Windows.CppWinRT.targets Microsoft.Cpp.CppWinRTEnabled.targets\ncopy $(Build.SourcesDirectory)\\nuget\\CppWinrtRules.Project.xml CppWinrtRules.Project.xml\necho d | xcopy $(Build.SourcesDirectory)\\x86\\cppwinrt_fast_forwarder.lib build\\native\\lib\\i386\necho d | xcopy $(Build.SourcesDirectory)\\x86\\cppwinrt_fast_forwarder.lib build\\native\\lib\\Win32\necho d | xcopy $(Build.SourcesDirectory)\\x64\\cppwinrt_fast_forwarder.lib build\\native\\lib\\amd64\necho d | xcopy $(Build.SourcesDirectory)\\x64\\cppwinrt_fast_forwarder.lib build\\native\\lib\\x64\necho d | xcopy $(Build.SourcesDirectory)\\arm64\\cppwinrt_fast_forwarder.lib build\\native\\lib\\arm64\n" + failOnStderr: true + + - task: PkgESVPack@12 + displayName: Publish MSBuild VPack + inputs: + serviceType: drop + versionAs: parts + sourceDirectory: $(Build.SourcesDirectory)\msbuild + description: C++/WinRT MSBuild + pushPkgName: CppWinRT.MSBuild + target: $(OSBuildToolsRoot)\cppwinrt + provData: false + majorVer: $(MajorVersion) + minorVer: $(MinorVersion) + patchVer: $(PatchVersion) + prereleaseVer: $(Build.SourceBranchName).$(Build.BuildNumber).$(Build.SourceVersion) + signSbom: false + + - task: CopyFiles@2 + displayName: Stage CppWinRT.MSBuild.man + inputs: + SourceFolder: $(XES_VPACKMANIFESTDIRECTORY) + Contents: $(XES_VPACKMANIFESTNAME) + TargetFolder: $(Build.ArtifactStagingDirectory) + + - task: CmdLine@2 + displayName: Stage OSBuildTools.Manifest Update + enabled: False + inputs: + script: | + copy $(Build.SourcesDirectory)\src\package\cppwinrt\vpack\GitCheckin.json + copy $(Build.SourcesDirectory)\vpack\*.man OSBuildTools.Manifest.Update + type OSBuildTools.Manifest.Update + workingDirectory: $(Build.ArtifactStagingDirectory) + failOnStderr: true + + - task: PublishPipelineArtifact@0 + displayName: Publish Update Manifests + inputs: + artifactName: VPack + targetPath: $(Build.ArtifactStagingDirectory) + +- job: BuildExternal + displayName: Build External Packages (NuGet, VSIX) + cancelTimeoutInMinutes: 1 + dependsOn: BuildBinaries + condition: and(succeeded(), eq(variables.manualRelease, 'true')) + pool: + name: Azure Pipelines + vmImage: 'windows-2022' + steps: + - checkout: self + clean: true + persistCredentials: True + + - task: NuGetToolInstaller@1 + displayName: Use NuGet 6.0.2 + continueOnError: True + inputs: + versionSpec: 6.0.2 + + - task: NuGetCommand@2 + displayName: NuGet restore + inputs: + command: 'restore' + feedsToUse: config + nugetConfigPath: NuGet.config + + - task: DownloadPipelineArtifact@1 + displayName: Download x86 Artifacts + inputs: + artifactName: $(BuildConfiguration)_x86 + downloadPath: $(Build.SourcesDirectory)\x86 + + - task: DownloadPipelineArtifact@1 + displayName: Download x64 Artifacts + inputs: + artifactName: $(BuildConfiguration)_x64 + downloadPath: $(Build.SourcesDirectory)\x64 + + - task: DownloadPipelineArtifact@1 + displayName: Download arm64 Artifacts + inputs: + artifactName: $(BuildConfiguration)_arm64 + downloadPath: $(Build.SourcesDirectory)\arm64 + + - task: EsrpCodeSigning@2 + displayName: ESRP CodeSigning NatVis + inputs: + ConnectedServiceName: 81cc6790-027c-4ef3-928d-65e8b96a691a + FolderPath: $(Build.SourcesDirectory) + Pattern: | + x86\cppwinrt\cppwinrt.exe + x86\Component\cppwinrtvisualizer.dll + x64\Component\cppwinrtvisualizer.dll + arm64\Component\cppwinrtvisualizer.dll + x86\Standalone\cppwinrtvisualizer.dll + x64\Standalone\cppwinrtvisualizer.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" + } + ] + + - task: CmdLine@2 + displayName: Stage Signed Binaries + inputs: + script: | + echo F|xcopy /S /Q /Y /F x86\cppwinrt\cppwinrt.exe $(Build.ArtifactStagingDirectory)\x86\cppwinrt.exe + echo F|xcopy /S /Q /Y /F x86\Component\cppwinrtvisualizer.dll $(Build.ArtifactStagingDirectory)\x86\Component\cppwinrtvisualizer.dll + echo F|xcopy /S /Q /Y /F x64\Component\cppwinrtvisualizer.dll $(Build.ArtifactStagingDirectory)\x64\Component\cppwinrtvisualizer.dll + echo F|xcopy /S /Q /Y /F arm64\Component\cppwinrtvisualizer.dll $(Build.ArtifactStagingDirectory)\arm64\Component\cppwinrtvisualizer.dll + echo F|xcopy /S /Q /Y /F x86\Standalone\cppwinrtvisualizer.dll $(Build.ArtifactStagingDirectory)\x86\Standalone\cppwinrtvisualizer.dll + echo F|xcopy /S /Q /Y /F x64\Standalone\cppwinrtvisualizer.dll $(Build.ArtifactStagingDirectory)\x64\Standalone\cppwinrtvisualizer.dll + echo F|xcopy /S /Q /Y /F arm64\Standalone\cppwinrtvisualizer.dll $(Build.ArtifactStagingDirectory)\arm64\Standalone\cppwinrtvisualizer.dll + workingDirectory: $(Build.SourcesDirectory) + failOnStderr: true + + - task: CmdLine@2 + displayName: Stage cppwinrtvisualizer.vsdconfig + inputs: + script: | + copy $(Build.SourcesDirectory)\x86\Component\cppwinrtvisualizer.vsdconfig x86\Component\cppwinrtvisualizer.vsdconfig + copy $(Build.SourcesDirectory)\x86\Standalone\cppwinrtvisualizer.vsdconfig x86\Standalone\cppwinrtvisualizer.vsdconfig + workingDirectory: $(Build.ArtifactStagingDirectory) + failOnStderr: true + + - task: NuGetCommand@2 + displayName: Build NuGet + inputs: + command: pack + searchPatternPack: nuget/Microsoft.Windows.CppWinRT.nuspec + versioningScheme: byBuildNumber + buildProperties: 'target_version=$(Build.BuildNumber);cppwinrt_exe=$(Build.ArtifactStagingDirectory)\x86\cppwinrt.exe;cppwinrt_fast_fwd_x86=$(Build.SourcesDirectory)\x86\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_x64=$(Build.SourcesDirectory)\x64\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm64=$(Build.SourcesDirectory)\arm64\cppwinrt_fast_forwarder.lib ' + + - task: ComponentGovernanceComponentDetection@0 + displayName: Component Detection + + - task: EsrpCodeSigning@2 + displayName: ESRP CodeSigning Nuget Package + inputs: + ConnectedServiceName: 81cc6790-027c-4ef3-928d-65e8b96a691a + FolderPath: $(Build.ArtifactStagingDirectory) + Pattern: Microsoft.Windows.CppWinRT.*.nupkg + UseMinimatch: true + 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" + } + ] + + - task: PkgESNuGetPublisher@0 + displayName: Publish NuGet Package + inputs: + searchPattern: $(System.ArtifactsDirectory)\Microsoft.Windows.CppWinRT.$(Build.BuildNumber).nupkg + nuGetFeedType: internal + feedName: https://microsoft.pkgs.visualstudio.com/_packaging/CppWinRT/nuget/v3/index.json + + - task: VSBuild@1 + displayName: Build Component VSIXes + inputs: + solution: vsix/vsix.sln + msbuildArgs: /p:Deployment=Component,CppWinRTVersion=$(Build.BuildNumber),NatvisDirx86=$(Build.ArtifactStagingDirectory)\x86\Component\,NatvisDirx64=$(Build.ArtifactStagingDirectory)\x64\Component\,NatvisDirarm64=$(Build.ArtifactStagingDirectory)\arm64\Component\,NupkgDir=$(Build.ArtifactStagingDirectory) /restore + platform: Any CPU + configuration: Release + + - task: ExtractFiles@1 + displayName: Extract Component VSIX files for signing + inputs: + archiveFilePatterns: $(Build.SourcesDirectory)\vsix\Dev17\bin\Release\Component\Microsoft.Windows.CppWinRT.Dev17.vsix + destinationFolder: $(Agent.TempDirectory)\Microsoft.Windows.CppWinRT.Dev17.vsix + overwriteExistingFiles: true + + - task: EsrpCodeSigning@2 + displayName: ESRP CodeSign VSIX contents + inputs: + ConnectedServiceName: 81cc6790-027c-4ef3-928d-65e8b96a691a + FolderPath: $(Agent.TempDirectory)\Microsoft.Windows.CppWinRT.Dev17.vsix + Pattern: '**/Microsoft.Windows.CppWinRT.*.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" + } + ] + - task: ArchiveFiles@2 + displayName: Repack signed VSIX contents + inputs: + rootFolderOrFile: $(Agent.TempDirectory)\Microsoft.Windows.CppWinRT.Dev17.vsix + includeRootFolder: false + archiveFile: $(Build.SourcesDirectory)\vsix\Dev17\bin\Release\Component\Microsoft.Windows.CppWinRT.Dev17.vsix + + - task: VSBuild@1 + displayName: Build Standalone VSIXes + inputs: + solution: vsix/vsix.sln + msbuildArgs: /p:Deployment=Standalone,CppWinRTVersion=$(Build.BuildNumber),NatvisDirx86=$(Build.ArtifactStagingDirectory)\x86\Standalone\,NatvisDirx64=$(Build.ArtifactStagingDirectory)\x64\Standalone\,NatvisDirarm64=$(Build.ArtifactStagingDirectory)\arm64\Standalone\,NupkgDir=$(Build.ArtifactStagingDirectory) /restore + platform: x86 + configuration: Release + + - task: EsrpCodeSigning@2 + displayName: ESRP CodeSigning VSIX + inputs: + ConnectedServiceName: 81cc6790-027c-4ef3-928d-65e8b96a691a + FolderPath: $(Build.SourcesDirectory)\vsix\ + Pattern: | + Dev16\bin\Release\Component\Microsoft.Windows.CppWinRT.vsix + Dev16\bin\Release\Standalone\Microsoft.Windows.CppWinRT.vsix + Dev17\bin\Release\Component\Microsoft.Windows.CppWinRT.Dev17.vsix + Dev17\bin\Release\Standalone\Microsoft.Windows.CppWinRT.Dev17.vsix + UseMinimatch: true + signConfigType: inlineSignParams + inlineOperation: | + [ + { + "KeyCode" : "CP-233016", + "OperationCode" : "OpcSign", + "Parameters" : { + "FileDigest" : "/fd SHA256" + }, + "ToolName" : "sign", + "ToolVersion" : "1.0" + }, + { + "KeyCode" : "CP-233016", + "OperationCode" : "OpcVerify", + "Parameters" : {}, + "ToolName" : "sign", + "ToolVersion" : "1.0" + } + ] + + - task: CmdLine@2 + displayName: Stage Component VSIX (Dev17) + inputs: + script: | + echo F|xcopy /S /Q /Y /F Microsoft.Windows.CppWinRT.Dev17.vsix $(Build.ArtifactStagingDirectory)\Component\Dev17\Microsoft.Windows.CppWinRT.Dev17.vsix + echo F|xcopy /S /Q /Y /F Microsoft.Windows.CppWinRT.Dev17.json $(Build.ArtifactStagingDirectory)\Component\Dev17\Microsoft.Windows.CppWinRT.Dev17.json + echo F|xcopy /S /Q /Y /F Microsoft.Windows.CppWinRT.Dev17.pdb $(Build.ArtifactStagingDirectory)\Component\Dev17\Microsoft.Windows.CppWinRT.Dev17.pdb + workingDirectory: $(Build.SourcesDirectory)\vsix\Dev17\bin\Release\Component + failOnStderr: true + + - task: CopyFiles@2 + displayName: Stage Component VSIX Manifest (Dev17) + inputs: + SourceFolder: $(Build.SourcesDirectory)\vsix + Contents: | + extension.manifest.json + overview.md + TargetFolder: $(Build.ArtifactStagingDirectory)\Component\Dev17 + OverWrite: true + + - task: CmdLine@2 + displayName: Stage Standalone VSIX (Dev16) + inputs: + script: echo F|xcopy /S /Q /Y /F Microsoft.Windows.CppWinRT.vsix $(Build.ArtifactStagingDirectory)\Standalone\Dev16\Microsoft.Windows.CppWinRT.vsix + workingDirectory: $(Build.SourcesDirectory)\vsix\Dev16\bin\$(BuildConfiguration)\Standalone + failOnStderr: true + + - task: CopyFiles@2 + displayName: Stage Standalone VSIX Manifest (Dev16) + inputs: + SourceFolder: $(Build.SourcesDirectory)\vsix + Contents: | + extension.manifest.json + overview.md + TargetFolder: $(Build.ArtifactStagingDirectory)\Standalone\Dev16 + OverWrite: true + + - task: CmdLine@2 + displayName: Stage Standalone VSIX (Dev17) + inputs: + script: echo F|xcopy /S /Q /Y /F Microsoft.Windows.CppWinRT.Dev17.vsix $(Build.ArtifactStagingDirectory)\Standalone\Dev17\Microsoft.Windows.CppWinRT.Dev17.vsix + workingDirectory: $(Build.SourcesDirectory)\vsix\Dev17\bin\$(BuildConfiguration)\Standalone + failOnStderr: true + + - task: CopyFiles@2 + displayName: Stage Standalone VSIX Manifest (Dev17) + inputs: + SourceFolder: $(Build.SourcesDirectory)\vsix + Contents: | + extension.manifest.json + overview.md + TargetFolder: $(Build.ArtifactStagingDirectory)\Standalone\Dev17 + OverWrite: true + + - task: ManifestGeneratorTask@0 + displayName: SBOM for Dev16 + inputs: + BuildDropPath: $(Build.ArtifactStagingDirectory)\Standalone\Dev16 + + - task: ManifestGeneratorTask@0 + displayName: SBOM for Dev17 Standalone + inputs: + BuildDropPath: $(Build.ArtifactStagingDirectory)\Standalone\Dev17 + + - task: ManifestGeneratorTask@0 + displayName: SBOM for Dev17 Component + inputs: + BuildDropPath: $(Build.ArtifactStagingDirectory)\Component\Dev17 + + - task: PublishPipelineArtifact@0 + displayName: Publish VSIX + inputs: + artifactName: Publish + targetPath: $(Build.ArtifactStagingDirectory) + + - task: PublishSymbols@2 + displayName: Publish Component VSIX symbols + inputs: + SymbolsFolder: $(Build.ArtifactStagingDirectory) + SearchPattern: '**/Microsoft.Windows.CppWinRT.Dev17.pdb' + SymbolServerType: TeamServices + SymbolsProduct: CppWinRT +... diff --git a/.pipelines/jobs/OneBranchBuild.yml b/.pipelines/jobs/OneBranchBuild.yml new file mode 100644 index 000000000..dfee85edf --- /dev/null +++ b/.pipelines/jobs/OneBranchBuild.yml @@ -0,0 +1,147 @@ +parameters: + - name: BuildConfiguration + type: string + - name: BuildVersion + type: string + - name: OutputDirectory + type: string + default: '$(Build.SourcesDirectory)\out' + - name: OfficialBuild + type: boolean + default: false + +jobs: +- job: + pool: + type: windows + strategy: + matrix: + x86: + BuildPlatform: 'x86' + x64: + BuildPlatform: 'x64' + arm64: + BuildPlatform: 'arm64' + + variables: + ob_outputDirectory: ${{ parameters.OutputDirectory }} + ob_artifactSuffix: $(BuildPlatform) + StagingFolder: $(ob_outputDirectory) + + ${{ if eq(parameters.OfficialBuild, 'false') }}: + ob_sdl_codeSignValidation_excludes: '-|**\*.exe;-|**\*.dll' + + ob_sdl_prefast_enabled: true + ob_sdl_prefast_runDuring: "Guardian" + ob_sdl_checkCompliantCompilerWarnings: true + + ob_symbolsPublishing_enabled: ${{ parameters.OfficialBuild }} + ob_symbolsPublishing_symbolsFolder: '$(ob_outputDirectory)' + ob_symbolsPublishing_searchPattern: '**\*.pdb' + ob_symbolsPublishing_indexSources: true + + steps: + - task: UseDotNet@2 + continueOnError: true + inputs: + packageType: 'runtime' + version: '6.x' + performMultiLevelLookup: true + + - task: NuGetCommand@2 + displayName: NuGet restore cppwinrt.sln + inputs: + command: 'restore' + restoreSolution: '$(Build.SourcesDirectory)\cppwinrt.sln' + feedsToUse: config + nugetConfigPath: NuGet.config + + - task: VSBuild@1 + displayName: Build fast_fwd + inputs: + solution: $(Build.SourcesDirectory)\cppwinrt.sln + msbuildArgs: /t:fast_fwd /m /p:CppWinRTBuildVersion=${{ parameters.BuildVersion }},clean_intermediate_files=true + platform: $(BuildPlatform) + configuration: ${{ parameters.BuildConfiguration }} + + - task: CopyFiles@2 + displayName: Stage cppwinrt_fast_forwarder.lib + inputs: + SourceFolder: $(Build.SourcesDirectory)\_build\$(BuildPlatform)\$(BuildConfiguration) + Contents: cppwinrt_fast_forwarder.lib + TargetFolder: $(StagingFolder) + + - task: NuGetCommand@2 + displayName: NuGet restore cppwinrtvisualizer.sln + inputs: + command: 'restore' + restoreSolution: '$(Build.SourcesDirectory)\natvis\cppwinrtvisualizer.sln' + feedsToUse: config + nugetConfigPath: NuGet.config + + - task: VSBuild@1 + displayName: Build Component visualizer + condition: or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'arm64')) + inputs: + solution: $(Build.SourcesDirectory)\natvis\cppwinrtvisualizer.sln + msbuildArgs: /m /p:Deployment=Component,CppWinRTBuildVersion=${{ parameters.BuildVersion }},clean_intermediate_files=true + platform: $(BuildPlatform) + configuration: ${{ parameters.BuildConfiguration }} + + - task: VSBuild@1 + displayName: Build Standalone visualizer + condition: or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'arm64')) + inputs: + solution: $(Build.SourcesDirectory)\natvis\cppwinrtvisualizer.sln + msbuildArgs: /m /p:Deployment=Standalone,CppWinRTBuildVersion=${{ parameters.BuildVersion }},clean_intermediate_files=true + platform: $(BuildPlatform) + configuration: ${{ parameters.BuildConfiguration }} + + - task: CopyFiles@2 + displayName: Stage Component cppwinrtvisualizer.* + condition: or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'arm64')) + inputs: + SourceFolder: $(Build.SourcesDirectory)\natvis\$(BuildPlatform)\$(BuildConfiguration)\Component + Contents: | + cppwinrtvisualizer.dll + cppwinrtvisualizer.pdb + cppwinrtvisualizer.vsdconfig + TargetFolder: $(StagingFolder)\Component + + - task: CopyFiles@2 + displayName: Stage Standalone cppwinrtvisualizer.* + condition: or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64'), eq(variables['BuildPlatform'], 'arm64')) + inputs: + SourceFolder: $(Build.SourcesDirectory)\natvis\$(BuildPlatform)\$(BuildConfiguration)\Standalone + Contents: | + cppwinrtvisualizer.dll + cppwinrtvisualizer.pdb + cppwinrtvisualizer.vsdconfig + TargetFolder: $(StagingFolder)\Standalone + + - task: VSBuild@1 + displayName: Build cppwinrt + condition: or(eq(variables['BuildPlatform'], 'x86'), eq(variables['BuildPlatform'], 'x64')) + inputs: + solution: $(Build.SourcesDirectory)\cppwinrt.sln + msbuildArgs: /t:cppwinrt /m /p:CppWinRTBuildVersion=${{ parameters.BuildVersion }},clean_intermediate_files=true + platform: $(BuildPlatform) + configuration: ${{ parameters.BuildConfiguration }} + + - task: CopyFiles@2 + displayName: Stage cppwinrt.* + inputs: + SourceFolder: $(Build.SourcesDirectory)\_build\$(BuildPlatform)\$(BuildConfiguration) + Contents: | + cppwinrt.exe + cppwinrt.pdb + TargetFolder: $(StagingFolder)\cppwinrt + + - task: onebranch.pipeline.signing@1 + displayName: '🔒 Onebranch Signing for cppwinrt binaries' + condition: eq(${{ parameters.OfficialBuild }}, 'true') + inputs: + command: sign + signing_profile: external_distribution + files_to_sign: '**/*.dll;**/*.exe' + search_root: $(StagingFolder) diff --git a/.pipelines/jobs/OneBranchNuGet.yml b/.pipelines/jobs/OneBranchNuGet.yml new file mode 100644 index 000000000..36ec3144b --- /dev/null +++ b/.pipelines/jobs/OneBranchNuGet.yml @@ -0,0 +1,75 @@ +# Build the NuGet package +parameters: + - name: BuildConfiguration + type: string + - name: NugetPackageVersion + type: string + - name: OfficialBuild + type: boolean + default: false + +jobs: + - job: + pool: + type: windows + + variables: + ob_outputDirectory: '$(Build.SourcesDirectory)\out' + PackageVersion: ${{ parameters.NugetPackageVersion }} + + ob_sdl_prefast_enabled: true + ob_sdl_prefast_runDuring: 'Guardian' + ob_sdl_checkCompliantCompilerWarnings: true + + steps: + - task: UseDotNet@2 + continueOnError: true + inputs: + packageType: 'runtime' + version: '6.x' + performMultiLevelLookup: true + + - task: NuGetToolInstaller@1 + displayName: Use NuGet 6.0.2 + continueOnError: True + inputs: + versionSpec: 6.0.2 + + - task: DownloadPipelineArtifact@1 + displayName: 'Download x86 artifacts' + inputs: + artifactName: 'drop_build_x86' + targetPath: '$(Build.SourcesDirectory)/x86' + + - task: DownloadPipelineArtifact@1 + displayName: 'Download x64 artifacts' + inputs: + artifactName: 'drop_build_x64' + targetPath: '$(Build.SourcesDirectory)/x64' + + - task: DownloadPipelineArtifact@1 + displayName: 'Download arm64 artifacts' + inputs: + artifactName: 'drop_build_arm64' + targetPath: '$(Build.SourcesDirectory)/arm64' + + - task: NuGetCommand@2 + displayName: 'Build NuGet package' + inputs: + command: 'custom' + arguments: 'pack nuget/Microsoft.Windows.CppWinRT.nuspec -NonInteractive -OutputDirectory $(ob_outputDirectory)\packages -Properties Configuration=release;cppwinrt_exe=$(Build.SourcesDirectory)\x86\cppwinrt\cppwinrt.exe;cppwinrt_fast_fwd_x86=$(Build.SourcesDirectory)\x86\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_x64=$(Build.SourcesDirectory)\x64\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm64=$(Build.SourcesDirectory)\arm64\cppwinrt_fast_forwarder.lib;target_version=$(PackageVersion) -Version $(PackageVersion) -Verbosity Detailed' + + - task: onebranch.pipeline.signing@1 + displayName: '🔒 Onebranch Signing for NuGet package' + condition: eq(${{ parameters.OfficialBuild }}, 'true') + inputs: + command: sign + signing_profile: external_distribution + files_to_sign: 'Microsoft.Windows.CppWinRT.*.nupkg' + search_root: $(ob_outputDirectory)\packages + + - task: NuGetCommand@2 + displayName: 'Publish NuGet package' + inputs: + command: 'custom' + arguments: 'push $(ob_outputDirectory)\packages\Microsoft.Windows.CppWinRT.$(PackageVersion).nupkg -NonInteractive -Source https://microsoft.pkgs.visualstudio.com/_packaging/CppWinRT/nuget/v3/index.json -ApiKey VSTS' diff --git a/.pipelines/jobs/OneBranchTest.yml b/.pipelines/jobs/OneBranchTest.yml new file mode 100644 index 000000000..2fb3a4bd1 --- /dev/null +++ b/.pipelines/jobs/OneBranchTest.yml @@ -0,0 +1,123 @@ +parameters: + - name: BuildConfiguration + type: string + - name: BuildVersion + type: string + +jobs: +- job: + pool: + type: windows + isCustom: true + name: 'Azure Pipelines' + vmImage: 'windows-2022' # (or 2019) + strategy: + matrix: + test.x86: + TestExe: 'test' + TestProject: 'test' + BuildPlatform: 'x86' + test_nocoro.x86: + TestExe: 'test_nocoro' + TestProject: 'test_nocoro' + BuildPlatform: 'x86' + test_cpp20.x86: + TestExe: 'test_cpp20' + TestProject: 'test_cpp20' + BuildPlatform: 'x86' + test_cpp20_no_sourcelocation.x86: + TestExe: 'test_cpp20_no_sourcelocation' + TestProject: 'test_cpp20_no_sourcelocation' + BuildPlatform: 'x86' + test_fast.x86: + TestExe: 'test_fast' + TestProject: 'test_fast' + BuildPlatform: 'x86' + test_slow.x86: + TestExe: 'test_slow' + TestProject: 'test_slow' + BuildPlatform: 'x86' + test_old.x86: + TestExe: 'test_old' + TestProject: 'old_tests\test_old' + BuildPlatform: 'x86' + test_module_lock_custom.x86: + TestExe: 'test_module_lock_custom' + TestProject: 'test_module_lock_custom' + BuildPlatform: 'x86' + test_module_lock_none.x86: + TestExe: 'test_module_lock_none' + TestProject: 'test_module_lock_none' + BuildPlatform: 'x86' + + variables: + ob_outputDirectory: $(Build.SourcesDirectory)\out + ob_artifactSuffix: $(TestExe).$(BuildPlatform) + ob_sdl_codeSignValidation_excludes: '-|**\*.exe;-|**\*.dll' + + ob_sdl_prefast_enabled: true + ob_sdl_prefast_runDuring: 'Build' + ob_sdl_checkCompliantCompilerWarnings: true + + BuildPath: '$(Build.SourcesDirectory)/_build/$(BuildPlatform)/${{ parameters.BuildConfiguration }}' + + steps: + - task: UseDotNet@2 + continueOnError: true + inputs: + packageType: 'runtime' + version: '6.x' + performMultiLevelLookup: true + + - task: DownloadPipelineArtifact@2 + displayName: 'Download cppwinrt executable' + inputs: + artifactName: 'drop_build_$(BuildPlatform)' + targetPath: '$(Pipeline.Workspace)' + + - task: CopyFiles@2 + displayName: 'Patch cppwinrt executable into build directory' + inputs: + sourceFolder: '$(Pipeline.Workspace)' + Contents: '**\cppwinrt.exe' + TargetFolder: $(BuildPath) + flattenFolders: true + + - task: NuGetCommand@2 + displayName: NuGet restore cppwinrt.sln + inputs: + command: 'restore' + restoreSolution: '$(Build.SourcesDirectory)\cppwinrt.sln' + feedsToUse: config + nugetConfigPath: NuGet.config + + - task: PowerShell@2 + displayName: Remove cppwinrt dependency from test projects + inputs: + targetType: inline + script: | + # Hack-ish: We already have a built exe, so we want to avoid rebuilding cppwinrt + mv cppwinrt.sln cppwinrt.sln.orig + Get-Content cppwinrt.sln.orig | + Where-Object { -not $_.Contains("{D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4}") } | + Set-Content cppwinrt.sln + "Modified contents" + Get-Content cppwinrt.sln + + - task: CmdLine@2 + displayName: Run cppwinrt to build projection + inputs: + script: $(BuildPath)\cppwinrt.exe -in local -out $(BuildPath) -verbose + + - task: VSBuild@1 + displayName: Build test + inputs: + solution: $(Build.SourcesDirectory)\cppwinrt.sln + msbuildArgs: /t:test\$(TestProject) /m /p:CppWinRTBuildVersion=${{ parameters.BuildVersion }},clean_intermediate_files=true /bl:$(ob_outputDirectory)\output.binlog + platform: $(BuildPlatform) + configuration: ${{ parameters.BuildConfiguration }} + + - task: CmdLine@2 + displayName: Run test + inputs: + script: $(BuildPath)\$(TestExe).exe diff --git a/.pipelines/jobs/OneBranchVsix.yml b/.pipelines/jobs/OneBranchVsix.yml new file mode 100644 index 000000000..696b7e292 --- /dev/null +++ b/.pipelines/jobs/OneBranchVsix.yml @@ -0,0 +1,149 @@ +parameters: + - name: BuildConfiguration + type: string + - name: BuildVersion + type: string + - name: NugetPackageVersion + type: string + - name: OfficialBuild + type: boolean + default: false + +jobs: +- job: + pool: + type: windows + strategy: + matrix: + Standalone: + Deployment: 'Standalone' + VsVersion: 'Dev17' + VsixFilename: Microsoft.Windows.CppWinRT.$(VsVersion) + Component: + Deployment: 'Component' + VsVersion: 'Dev17' + VsixFilename: Microsoft.Windows.CppWinRT.$(VsVersion) + Dev16: + Deployment: 'Standalone' + VsVersion: 'Dev16' + VsixFilename: Microsoft.Windows.CppWinRT + + variables: + ob_outputDirectory: $(Build.SourcesDirectory)\out + ob_artifactSuffix: $(VsVersion)_$(Deployment) + + BuildFolder: $(Build.SourcesDirectory)\vsix\$(VsVersion)\bin\${{ parameters.BuildConfiguration }}\$(Deployment) + + ob_symbolsPublishing_enabled: true + ob_symbolsPublishing_symbolsFolder: '$(ob_outputDirectory)' + ob_symbolsPublishing_searchPattern: '**\*.pdb' + ob_symbolsPublishing_indexSources: true + + ob_sdl_prefast_enabled: true + ob_sdl_prefast_runDuring: 'Build' + ob_sdl_checkCompliantCompilerWarnings: true + + steps: + - task: UseDotNet@2 + continueOnError: true + inputs: + packageType: 'sdk' + version: '6.x' + performMultiLevelLookup: true + + - task: NuGetToolInstaller@1 + displayName: Use NuGet 6.0.2 + continueOnError: True + inputs: + versionSpec: 6.0.2 + + - task: NuGetCommand@2 + displayName: NuGet restore + inputs: + command: 'restore' + restoreSolution: '$(Build.SourcesDirectory)\vsix\vsix.sln' + feedsToUse: config + nugetConfigPath: NuGet.config + + - task: DownloadPipelineArtifact@2 + displayName: 'Download x86 binaries' + inputs: + artifactName: 'drop_build_x86' + targetPath: '$(Build.SourcesDirectory)\x86' + + - task: DownloadPipelineArtifact@2 + displayName: 'Download x64 binaries' + inputs: + artifactName: 'drop_build_x64' + targetPath: '$(Build.SourcesDirectory)\x64' + + - task: DownloadPipelineArtifact@2 + displayName: 'Download arm64 binaries' + inputs: + artifactName: 'drop_build_arm64' + targetPath: '$(Build.SourcesDirectory)\arm64' + + - task: DownloadPipelineArtifact@2 + displayName: 'Download NuGet' + inputs: + artifactName: 'drop_NuGet_' + targetPath: '$(Pipeline.Workspace)\nuget' + + - task: VSBuild@1 + displayName: Build VSIX + inputs: + solution: $(Build.SourcesDirectory)\vsix\vsix.sln + msbuildArgs: /t:vsix_$(VsVersion) /m /p:CppWinRTVersion=${{ parameters.BuildVersion }},NugetPackageVersion=${{ parameters.NugetPackageVersion }},clean_intermediate_files=true,Deployment=$(Deployment),NatvisDirx86=$(Build.SourcesDirectory)\x86\$(Deployment)\,NatvisDirx64=$(Build.SourcesDirectory)\x64\$(Deployment)\,NatvisDirarm64=$(Build.SourcesDirectory)\arm64\$(Deployment)\,NupkgDir=$(Pipeline.Workspace)\nuget\packages /bl:$(ob_outputDirectory)\output.binlog + platform: 'Any CPU' + configuration: ${{ parameters.BuildConfiguration }} + + - task: ExtractFiles@1 + displayName: Extract VSIX contents for signing + inputs: + archiveFilePatterns: '$(BuildFolder)\$(VsixFilename).vsix' + destinationFolder: '$(Agent.TempDirectory)\$(VsixFilename)' + overwriteExistingFiles: true + + - task: onebranch.pipeline.signing@1 + displayName: '🔒 Onebranch Signing for VSIX contents' + condition: eq(${{ parameters.OfficialBuild }}, 'true') + inputs: + command: sign + signing_profile: external_distribution + files_to_sign: '**\*.dll' + search_root: '$(Agent.TempDirectory)\$(VsixFilename)' + + - task: ArchiveFiles@2 + displayName: 'Repack signed VSIX contents' + inputs: + rootFolderOrFile: '$(Agent.TempDirectory)\$(VsixFilename)' + includeRootFolder: false + archiveFile: '$(ob_outputDirectory)\$(VsixFilename).vsix' + + - task: onebranch.pipeline.signing@1 + displayName: '🔒 Onebranch Signing for VSIX' + condition: eq(${{ parameters.OfficialBuild }}, 'true') + inputs: + command: sign + signing_profile: external_distribution + files_to_sign: '$(VsixFilename).vsix' + search_root: '$(ob_outputDirectory)' + + - task: CopyFiles@2 + displayName: Stage VSIX Manifest + inputs: + SourceFolder: '$(Build.SourcesDirectory)\vsix' + Contents: | + extension.manifest.json + overview.md + TargetFolder: '$(ob_outputDirectory)' + + - task: CopyFiles@2 + displayName: Stage VSIX json and symbols + inputs: + SourceFolder: $(Buildfolder) + Contents: | + $(VsixFilename).pdb + $(VsixFilename).json + TargetFolder: '$(ob_outputDirectory)' + diff --git a/.pipelines/sync-mirror.yml b/.pipelines/sync-mirror.yml new file mode 100644 index 000000000..f7be6296d --- /dev/null +++ b/.pipelines/sync-mirror.yml @@ -0,0 +1,67 @@ +# 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/cppwinrt.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/.pipelines/variables/OneBranchVariables.yml b/.pipelines/variables/OneBranchVariables.yml new file mode 100644 index 000000000..ff7a98adb --- /dev/null +++ b/.pipelines/variables/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/.pipelines/variables/version.yml b/.pipelines/variables/version.yml new file mode 100644 index 000000000..46a535ee0 --- /dev/null +++ b/.pipelines/variables/version.yml @@ -0,0 +1,17 @@ +parameters: + - name: OfficialBuild + type: boolean + default: false + +variables: + MajorVersion: "3" + MinorVersion: "0" + VersionDate: $[format('{0:yyMMdd}', pipeline.startTime)] + VersionCounter: $[counter(variables['VersionDate'], 1)] + BuildVersion: $(MajorVersion).$(MinorVersion).$(VersionDate).$(VersionCounter) + PatchVersion: $(VersionDate)$(VersionCounter) + + ${{ if eq(parameters.OfficialBuild, true) }}: + NugetPackageVersion: $(BuildVersion) + ${{ else }}: + NugetPackageVersion: $(BuildVersion)-unofficial diff --git a/.runsettings b/.runsettings new file mode 100644 index 000000000..5f10e44f8 --- /dev/null +++ b/.runsettings @@ -0,0 +1,24 @@ + + + + .\TestResults + 60000 + true + + + + + + + Single + (?i:Test) + true + on + true + Verbose + AdditionalInfo + ShortInfo + , + 20000 + + \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 000000000..b0089e7ad --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,208 @@ +# This CMake build file is intended for use with the llvm-mingw toolchain: +# https://github.com/mstorsjo/llvm-mingw +# +# It also works for building natively on Linux, or cross-building from Linux +# for running on Windows with a mingw-w64 toolchain. +# +# It most probably doesn't work with MSVC. + +cmake_minimum_required(VERSION 3.16) + +project(cppwinrt LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +set(CPPWINRT_BUILD_VERSION "999.999.999.999" CACHE STRING "The version string used for cppwinrt.") +if(CPPWINRT_BUILD_VERSION STREQUAL "999.999.999.999" OR CPPWINRT_BUILD_VERSION STREQUAL "0.0.0.0") + message(WARNING "CPPWINRT_BUILD_VERSION has been set to a dummy version string. Do not use in production!") +endif() +message(STATUS "Using version string: ${CPPWINRT_BUILD_VERSION}") + +if(WIN32) + # WinMD uses CreateFile2 which requires Windows 8. + add_compile_definitions(_WIN32_WINNT=0x0602) +endif() + + +# === prebuild: Generator tool for strings.cpp, strings.h, version.rc === + +if(CMAKE_CROSSCOMPILING) + include(ExternalProject) + ExternalProject_Add(cppwinrt-prebuild + SOURCE_DIR "${PROJECT_SOURCE_DIR}/prebuild" + CMAKE_ARGS -DCMAKE_INSTALL_PREFIX= "-DCPPWINRT_BUILD_VERSION=${CPPWINRT_BUILD_VERSION}" + ) + ExternalProject_Get_Property(cppwinrt-prebuild INSTALL_DIR) + set(PREBUILD_TOOL "${INSTALL_DIR}/bin/cppwinrt-prebuild") + unset(INSTALL_DIR) +else() + add_subdirectory(prebuild) + set(PREBUILD_TOOL cppwinrt-prebuild) +endif() + + +# === Step to create autogenerated files === + +file(GLOB PREBUILD_STRINGS_FILES + LIST_DIRECTORIES false + CONFIGURE_DEPENDS + strings/*.h +) +add_custom_command( + OUTPUT + ${PROJECT_BINARY_DIR}/strings.cpp + ${PROJECT_BINARY_DIR}/version.rc + COMMAND "${PREBUILD_TOOL}" ARGS "${PROJECT_SOURCE_DIR}/strings" "${PROJECT_BINARY_DIR}" + DEPENDS + cppwinrt-prebuild + ${PREBUILD_STRINGS_FILES} + VERBATIM +) + + +# === cppwinrt === + +set(CPPWINRT_SRCS + cppwinrt/main.cpp + "${PROJECT_BINARY_DIR}/strings.cpp" +) + +set(CPPWINRT_HEADERS + cppwinrt/pch.h + cppwinrt/cmd_reader.h + cppwinrt/code_writers.h + cppwinrt/component_writers.h + cppwinrt/file_writers.h + cppwinrt/helpers.h + cppwinrt/pch.h + cppwinrt/settings.h + cppwinrt/task_group.h + cppwinrt/text_writer.h + cppwinrt/type_writers.h +) + +if(WIN32) + add_custom_command( + OUTPUT + "${PROJECT_BINARY_DIR}/app.manifest" + COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/cppwinrt/app.manifest" "${PROJECT_BINARY_DIR}/app.manifest" + DEPENDS "${PROJECT_SOURCE_DIR}/cppwinrt/app.manifest" + VERBATIM + ) + # Do the configure_file dance so that app.manifest.rc don't get modified every + # single time the project is reconfigured and trigger a rebuild. + file(WRITE "${PROJECT_BINARY_DIR}/app.manifest.rc.in" "1 24 \"app.manifest\"\n") + configure_file( + "${PROJECT_BINARY_DIR}/app.manifest.rc.in" + "${PROJECT_BINARY_DIR}/app.manifest.rc" + COPYONLY + ) + + set(CPPWINRT_RESOURCES + "${PROJECT_BINARY_DIR}/app.manifest" + "${PROJECT_BINARY_DIR}/app.manifest.rc" + "${PROJECT_BINARY_DIR}/version.rc" + ) +endif() + +add_executable(cppwinrt ${CPPWINRT_SRCS} ${CPPWINRT_RESOURCES} ${CPPWINRT_HEADERS}) +target_compile_definitions(cppwinrt PRIVATE CPPWINRT_VERSION_STRING="${CPPWINRT_BUILD_VERSION}") +target_include_directories(cppwinrt PRIVATE ${PROJECT_BINARY_DIR}) + +if(WIN32) + target_link_libraries(cppwinrt shlwapi) +endif() + +install(TARGETS cppwinrt) + + +# HACK: Handle the xmllite import lib. +# mingw-w64 before commit 5ac1a2c is missing the import lib for xmllite. This +# checks whether the current build environment provides libxmllite.a, and +# generates the import lib if needed. + +set(XMLLITE_LIBRARY xmllite) +if(MINGW) + function(TestLinkXmlLite OUTPUT_VARNAME) + include(CheckCXXSourceCompiles) + set(CMAKE_REQUIRED_LIBRARIES xmllite) + check_cxx_source_compiles(" +#include +int main() { + CreateXmlReader(__uuidof(IXmlReader), nullptr, nullptr); +} + " ${OUTPUT_VARNAME}) + endfunction() + + function(TestIsI386 OUTPUT_VARNAME) + include(CheckCXXSourceCompiles) + check_cxx_source_compiles(" +#if !defined(__i386__) && !defined(_M_IX86) +# error Not i386 +#endif +int main() {} + " ${OUTPUT_VARNAME}) + endfunction() + + TestLinkXmlLite(HAS_LIBXMLLITE) + if(NOT HAS_LIBXMLLITE) + TestIsI386(TARGET_IS_I386) + if(TARGET_IS_I386) + set(XMLLITE_DEF_FILE xmllite_i386) + else() + set(XMLLITE_DEF_FILE xmllite) + endif() + include(CMakeFindBinUtils) + add_custom_command( + OUTPUT + "${PROJECT_BINARY_DIR}/libxmllite.a" + COMMAND "${CMAKE_DLLTOOL}" -k -d "${PROJECT_SOURCE_DIR}/mingw-support/${XMLLITE_DEF_FILE}.def" -l "${PROJECT_BINARY_DIR}/libxmllite.a" + DEPENDS "${PROJECT_SOURCE_DIR}/mingw-support/${XMLLITE_DEF_FILE}.def" + VERBATIM + ) + add_custom_target(gen-libxmllite + DEPENDS "${PROJECT_BINARY_DIR}/libxmllite.a" + ) + set(XMLLITE_LIBRARY "${PROJECT_BINARY_DIR}/libxmllite.a") + add_dependencies(cppwinrt gen-libxmllite) + endif() +endif() +if(WIN32) + target_link_libraries(cppwinrt "${XMLLITE_LIBRARY}") +endif() + + +# === winmd: External header-only library for reading winmd files === + +set(EXTERNAL_WINMD_INCLUDE_DIR "" CACHE PATH "Path to the include dir of an\ + external copy of the winmd library headers. Leave empty (default) to have\ + it downloaded as ExternalProject during build.") + +if(EXTERNAL_WINMD_INCLUDE_DIR STREQUAL "") + message(STATUS "The winmd library will be downloaded using ExternalProject.") + include(ExternalProject) + ExternalProject_Add(winmd + GIT_REPOSITORY https://github.com/microsoft/winmd.git + GIT_TAG 0f1eae3bfa63fa2ba3c2912cbfe72a01db94cc5a + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND "" + UPDATE_COMMAND "" + ) + add_dependencies(cppwinrt winmd) + ExternalProject_Get_Property(winmd SOURCE_DIR) + set(winmd_INCLUDE_DIR "${SOURCE_DIR}/src") +else() +message(STATUS "Using winmd library headers at ${EXTERNAL_WINMD_INCLUDE_DIR}") + set(winmd_INCLUDE_DIR "${EXTERNAL_WINMD_INCLUDE_DIR}") +endif() +target_include_directories(cppwinrt PRIVATE "${winmd_INCLUDE_DIR}") + + +if(WIN32 AND NOT CMAKE_CROSSCOMPILING) + include(CTest) + if(BUILD_TESTING) + add_subdirectory(test) + endif() +endif() diff --git a/Directory.Build.Props b/Directory.Build.Props new file mode 100644 index 000000000..5972803c0 --- /dev/null +++ b/Directory.Build.Props @@ -0,0 +1,83 @@ + + + + + + + v143 + v145 + 10.0 + 10.0.18362.0 + + + + + + ClangCL + + 20 + + false + + + + 999.999.999.999 + $(Platform) + x86 + $(SolutionDir)_build\$(CppWinRTPlatform)\$(Configuration)\ + $(SolutionDir)_build\$(CppWinRTPlatform)\$(Configuration)\temp\$(MSBuildProjectName)\ + $(OutDir) + $(SolutionDir)_build\x86\$(Configuration)\ + + + + + Level4 + true + true + true + stdcpp17 + stdcpp20 + Use + pch.h + CPPWINRT_VERSION_STRING="$(CppWinRTBuildVersion)";%(PreprocessorDefinitions) + CATCH_CONFIG_COLOUR_ANSI;%(PreprocessorDefinitions) + true + /bigobj + /await:strict %(AdditionalOptions) + -Wno-unused-command-line-argument -fno-delayed-template-parsing -mcx16 + + + onecore.lib + + + CPPWINRT_VERSION_STRING="$(CppWinRTBuildVersion)";%(PreprocessorDefinitions) + + + + + + + + + diff --git a/Directory.Packages.props b/Directory.Packages.props new file mode 100644 index 000000000..1d77adffd --- /dev/null +++ b/Directory.Packages.props @@ -0,0 +1,12 @@ + + + true + + + + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 7ca9f70ac..19e1493dc 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -[![Build status](https://dev.azure.com/microsoft/Dart/_apis/build/status/cppwinrt%20internal%20build)](https://dev.azure.com/microsoft/Dart/_build/latest?definitionId=31784) - # The C++/WinRT language projection C++/WinRT is an entirely standard C++ language projection for Windows Runtime (WinRT) APIs, implemented as a header-file-based library, and designed to provide you with first-class access to the modern Windows API. With C++/WinRT, you can author and consume Windows Runtime APIs using any standards-compliant C++17 compiler. @@ -9,30 +7,35 @@ C++/WinRT is an entirely standard C++ language projection for Windows Runtime (W * Visual Studio extension: http://aka.ms/cppwinrt/vsix * Wikipedia: https://en.wikipedia.org/wiki/C++/WinRT -C++/WinRT is part of the [xlang](https://github.com/microsoft/xlang) family of projects that help developers create APIs that can run on multiple platforms and be used with a variety of languages. - # Building C++/WinRT Don't build C++/WinRT yourself - just download the latest version here: https://aka.ms/cppwinrt/nuget +## Working on the compiler + If you really want to build it yourself, the simplest way to do so is to run the `build_test_all.cmd` script in the root directory. Developers needing to work on the C++/WinRT compiler itself should go through the following steps to arrive at an efficient inner loop: * Open a dev command prompt pointing at the root of the repo. * Open the `cppwinrt.sln` solution. -* Build the x64 Release configuration of the `cppwinrt` project only. Don’t attempt to build anything just yet. -* Run `build_projection.cmd` in the dev command prompt. -* Switch to the x64 Debug configuration in Visual Studio and build all projects as needed. +* Choose a configuration (x64, x86, Release, Debug) and build projects as needed. + +If you are working on an ARM64 specific issue from an x64 or x86 host, you will need to instead: + +* Open the `cppwinrt.sln` solution +* Build the x86 version of the "cppwinrt" project first +* Switch to your preferred configuration and build the test binaries and run them in your test environment -# Contributing +## Comparing Outputs -This project welcomes contributions and suggestions. Most contributions require you to agree to a -Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us -the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. +Comparing the output of the prior release and your current changes will help show the impact of any updates. Starting from +a dev command prompt at the root of the repo _after_ following the above build instructions: -When you submit a pull request, a CLA bot will automatically determine whether you need to provide -a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions -provided by the bot. You will only need to do this once across all repos using our CLA. +* Run `build_projection.cmd` in the dev command prompt +* Run `build_prior_projection.cmd` in the dev command prompt as well +* Run `prepare_versionless_diffs.cmd` which removes version stamps on both current and prior projection +* Use a directory-level differencing tool to compare `_build\$(arch)\$(flavor)\winrt` and `_reference\$(arch)\$(flavor)\winrt` -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or -contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. +## Testing +This repository uses the [Catch2](https://github.com/catchorg/Catch2) testing framework. +- From a Visual Studio command line, you should run `build_tests_all.cmd` to build and run the tests. To Debug the tests, you can debug the associated `_build\$(arch)\$(flavor)\.exe` under the debugger of your choice. +- Optionally, you can install the [Catch2Adapter](https://marketplace.visualstudio.com/items?itemName=JohnnyHendriks.ext01) to run the tests from Visual Studio. \ No newline at end of file diff --git a/build_nuget.cmd b/build_nuget.cmd index 068e1dfd0..99e193311 100644 --- a/build_nuget.cmd +++ b/build_nuget.cmd @@ -1,13 +1,12 @@ rem @echo off set target_version=%1 -if "%target_version%"=="" set target_version=1.2.3.4 +if "%target_version%"=="" set target_version=999.999.999.999 call msbuild /m /p:Configuration=Release,Platform=x86,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:fast_fwd call msbuild /m /p:Configuration=Release,Platform=x64,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:fast_fwd -call msbuild /m /p:Configuration=Release,Platform=arm,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:fast_fwd call msbuild /m /p:Configuration=Release,Platform=arm64,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:fast_fwd call msbuild /m /p:Configuration=Release,Platform=x86,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:cppwinrt -nuget pack nuget\Microsoft.Windows.CppWinRT.nuspec -Properties cppwinrt_exe=%cd%\_build\x86\Release\cppwinrt.exe;cppwinrt_fast_fwd_x86=%cd%\_build\x86\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_x64=%cd%\_build\x64\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm=%cd%\_build\arm\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm64=%cd%\_build\arm64\Release\cppwinrt_fast_forwarder.lib +nuget pack nuget\Microsoft.Windows.CppWinRT.nuspec -Properties target_version=%target_version%;cppwinrt_exe=%cd%\_build\x86\Release\cppwinrt.exe;cppwinrt_fast_fwd_x86=%cd%\_build\x86\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_x64=%cd%\_build\x64\Release\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm64=%cd%\_build\arm64\Release\cppwinrt_fast_forwarder.lib diff --git a/build_prior_projection.cmd b/build_prior_projection.cmd new file mode 100644 index 000000000..9d10f3eaa --- /dev/null +++ b/build_prior_projection.cmd @@ -0,0 +1,49 @@ +@echo off + +setlocal ENABLEDELAYEDEXPANSION + +set target_platform=%1 +set target_configuration=%2 +if "%target_platform%"=="" set target_platform=x64 + +if /I "%target_platform%" equ "all" ( + if "%target_configuration%"=="" ( + set target_configuration=all + ) + call %0 x86 !target_configuration! + call %0 x64 !target_configuration! + call %0 arm64 !target_configuration! + goto :eof +) + +if /I "%target_configuration%" equ "all" ( + call %0 %target_platform% Debug + call %0 %target_platform% Release + goto :eof +) + +if "%target_configuration%"=="" ( + set target_configuration=Debug +) + +set reference_output=%~p0\_reference\%target_platform%\%target_configuration% +if exist "%reference_output%" ( + echo Removing existing reference projections + rmdir /s /q "%reference_output%" +) + +if not exist ".\.nuget" mkdir ".\.nuget" +if not exist ".\.nuget\nuget.exe" powershell -Command "$ProgressPreference = 'SilentlyContinue' ; Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile .\.nuget\nuget.exe" + +mkdir %reference_output%\package +.\.nuget\nuget.exe install Microsoft.Windows.CppWinRT -o %reference_output%\package +set reference_cppwinrt= +for /F "delims=" %%a in ('dir /s /b %reference_output%\package\cppwinrt.exe') DO set reference_cppwinrt=%%a +if "%reference_cppwinrt%"=="" ( + echo Could not find the reference cppwinrt.exe under %reference_output%\package + goto :EOF +) + +echo Generating reference projection from %reference_cppwinrt% to %reference_output%\cppwinrt +%reference_cppwinrt% -in local -out %reference_output% -verbose +echo. diff --git a/build_projection.cmd b/build_projection.cmd index 9480b5ec0..140e6c7f8 100644 --- a/build_projection.cmd +++ b/build_projection.cmd @@ -12,7 +12,6 @@ if /I "%target_platform%" equ "all" ( ) call %0 x86 !target_configuration! call %0 x64 !target_configuration! - call %0 arm !target_configuration! call %0 arm64 !target_configuration! goto :eof ) @@ -27,6 +26,13 @@ if "%target_configuration%"=="" ( set target_configuration=Debug ) +set cppwinrt_exe=%~p0\_build\x64\Release\cppwinrt.exe + +if not exist "%cppwinrt_exe%" ( + echo Remember to build the "prebuild" and then "cppwinrt" projects for Release x64 first + goto :eof +) + echo Building projection into %target_platform% %target_configuration% -%~p0\_build\x64\Release\cppwinrt.exe -in local -out %~p0\_build\%target_platform%\%target_configuration% -verbose +%cppwinrt_exe% -in local -out %~p0\_build\%target_platform%\%target_configuration% -verbose echo. diff --git a/build_test_all.cmd b/build_test_all.cmd index 50aab9b28..c9beb852c 100644 --- a/build_test_all.cmd +++ b/build_test_all.cmd @@ -3,13 +3,14 @@ set target_platform=%1 set target_configuration=%2 set target_version=%3 +set clean_intermediate_files=%4 if "%target_platform%"=="" set target_platform=x64 if "%target_configuration%"=="" set target_configuration=Release -if "%target_version%"=="" set target_version=1.2.3.4 +if "%target_version%"=="" set target_version=999.999.999.999 if not exist ".\.nuget" mkdir ".\.nuget" -if not exist ".\.nuget\nuget.exe" powershell -Command "Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile .\.nuget\nuget.exe" +if not exist ".\.nuget\nuget.exe" powershell -Command "$ProgressPreference = 'SilentlyContinue' ; Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile .\.nuget\nuget.exe" call .nuget\nuget.exe restore cppwinrt.sln" call .nuget\nuget.exe restore natvis\cppwinrtvisualizer.sln @@ -17,20 +18,24 @@ call .nuget\nuget.exe restore test\nuget\NugetTest.sln call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:fast_fwd -if "%target_platform%"=="arm" goto :eof +call msbuild /p:Configuration=%target_configuration%,Platform=%target_platform%,Deployment=Component;CppWinRTBuildVersion=%target_version% natvis\cppwinrtvisualizer.sln +call msbuild /p:Configuration=%target_configuration%,Platform=%target_platform%,Deployment=Standalone;CppWinRTBuildVersion=%target_version% natvis\cppwinrtvisualizer.sln + if "%target_platform%"=="arm64" goto :eof call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:cppwinrt -_build\%target_platform%\%target_configuration%\cppwinrt.exe -in local -out _build\%target_platform%\%target_configuration% -verbose -call msbuild /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% natvis\cppwinrtvisualizer.sln + call msbuild /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% test\nuget\NugetTest.sln call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test -call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_win7 +call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_nocoro +call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_cpp20 +call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_cpp20_no_sourcelocation call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_fast call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_slow call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_module_lock_custom call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_module_lock_none +call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\test_module_lock_none call msbuild /m /p:Configuration=%target_configuration%,Platform=%target_platform%,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:test\old_tests\test_old call run_tests.cmd %target_platform% %target_configuration% diff --git a/build_vsix.cmd b/build_vsix.cmd new file mode 100644 index 000000000..0a47f6bd6 --- /dev/null +++ b/build_vsix.cmd @@ -0,0 +1,36 @@ +@echo off + +set this_dir=%~dp0 +set target_configuration=%1 +set target_version=%2 +set target_deployment=%3 + +if "%target_configuration%"=="" set target_configuration=Release +if "%target_version%"=="" set target_version=999.999.999.999 +if "%target_deployment%"=="" set target_deployment=Standalone + +if not exist ".\.nuget" mkdir ".\.nuget" +if not exist ".\.nuget\nuget.exe" powershell -Command "Invoke-WebRequest https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -OutFile .\.nuget\nuget.exe" + +call .nuget\nuget.exe restore cppwinrt.sln" +call .nuget\nuget.exe restore natvis\cppwinrtvisualizer.sln +call .nuget\nuget.exe restore test\nuget\NugetTest.sln + +rem Build fast forwarder libs or all arches +call msbuild /m /p:Configuration=%target_configuration%,Platform=x86,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:fast_fwd +call msbuild /m /p:Configuration=%target_configuration%,Platform=x64,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:fast_fwd +call msbuild /m /p:Configuration=%target_configuration%,Platform=arm64,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:fast_fwd + +rem Build cppwinrt.exe for x86 only +call msbuild /m /p:Configuration=%target_configuration%,Platform=x86,CppWinRTBuildVersion=%target_version% cppwinrt.sln /t:cppwinrt + +rem Build cppwinrt visualizer dll for x86, x64, and arm64 +call msbuild /p:Configuration=%target_configuration%,Platform=x64,Deployment=%target_deployment%,CppWinRTBuildVersion=%target_version% natvis\cppwinrtvisualizer.sln +call msbuild /p:Configuration=%target_configuration%,Platform=x86,Deployment=%target_deployment%,CppWinRTBuildVersion=%target_version% natvis\cppwinrtvisualizer.sln +call msbuild /p:Configuration=%target_configuration%,Platform=arm64,Deployment=%target_deployment%,CppWinRTBuildVersion=%target_version% natvis\cppwinrtvisualizer.sln + +rem Build nuget +.nuget\nuget.exe pack nuget\Microsoft.Windows.CppWinRT.nuspec -NonInteractive -OutputDirectory %this_dir%_build -Properties Configuration=%target_configuration%;cppwinrt_exe=%this_dir%_build\x86\%target_configuration%\cppwinrt.exe;cppwinrt_fast_fwd_x86=%this_dir%_build\x86\%target_configuration%\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_x64=%this_dir%_build\x64\%target_configuration%\cppwinrt_fast_forwarder.lib;cppwinrt_fast_fwd_arm64=%this_dir%_build\arm64\%target_configuration%\cppwinrt_fast_forwarder.lib;target_version=%target_version% -version %target_version% -Verbosity Detailed + +rem Build vsix +call msbuild /restore /p:Configuration=%target_configuration%,Platform="Any CPU",Deployment=%target_deployment%,CppWinRTVersion=%target_version%,NugetPackageVersion=%target_version%,NatvisDirx86=%this_dir%natvis\x86\%target_configuration%\%target_deployment%,NatvisDirx64=%this_dir%natvis\x64\%target_configuration%\%target_deployment%,NatvisDirarm64=%this_dir%natvis\arm64\%target_configuration%\%target_deployment%,NupkgDir=%this_dir%_build vsix\vsix.sln diff --git a/cppwinrt.props b/cppwinrt.props deleted file mode 100644 index b313ffd04..000000000 --- a/cppwinrt.props +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - v141 - 10.0.17763.0 - - - - v142 - 10.0 - - - - - - clang-cl.exe - C:\Program Files\LLVM\bin - - - - - - 2.3.4.5 - $(Platform) - x86 - $(SolutionDir)_build\$(CmakePlatform)\$(Configuration) - $(CmakeOutDir)\ - $(SolutionDir)_build\x86\$(Configuration)\ - $(CmakeOutDir)\ - - - - - Level4 - true - true - stdcpp17 - Use - pch.h - CPPWINRT_VERSION_STRING="$(CppWinRTBuildVersion)";%(PreprocessorDefinitions) - true - /await /bigobj - -Wno-unused-command-line-argument -fno-delayed-template-parsing -Xclang -fcoroutines-ts -mcx16 - - - onecore.lib - - - CPPWINRT_VERSION_STRING="$(CppWinRTBuildVersion)";%(PreprocessorDefinitions) - - - - diff --git a/cppwinrt.sln b/cppwinrt.sln index 3189d1555..700e9f5ea 100644 --- a/cppwinrt.sln +++ b/cppwinrt.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28606.126 +# Visual Studio Version 17 +VisualStudioVersion = 17.6.33829.357 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "cppwinrt", "cppwinrt\cppwinrt.vcxproj", "{D613FB39-5035-4043-91E2-BAB323908AF4}" ProjectSection(ProjectDependencies) = postProject @@ -24,6 +24,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Component", "test\old_tests EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Composable", "test\old_tests\Composable\Composable.vcxproj", "{152E4C6E-9A9D-4D5A-B38D-4905D173649A}" ProjectSection(ProjectDependencies) = postProject + {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} = {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} EndProjectSection EndProject @@ -34,23 +35,26 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_component", "test\test EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test", "test\test\test.vcxproj", "{D2961EA1-A8CA-4A62-B760-948403DC8494}" ProjectSection(ProjectDependencies) = postProject + {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} = {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} {F1C915B3-2C64-4992-AFB7-7F035B1A7607} = {F1C915B3-2C64-4992-AFB7-7F035B1A7607} - {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} = {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_component_folders", "test\test_component_folders\test_component_folders.vcxproj", "{85695954-3800-4558-9857-966E69E9F9EC}" ProjectSection(ProjectDependencies) = postProject + {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} = {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_component_no_pch", "test\test_component_no_pch\test_component_no_pch.vcxproj", "{F1C915B3-2C64-4992-AFB7-7F035B1A7607}" ProjectSection(ProjectDependencies) = postProject + {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} = {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_component_base", "test\test_component_base\test_component_base.vcxproj", "{13333A6F-6A4A-48CD-865C-0F65135EB018}" ProjectSection(ProjectDependencies) = postProject + {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} = {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} EndProjectSection EndProject @@ -61,6 +65,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_component_derived", "t EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_component_fast", "test\test_component_fast\test_component_fast.vcxproj", "{0E0ACA62-A92F-44CF-BD41-AEB541946DF8}" ProjectSection(ProjectDependencies) = postProject + {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} = {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} EndProjectSection EndProject @@ -76,365 +81,353 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_slow", "test\test_slow EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_fast_fwd", "test\test_fast_fwd\test_fast_fwd.vcxproj", "{303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}" ProjectSection(ProjectDependencies) = postProject - {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} {0E0ACA62-A92F-44CF-BD41-AEB541946DF8} = {0E0ACA62-A92F-44CF-BD41-AEB541946DF8} + {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "fast_fwd", "fast_fwd\fast_fwd.vcxproj", "{A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "scratch", "scratch\scratch.vcxproj", "{E893622C-47DE-4F83-B422-0A26711590A4}" + ProjectSection(ProjectDependencies) = postProject + {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_module_lock_none", "test\test_module_lock_none\test_module_lock_none.vcxproj", "{D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}" ProjectSection(ProjectDependencies) = postProject + {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E} = {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E} + {13333A6F-6A4A-48CD-865C-0F65135EB018} = {13333A6F-6A4A-48CD-865C-0F65135EB018} {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_module_lock_custom", "test\test_module_lock_custom\test_module_lock_custom.vcxproj", "{08C40663-B6A3-481E-8755-AE32BAD99501}" ProjectSection(ProjectDependencies) = postProject + {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} = {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{3C7EA5F8-6E8C-4376-B499-2CAF596384B0}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_win7", "test\test_win7\test_win7.vcxproj", "{2EF696B9-7F4A-410F-AE5C-5301565C0F08}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_cpp20", "test\test_cpp20\test_cpp20.vcxproj", "{5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA}" ProjectSection(ProjectDependencies) = postProject + {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} = {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} - {F1C915B3-2C64-4992-AFB7-7F035B1A7607} = {F1C915B3-2C64-4992-AFB7-7F035B1A7607} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_cpp20_no_sourcelocation", "test\test_cpp20_no_sourcelocation\test_cpp20_no_sourcelocation.vcxproj", "{D4C8F881-84D5-4A7B-8BDE-AB4E34A05374}" + ProjectSection(ProjectDependencies) = postProject {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} = {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270} + {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_nocoro", "test\test_nocoro\test_nocoro.vcxproj", "{9E392830-805A-4AAF-932D-C493143EFACA}" + ProjectSection(ProjectDependencies) = postProject + {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "test_cpp20_module", "test\test_cpp20_module\test_cpp20_module.vcxproj", "{B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}" + ProjectSection(ProjectDependencies) = postProject + {D613FB39-5035-4043-91E2-BAB323908AF4} = {D613FB39-5035-4043-91E2-BAB323908AF4} + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D15C8430-A7CD-4616-BD84-243B26A9F1C2}" + ProjectSection(SolutionItems) = preProject + build_nuget.cmd = build_nuget.cmd + build_prior_projection.cmd = build_prior_projection.cmd + build_projection.cmd = build_projection.cmd + build_test_all.cmd = build_test_all.cmd + build_vsix.cmd = build_vsix.cmd + compile_tests.cmd = compile_tests.cmd + prepare_versionless_diffs.cmd = prepare_versionless_diffs.cmd + README.md = README.md + run_tests.cmd = run_tests.cmd EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|ARM = Debug|ARM Debug|ARM64 = Debug|ARM64 Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 - Release|ARM = Release|ARM Release|ARM64 = Release|ARM64 Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {D613FB39-5035-4043-91E2-BAB323908AF4}.Debug|ARM.ActiveCfg = Debug|ARM - {D613FB39-5035-4043-91E2-BAB323908AF4}.Debug|ARM.Build.0 = Debug|ARM {D613FB39-5035-4043-91E2-BAB323908AF4}.Debug|ARM64.ActiveCfg = Debug|ARM64 {D613FB39-5035-4043-91E2-BAB323908AF4}.Debug|ARM64.Build.0 = Debug|ARM64 {D613FB39-5035-4043-91E2-BAB323908AF4}.Debug|x64.ActiveCfg = Debug|x64 {D613FB39-5035-4043-91E2-BAB323908AF4}.Debug|x64.Build.0 = Debug|x64 {D613FB39-5035-4043-91E2-BAB323908AF4}.Debug|x86.ActiveCfg = Debug|Win32 {D613FB39-5035-4043-91E2-BAB323908AF4}.Debug|x86.Build.0 = Debug|Win32 - {D613FB39-5035-4043-91E2-BAB323908AF4}.Release|ARM.ActiveCfg = Release|ARM - {D613FB39-5035-4043-91E2-BAB323908AF4}.Release|ARM.Build.0 = Release|ARM {D613FB39-5035-4043-91E2-BAB323908AF4}.Release|ARM64.ActiveCfg = Release|ARM64 {D613FB39-5035-4043-91E2-BAB323908AF4}.Release|ARM64.Build.0 = Release|ARM64 {D613FB39-5035-4043-91E2-BAB323908AF4}.Release|x64.ActiveCfg = Release|x64 {D613FB39-5035-4043-91E2-BAB323908AF4}.Release|x64.Build.0 = Release|x64 {D613FB39-5035-4043-91E2-BAB323908AF4}.Release|x86.ActiveCfg = Release|Win32 {D613FB39-5035-4043-91E2-BAB323908AF4}.Release|x86.Build.0 = Release|Win32 - {FB239623-7D19-4025-BCEA-B43298D4A315}.Debug|ARM.ActiveCfg = Debug|ARM - {FB239623-7D19-4025-BCEA-B43298D4A315}.Debug|ARM.Build.0 = Debug|ARM {FB239623-7D19-4025-BCEA-B43298D4A315}.Debug|ARM64.ActiveCfg = Debug|ARM64 {FB239623-7D19-4025-BCEA-B43298D4A315}.Debug|ARM64.Build.0 = Debug|ARM64 {FB239623-7D19-4025-BCEA-B43298D4A315}.Debug|x64.ActiveCfg = Debug|x64 {FB239623-7D19-4025-BCEA-B43298D4A315}.Debug|x64.Build.0 = Debug|x64 {FB239623-7D19-4025-BCEA-B43298D4A315}.Debug|x86.ActiveCfg = Debug|Win32 {FB239623-7D19-4025-BCEA-B43298D4A315}.Debug|x86.Build.0 = Debug|Win32 - {FB239623-7D19-4025-BCEA-B43298D4A315}.Release|ARM.ActiveCfg = Release|ARM - {FB239623-7D19-4025-BCEA-B43298D4A315}.Release|ARM.Build.0 = Release|ARM {FB239623-7D19-4025-BCEA-B43298D4A315}.Release|ARM64.ActiveCfg = Release|ARM64 {FB239623-7D19-4025-BCEA-B43298D4A315}.Release|ARM64.Build.0 = Release|ARM64 {FB239623-7D19-4025-BCEA-B43298D4A315}.Release|x64.ActiveCfg = Release|x64 {FB239623-7D19-4025-BCEA-B43298D4A315}.Release|x64.Build.0 = Release|x64 {FB239623-7D19-4025-BCEA-B43298D4A315}.Release|x86.ActiveCfg = Release|Win32 {FB239623-7D19-4025-BCEA-B43298D4A315}.Release|x86.Build.0 = Release|Win32 - {C8B95FCB-9B0B-4E9F-B7D5-643883C192C9}.Debug|ARM.ActiveCfg = Debug|ARM - {C8B95FCB-9B0B-4E9F-B7D5-643883C192C9}.Debug|ARM.Build.0 = Debug|ARM {C8B95FCB-9B0B-4E9F-B7D5-643883C192C9}.Debug|ARM64.ActiveCfg = Debug|ARM64 {C8B95FCB-9B0B-4E9F-B7D5-643883C192C9}.Debug|ARM64.Build.0 = Debug|ARM64 {C8B95FCB-9B0B-4E9F-B7D5-643883C192C9}.Debug|x64.ActiveCfg = Debug|x64 {C8B95FCB-9B0B-4E9F-B7D5-643883C192C9}.Debug|x64.Build.0 = Debug|x64 {C8B95FCB-9B0B-4E9F-B7D5-643883C192C9}.Debug|x86.ActiveCfg = Debug|Win32 {C8B95FCB-9B0B-4E9F-B7D5-643883C192C9}.Debug|x86.Build.0 = Debug|Win32 - {C8B95FCB-9B0B-4E9F-B7D5-643883C192C9}.Release|ARM.ActiveCfg = Release|ARM - {C8B95FCB-9B0B-4E9F-B7D5-643883C192C9}.Release|ARM.Build.0 = Release|ARM {C8B95FCB-9B0B-4E9F-B7D5-643883C192C9}.Release|ARM64.ActiveCfg = Release|ARM64 {C8B95FCB-9B0B-4E9F-B7D5-643883C192C9}.Release|ARM64.Build.0 = Release|ARM64 {C8B95FCB-9B0B-4E9F-B7D5-643883C192C9}.Release|x64.ActiveCfg = Release|x64 {C8B95FCB-9B0B-4E9F-B7D5-643883C192C9}.Release|x64.Build.0 = Release|x64 {C8B95FCB-9B0B-4E9F-B7D5-643883C192C9}.Release|x86.ActiveCfg = Release|Win32 {C8B95FCB-9B0B-4E9F-B7D5-643883C192C9}.Release|x86.Build.0 = Release|Win32 - {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8}.Debug|ARM.ActiveCfg = Debug|ARM - {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8}.Debug|ARM.Build.0 = Debug|ARM {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8}.Debug|ARM64.ActiveCfg = Debug|ARM64 {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8}.Debug|ARM64.Build.0 = Debug|ARM64 {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8}.Debug|x64.ActiveCfg = Debug|x64 {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8}.Debug|x64.Build.0 = Debug|x64 {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8}.Debug|x86.ActiveCfg = Debug|Win32 {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8}.Debug|x86.Build.0 = Debug|Win32 - {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8}.Release|ARM.ActiveCfg = Release|ARM - {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8}.Release|ARM.Build.0 = Release|ARM {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8}.Release|ARM64.ActiveCfg = Release|ARM64 {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8}.Release|ARM64.Build.0 = Release|ARM64 {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8}.Release|x64.ActiveCfg = Release|x64 {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8}.Release|x64.Build.0 = Release|x64 {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8}.Release|x86.ActiveCfg = Release|Win32 {559A7CF4-DC5F-4D62-BA6B-0C2B025593F8}.Release|x86.Build.0 = Release|Win32 - {152E4C6E-9A9D-4D5A-B38D-4905D173649A}.Debug|ARM.ActiveCfg = Debug|ARM - {152E4C6E-9A9D-4D5A-B38D-4905D173649A}.Debug|ARM.Build.0 = Debug|ARM {152E4C6E-9A9D-4D5A-B38D-4905D173649A}.Debug|ARM64.ActiveCfg = Debug|ARM64 {152E4C6E-9A9D-4D5A-B38D-4905D173649A}.Debug|ARM64.Build.0 = Debug|ARM64 {152E4C6E-9A9D-4D5A-B38D-4905D173649A}.Debug|x64.ActiveCfg = Debug|x64 {152E4C6E-9A9D-4D5A-B38D-4905D173649A}.Debug|x64.Build.0 = Debug|x64 {152E4C6E-9A9D-4D5A-B38D-4905D173649A}.Debug|x86.ActiveCfg = Debug|Win32 {152E4C6E-9A9D-4D5A-B38D-4905D173649A}.Debug|x86.Build.0 = Debug|Win32 - {152E4C6E-9A9D-4D5A-B38D-4905D173649A}.Release|ARM.ActiveCfg = Release|ARM - {152E4C6E-9A9D-4D5A-B38D-4905D173649A}.Release|ARM.Build.0 = Release|ARM {152E4C6E-9A9D-4D5A-B38D-4905D173649A}.Release|ARM64.ActiveCfg = Release|ARM64 {152E4C6E-9A9D-4D5A-B38D-4905D173649A}.Release|ARM64.Build.0 = Release|ARM64 {152E4C6E-9A9D-4D5A-B38D-4905D173649A}.Release|x64.ActiveCfg = Release|x64 {152E4C6E-9A9D-4D5A-B38D-4905D173649A}.Release|x64.Build.0 = Release|x64 {152E4C6E-9A9D-4D5A-B38D-4905D173649A}.Release|x86.ActiveCfg = Release|Win32 {152E4C6E-9A9D-4D5A-B38D-4905D173649A}.Release|x86.Build.0 = Release|Win32 - {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}.Debug|ARM.ActiveCfg = Debug|ARM - {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}.Debug|ARM.Build.0 = Debug|ARM {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}.Debug|ARM64.ActiveCfg = Debug|ARM64 {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}.Debug|ARM64.Build.0 = Debug|ARM64 {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}.Debug|x64.ActiveCfg = Debug|x64 {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}.Debug|x64.Build.0 = Debug|x64 {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}.Debug|x86.ActiveCfg = Debug|Win32 {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}.Debug|x86.Build.0 = Debug|Win32 - {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}.Release|ARM.ActiveCfg = Release|ARM - {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}.Release|ARM.Build.0 = Release|ARM {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}.Release|ARM64.ActiveCfg = Release|ARM64 {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}.Release|ARM64.Build.0 = Release|ARM64 {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}.Release|x64.ActiveCfg = Release|x64 {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}.Release|x64.Build.0 = Release|x64 {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}.Release|x86.ActiveCfg = Release|Win32 {A91B8BF3-28E4-4D9E-8DBA-64B70E4F0270}.Release|x86.Build.0 = Release|Win32 - {D2961EA1-A8CA-4A62-B760-948403DC8494}.Debug|ARM.ActiveCfg = Debug|ARM - {D2961EA1-A8CA-4A62-B760-948403DC8494}.Debug|ARM.Build.0 = Debug|ARM {D2961EA1-A8CA-4A62-B760-948403DC8494}.Debug|ARM64.ActiveCfg = Debug|ARM64 {D2961EA1-A8CA-4A62-B760-948403DC8494}.Debug|ARM64.Build.0 = Debug|ARM64 {D2961EA1-A8CA-4A62-B760-948403DC8494}.Debug|x64.ActiveCfg = Debug|x64 {D2961EA1-A8CA-4A62-B760-948403DC8494}.Debug|x64.Build.0 = Debug|x64 {D2961EA1-A8CA-4A62-B760-948403DC8494}.Debug|x86.ActiveCfg = Debug|Win32 {D2961EA1-A8CA-4A62-B760-948403DC8494}.Debug|x86.Build.0 = Debug|Win32 - {D2961EA1-A8CA-4A62-B760-948403DC8494}.Release|ARM.ActiveCfg = Release|ARM - {D2961EA1-A8CA-4A62-B760-948403DC8494}.Release|ARM.Build.0 = Release|ARM {D2961EA1-A8CA-4A62-B760-948403DC8494}.Release|ARM64.ActiveCfg = Release|ARM64 {D2961EA1-A8CA-4A62-B760-948403DC8494}.Release|ARM64.Build.0 = Release|ARM64 {D2961EA1-A8CA-4A62-B760-948403DC8494}.Release|x64.ActiveCfg = Release|x64 {D2961EA1-A8CA-4A62-B760-948403DC8494}.Release|x64.Build.0 = Release|x64 {D2961EA1-A8CA-4A62-B760-948403DC8494}.Release|x86.ActiveCfg = Release|Win32 {D2961EA1-A8CA-4A62-B760-948403DC8494}.Release|x86.Build.0 = Release|Win32 - {85695954-3800-4558-9857-966E69E9F9EC}.Debug|ARM.ActiveCfg = Debug|ARM - {85695954-3800-4558-9857-966E69E9F9EC}.Debug|ARM.Build.0 = Debug|ARM {85695954-3800-4558-9857-966E69E9F9EC}.Debug|ARM64.ActiveCfg = Debug|ARM64 {85695954-3800-4558-9857-966E69E9F9EC}.Debug|ARM64.Build.0 = Debug|ARM64 {85695954-3800-4558-9857-966E69E9F9EC}.Debug|x64.ActiveCfg = Debug|x64 {85695954-3800-4558-9857-966E69E9F9EC}.Debug|x64.Build.0 = Debug|x64 {85695954-3800-4558-9857-966E69E9F9EC}.Debug|x86.ActiveCfg = Debug|Win32 {85695954-3800-4558-9857-966E69E9F9EC}.Debug|x86.Build.0 = Debug|Win32 - {85695954-3800-4558-9857-966E69E9F9EC}.Release|ARM.ActiveCfg = Release|ARM - {85695954-3800-4558-9857-966E69E9F9EC}.Release|ARM.Build.0 = Release|ARM {85695954-3800-4558-9857-966E69E9F9EC}.Release|ARM64.ActiveCfg = Release|ARM64 {85695954-3800-4558-9857-966E69E9F9EC}.Release|ARM64.Build.0 = Release|ARM64 {85695954-3800-4558-9857-966E69E9F9EC}.Release|x64.ActiveCfg = Release|x64 {85695954-3800-4558-9857-966E69E9F9EC}.Release|x64.Build.0 = Release|x64 {85695954-3800-4558-9857-966E69E9F9EC}.Release|x86.ActiveCfg = Release|Win32 {85695954-3800-4558-9857-966E69E9F9EC}.Release|x86.Build.0 = Release|Win32 - {F1C915B3-2C64-4992-AFB7-7F035B1A7607}.Debug|ARM.ActiveCfg = Debug|ARM - {F1C915B3-2C64-4992-AFB7-7F035B1A7607}.Debug|ARM.Build.0 = Debug|ARM {F1C915B3-2C64-4992-AFB7-7F035B1A7607}.Debug|ARM64.ActiveCfg = Debug|ARM64 {F1C915B3-2C64-4992-AFB7-7F035B1A7607}.Debug|ARM64.Build.0 = Debug|ARM64 {F1C915B3-2C64-4992-AFB7-7F035B1A7607}.Debug|x64.ActiveCfg = Debug|x64 {F1C915B3-2C64-4992-AFB7-7F035B1A7607}.Debug|x64.Build.0 = Debug|x64 {F1C915B3-2C64-4992-AFB7-7F035B1A7607}.Debug|x86.ActiveCfg = Debug|Win32 {F1C915B3-2C64-4992-AFB7-7F035B1A7607}.Debug|x86.Build.0 = Debug|Win32 - {F1C915B3-2C64-4992-AFB7-7F035B1A7607}.Release|ARM.ActiveCfg = Release|ARM - {F1C915B3-2C64-4992-AFB7-7F035B1A7607}.Release|ARM.Build.0 = Release|ARM {F1C915B3-2C64-4992-AFB7-7F035B1A7607}.Release|ARM64.ActiveCfg = Release|ARM64 {F1C915B3-2C64-4992-AFB7-7F035B1A7607}.Release|ARM64.Build.0 = Release|ARM64 {F1C915B3-2C64-4992-AFB7-7F035B1A7607}.Release|x64.ActiveCfg = Release|x64 {F1C915B3-2C64-4992-AFB7-7F035B1A7607}.Release|x64.Build.0 = Release|x64 {F1C915B3-2C64-4992-AFB7-7F035B1A7607}.Release|x86.ActiveCfg = Release|Win32 {F1C915B3-2C64-4992-AFB7-7F035B1A7607}.Release|x86.Build.0 = Release|Win32 - {13333A6F-6A4A-48CD-865C-0F65135EB018}.Debug|ARM.ActiveCfg = Debug|ARM - {13333A6F-6A4A-48CD-865C-0F65135EB018}.Debug|ARM.Build.0 = Debug|ARM {13333A6F-6A4A-48CD-865C-0F65135EB018}.Debug|ARM64.ActiveCfg = Debug|ARM64 {13333A6F-6A4A-48CD-865C-0F65135EB018}.Debug|ARM64.Build.0 = Debug|ARM64 {13333A6F-6A4A-48CD-865C-0F65135EB018}.Debug|x64.ActiveCfg = Debug|x64 {13333A6F-6A4A-48CD-865C-0F65135EB018}.Debug|x64.Build.0 = Debug|x64 {13333A6F-6A4A-48CD-865C-0F65135EB018}.Debug|x86.ActiveCfg = Debug|Win32 {13333A6F-6A4A-48CD-865C-0F65135EB018}.Debug|x86.Build.0 = Debug|Win32 - {13333A6F-6A4A-48CD-865C-0F65135EB018}.Release|ARM.ActiveCfg = Release|ARM - {13333A6F-6A4A-48CD-865C-0F65135EB018}.Release|ARM.Build.0 = Release|ARM {13333A6F-6A4A-48CD-865C-0F65135EB018}.Release|ARM64.ActiveCfg = Release|ARM64 {13333A6F-6A4A-48CD-865C-0F65135EB018}.Release|ARM64.Build.0 = Release|ARM64 {13333A6F-6A4A-48CD-865C-0F65135EB018}.Release|x64.ActiveCfg = Release|x64 {13333A6F-6A4A-48CD-865C-0F65135EB018}.Release|x64.Build.0 = Release|x64 {13333A6F-6A4A-48CD-865C-0F65135EB018}.Release|x86.ActiveCfg = Release|Win32 {13333A6F-6A4A-48CD-865C-0F65135EB018}.Release|x86.Build.0 = Release|Win32 - {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E}.Debug|ARM.ActiveCfg = Debug|ARM - {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E}.Debug|ARM.Build.0 = Debug|ARM {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E}.Debug|ARM64.ActiveCfg = Debug|ARM64 {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E}.Debug|ARM64.Build.0 = Debug|ARM64 {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E}.Debug|x64.ActiveCfg = Debug|x64 {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E}.Debug|x64.Build.0 = Debug|x64 {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E}.Debug|x86.ActiveCfg = Debug|Win32 {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E}.Debug|x86.Build.0 = Debug|Win32 - {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E}.Release|ARM.ActiveCfg = Release|ARM - {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E}.Release|ARM.Build.0 = Release|ARM {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E}.Release|ARM64.ActiveCfg = Release|ARM64 {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E}.Release|ARM64.Build.0 = Release|ARM64 {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E}.Release|x64.ActiveCfg = Release|x64 {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E}.Release|x64.Build.0 = Release|x64 {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E}.Release|x86.ActiveCfg = Release|Win32 {0080F6D1-AEC3-4F89-ADE1-3D22A7EBF99E}.Release|x86.Build.0 = Release|Win32 - {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Debug|ARM.ActiveCfg = Debug|ARM - {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Debug|ARM.Build.0 = Debug|ARM {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Debug|ARM64.ActiveCfg = Debug|ARM64 {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Debug|ARM64.Build.0 = Debug|ARM64 {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Debug|x64.ActiveCfg = Debug|x64 {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Debug|x64.Build.0 = Debug|x64 {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Debug|x86.ActiveCfg = Debug|Win32 {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Debug|x86.Build.0 = Debug|Win32 - {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Release|ARM.ActiveCfg = Release|ARM - {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Release|ARM.Build.0 = Release|ARM {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Release|ARM64.ActiveCfg = Release|ARM64 {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Release|ARM64.Build.0 = Release|ARM64 {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Release|x64.ActiveCfg = Release|x64 {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Release|x64.Build.0 = Release|x64 {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Release|x86.ActiveCfg = Release|Win32 {0E0ACA62-A92F-44CF-BD41-AEB541946DF8}.Release|x86.Build.0 = Release|Win32 - {F8A1FE5A-DC8A-49DF-B882-DEF76E38E484}.Debug|ARM.ActiveCfg = Debug|ARM - {F8A1FE5A-DC8A-49DF-B882-DEF76E38E484}.Debug|ARM.Build.0 = Debug|ARM {F8A1FE5A-DC8A-49DF-B882-DEF76E38E484}.Debug|ARM64.ActiveCfg = Debug|ARM64 {F8A1FE5A-DC8A-49DF-B882-DEF76E38E484}.Debug|ARM64.Build.0 = Debug|ARM64 {F8A1FE5A-DC8A-49DF-B882-DEF76E38E484}.Debug|x64.ActiveCfg = Debug|x64 {F8A1FE5A-DC8A-49DF-B882-DEF76E38E484}.Debug|x64.Build.0 = Debug|x64 {F8A1FE5A-DC8A-49DF-B882-DEF76E38E484}.Debug|x86.ActiveCfg = Debug|Win32 {F8A1FE5A-DC8A-49DF-B882-DEF76E38E484}.Debug|x86.Build.0 = Debug|Win32 - {F8A1FE5A-DC8A-49DF-B882-DEF76E38E484}.Release|ARM.ActiveCfg = Release|ARM - {F8A1FE5A-DC8A-49DF-B882-DEF76E38E484}.Release|ARM.Build.0 = Release|ARM {F8A1FE5A-DC8A-49DF-B882-DEF76E38E484}.Release|ARM64.ActiveCfg = Release|ARM64 {F8A1FE5A-DC8A-49DF-B882-DEF76E38E484}.Release|ARM64.Build.0 = Release|ARM64 {F8A1FE5A-DC8A-49DF-B882-DEF76E38E484}.Release|x64.ActiveCfg = Release|x64 {F8A1FE5A-DC8A-49DF-B882-DEF76E38E484}.Release|x64.Build.0 = Release|x64 {F8A1FE5A-DC8A-49DF-B882-DEF76E38E484}.Release|x86.ActiveCfg = Release|Win32 {F8A1FE5A-DC8A-49DF-B882-DEF76E38E484}.Release|x86.Build.0 = Release|Win32 - {B68C61C6-4699-41E6-A158-EA1BE029E7A0}.Debug|ARM.ActiveCfg = Debug|ARM - {B68C61C6-4699-41E6-A158-EA1BE029E7A0}.Debug|ARM.Build.0 = Debug|ARM {B68C61C6-4699-41E6-A158-EA1BE029E7A0}.Debug|ARM64.ActiveCfg = Debug|ARM64 {B68C61C6-4699-41E6-A158-EA1BE029E7A0}.Debug|ARM64.Build.0 = Debug|ARM64 {B68C61C6-4699-41E6-A158-EA1BE029E7A0}.Debug|x64.ActiveCfg = Debug|x64 {B68C61C6-4699-41E6-A158-EA1BE029E7A0}.Debug|x64.Build.0 = Debug|x64 {B68C61C6-4699-41E6-A158-EA1BE029E7A0}.Debug|x86.ActiveCfg = Debug|Win32 {B68C61C6-4699-41E6-A158-EA1BE029E7A0}.Debug|x86.Build.0 = Debug|Win32 - {B68C61C6-4699-41E6-A158-EA1BE029E7A0}.Release|ARM.ActiveCfg = Release|ARM - {B68C61C6-4699-41E6-A158-EA1BE029E7A0}.Release|ARM.Build.0 = Release|ARM {B68C61C6-4699-41E6-A158-EA1BE029E7A0}.Release|ARM64.ActiveCfg = Release|ARM64 {B68C61C6-4699-41E6-A158-EA1BE029E7A0}.Release|ARM64.Build.0 = Release|ARM64 {B68C61C6-4699-41E6-A158-EA1BE029E7A0}.Release|x64.ActiveCfg = Release|x64 {B68C61C6-4699-41E6-A158-EA1BE029E7A0}.Release|x64.Build.0 = Release|x64 {B68C61C6-4699-41E6-A158-EA1BE029E7A0}.Release|x86.ActiveCfg = Release|Win32 {B68C61C6-4699-41E6-A158-EA1BE029E7A0}.Release|x86.Build.0 = Release|Win32 - {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}.Debug|ARM.ActiveCfg = Debug|ARM - {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}.Debug|ARM.Build.0 = Debug|ARM {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}.Debug|ARM64.ActiveCfg = Debug|ARM64 {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}.Debug|ARM64.Build.0 = Debug|ARM64 {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}.Debug|x64.ActiveCfg = Debug|x64 {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}.Debug|x64.Build.0 = Debug|x64 {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}.Debug|x86.ActiveCfg = Debug|Win32 {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}.Debug|x86.Build.0 = Debug|Win32 - {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}.Release|ARM.ActiveCfg = Release|ARM - {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}.Release|ARM.Build.0 = Release|ARM {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}.Release|ARM64.ActiveCfg = Release|ARM64 {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}.Release|ARM64.Build.0 = Release|ARM64 {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}.Release|x64.ActiveCfg = Release|x64 {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}.Release|x64.Build.0 = Release|x64 {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}.Release|x86.ActiveCfg = Release|Win32 {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074}.Release|x86.Build.0 = Release|Win32 - {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}.Debug|ARM.ActiveCfg = Debug|ARM - {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}.Debug|ARM.Build.0 = Debug|ARM {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}.Debug|ARM64.ActiveCfg = Debug|ARM64 {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}.Debug|ARM64.Build.0 = Debug|ARM64 {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}.Debug|x64.ActiveCfg = Debug|x64 {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}.Debug|x64.Build.0 = Debug|x64 {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}.Debug|x86.ActiveCfg = Debug|Win32 {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}.Debug|x86.Build.0 = Debug|Win32 - {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}.Release|ARM.ActiveCfg = Release|ARM - {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}.Release|ARM.Build.0 = Release|ARM {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}.Release|ARM64.ActiveCfg = Release|ARM64 {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}.Release|ARM64.Build.0 = Release|ARM64 {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}.Release|x64.ActiveCfg = Release|x64 {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}.Release|x64.Build.0 = Release|x64 {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}.Release|x86.ActiveCfg = Release|Win32 {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459}.Release|x86.Build.0 = Release|Win32 - {E893622C-47DE-4F83-B422-0A26711590A4}.Debug|ARM.ActiveCfg = Debug|ARM - {E893622C-47DE-4F83-B422-0A26711590A4}.Debug|ARM.Build.0 = Debug|ARM {E893622C-47DE-4F83-B422-0A26711590A4}.Debug|ARM64.ActiveCfg = Debug|ARM64 {E893622C-47DE-4F83-B422-0A26711590A4}.Debug|ARM64.Build.0 = Debug|ARM64 {E893622C-47DE-4F83-B422-0A26711590A4}.Debug|x64.ActiveCfg = Debug|x64 {E893622C-47DE-4F83-B422-0A26711590A4}.Debug|x64.Build.0 = Debug|x64 {E893622C-47DE-4F83-B422-0A26711590A4}.Debug|x86.ActiveCfg = Debug|Win32 {E893622C-47DE-4F83-B422-0A26711590A4}.Debug|x86.Build.0 = Debug|Win32 - {E893622C-47DE-4F83-B422-0A26711590A4}.Release|ARM.ActiveCfg = Release|ARM - {E893622C-47DE-4F83-B422-0A26711590A4}.Release|ARM.Build.0 = Release|ARM {E893622C-47DE-4F83-B422-0A26711590A4}.Release|ARM64.ActiveCfg = Release|ARM64 {E893622C-47DE-4F83-B422-0A26711590A4}.Release|ARM64.Build.0 = Release|ARM64 {E893622C-47DE-4F83-B422-0A26711590A4}.Release|x64.ActiveCfg = Release|x64 {E893622C-47DE-4F83-B422-0A26711590A4}.Release|x64.Build.0 = Release|x64 {E893622C-47DE-4F83-B422-0A26711590A4}.Release|x86.ActiveCfg = Release|Win32 {E893622C-47DE-4F83-B422-0A26711590A4}.Release|x86.Build.0 = Release|Win32 - {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}.Debug|ARM.ActiveCfg = Debug|ARM - {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}.Debug|ARM.Build.0 = Debug|ARM {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}.Debug|ARM64.ActiveCfg = Debug|ARM64 {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}.Debug|ARM64.Build.0 = Debug|ARM64 {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}.Debug|x64.ActiveCfg = Debug|x64 {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}.Debug|x64.Build.0 = Debug|x64 {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}.Debug|x86.ActiveCfg = Debug|Win32 {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}.Debug|x86.Build.0 = Debug|Win32 - {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}.Release|ARM.ActiveCfg = Release|ARM - {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}.Release|ARM.Build.0 = Release|ARM {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}.Release|ARM64.ActiveCfg = Release|ARM64 {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}.Release|ARM64.Build.0 = Release|ARM64 {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}.Release|x64.ActiveCfg = Release|x64 {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}.Release|x64.Build.0 = Release|x64 {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}.Release|x86.ActiveCfg = Release|Win32 {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3}.Release|x86.Build.0 = Release|Win32 - {08C40663-B6A3-481E-8755-AE32BAD99501}.Debug|ARM.ActiveCfg = Debug|ARM - {08C40663-B6A3-481E-8755-AE32BAD99501}.Debug|ARM.Build.0 = Debug|ARM {08C40663-B6A3-481E-8755-AE32BAD99501}.Debug|ARM64.ActiveCfg = Debug|ARM64 {08C40663-B6A3-481E-8755-AE32BAD99501}.Debug|ARM64.Build.0 = Debug|ARM64 {08C40663-B6A3-481E-8755-AE32BAD99501}.Debug|x64.ActiveCfg = Debug|x64 {08C40663-B6A3-481E-8755-AE32BAD99501}.Debug|x64.Build.0 = Debug|x64 {08C40663-B6A3-481E-8755-AE32BAD99501}.Debug|x86.ActiveCfg = Debug|Win32 {08C40663-B6A3-481E-8755-AE32BAD99501}.Debug|x86.Build.0 = Debug|Win32 - {08C40663-B6A3-481E-8755-AE32BAD99501}.Release|ARM.ActiveCfg = Release|ARM - {08C40663-B6A3-481E-8755-AE32BAD99501}.Release|ARM.Build.0 = Release|ARM {08C40663-B6A3-481E-8755-AE32BAD99501}.Release|ARM64.ActiveCfg = Release|ARM64 {08C40663-B6A3-481E-8755-AE32BAD99501}.Release|ARM64.Build.0 = Release|ARM64 {08C40663-B6A3-481E-8755-AE32BAD99501}.Release|x64.ActiveCfg = Release|x64 {08C40663-B6A3-481E-8755-AE32BAD99501}.Release|x64.Build.0 = Release|x64 {08C40663-B6A3-481E-8755-AE32BAD99501}.Release|x86.ActiveCfg = Release|Win32 {08C40663-B6A3-481E-8755-AE32BAD99501}.Release|x86.Build.0 = Release|Win32 - {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Debug|ARM.ActiveCfg = Debug|ARM - {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Debug|ARM.Build.0 = Debug|ARM - {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Debug|ARM64.ActiveCfg = Debug|ARM64 - {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Debug|ARM64.Build.0 = Debug|ARM64 - {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Debug|x64.ActiveCfg = Debug|x64 - {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Debug|x64.Build.0 = Debug|x64 - {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Debug|x86.ActiveCfg = Debug|Win32 - {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Debug|x86.Build.0 = Debug|Win32 - {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Release|ARM.ActiveCfg = Release|ARM - {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Release|ARM.Build.0 = Release|ARM - {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Release|ARM64.ActiveCfg = Release|ARM64 - {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Release|ARM64.Build.0 = Release|ARM64 - {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Release|x64.ActiveCfg = Release|x64 - {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Release|x64.Build.0 = Release|x64 - {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Release|x86.ActiveCfg = Release|Win32 - {2EF696B9-7F4A-410F-AE5C-5301565C0F08}.Release|x86.Build.0 = Release|Win32 + {5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA}.Debug|ARM64.Build.0 = Debug|ARM64 + {5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA}.Debug|x64.ActiveCfg = Debug|x64 + {5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA}.Debug|x64.Build.0 = Debug|x64 + {5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA}.Debug|x86.ActiveCfg = Debug|Win32 + {5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA}.Debug|x86.Build.0 = Debug|Win32 + {5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA}.Release|ARM64.ActiveCfg = Release|ARM64 + {5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA}.Release|ARM64.Build.0 = Release|ARM64 + {5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA}.Release|x64.ActiveCfg = Release|x64 + {5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA}.Release|x64.Build.0 = Release|x64 + {5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA}.Release|x86.ActiveCfg = Release|Win32 + {5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA}.Release|x86.Build.0 = Release|Win32 + {D4C8F881-84D5-4A7B-8BDE-AB4E34A05374}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {D4C8F881-84D5-4A7B-8BDE-AB4E34A05374}.Debug|ARM64.Build.0 = Debug|ARM64 + {D4C8F881-84D5-4A7B-8BDE-AB4E34A05374}.Debug|x64.ActiveCfg = Debug|x64 + {D4C8F881-84D5-4A7B-8BDE-AB4E34A05374}.Debug|x64.Build.0 = Debug|x64 + {D4C8F881-84D5-4A7B-8BDE-AB4E34A05374}.Debug|x86.ActiveCfg = Debug|Win32 + {D4C8F881-84D5-4A7B-8BDE-AB4E34A05374}.Debug|x86.Build.0 = Debug|Win32 + {D4C8F881-84D5-4A7B-8BDE-AB4E34A05374}.Release|ARM64.ActiveCfg = Release|ARM64 + {D4C8F881-84D5-4A7B-8BDE-AB4E34A05374}.Release|ARM64.Build.0 = Release|ARM64 + {D4C8F881-84D5-4A7B-8BDE-AB4E34A05374}.Release|x64.ActiveCfg = Release|x64 + {D4C8F881-84D5-4A7B-8BDE-AB4E34A05374}.Release|x64.Build.0 = Release|x64 + {D4C8F881-84D5-4A7B-8BDE-AB4E34A05374}.Release|x86.ActiveCfg = Release|Win32 + {D4C8F881-84D5-4A7B-8BDE-AB4E34A05374}.Release|x86.Build.0 = Release|Win32 + {9E392830-805A-4AAF-932D-C493143EFACA}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {9E392830-805A-4AAF-932D-C493143EFACA}.Debug|ARM64.Build.0 = Debug|ARM64 + {9E392830-805A-4AAF-932D-C493143EFACA}.Debug|x64.ActiveCfg = Debug|x64 + {9E392830-805A-4AAF-932D-C493143EFACA}.Debug|x64.Build.0 = Debug|x64 + {9E392830-805A-4AAF-932D-C493143EFACA}.Debug|x86.ActiveCfg = Debug|Win32 + {9E392830-805A-4AAF-932D-C493143EFACA}.Debug|x86.Build.0 = Debug|Win32 + {9E392830-805A-4AAF-932D-C493143EFACA}.Release|ARM64.ActiveCfg = Release|ARM64 + {9E392830-805A-4AAF-932D-C493143EFACA}.Release|ARM64.Build.0 = Release|ARM64 + {9E392830-805A-4AAF-932D-C493143EFACA}.Release|x64.ActiveCfg = Release|x64 + {9E392830-805A-4AAF-932D-C493143EFACA}.Release|x64.Build.0 = Release|x64 + {9E392830-805A-4AAF-932D-C493143EFACA}.Release|x86.ActiveCfg = Release|Win32 + {9E392830-805A-4AAF-932D-C493143EFACA}.Release|x86.Build.0 = Release|Win32 + {B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Debug|ARM64.Build.0 = Debug|ARM64 + {B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Debug|x64.ActiveCfg = Debug|x64 + {B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Debug|x64.Build.0 = Debug|x64 + {B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Debug|x86.ActiveCfg = Debug|Win32 + {B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Debug|x86.Build.0 = Debug|Win32 + {B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Release|ARM64.ActiveCfg = Release|ARM64 + {B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Release|ARM64.Build.0 = Release|ARM64 + {B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Release|x64.ActiveCfg = Release|x64 + {B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Release|x64.Build.0 = Release|x64 + {B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Release|x86.ActiveCfg = Release|Win32 + {B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -456,7 +449,10 @@ Global {303CC0FE-7D66-4F9F-B7A1-0AF7F9359074} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0} {D48A96C2-8512-4CC3-B6E4-7CFF07ED8ED3} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0} {08C40663-B6A3-481E-8755-AE32BAD99501} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0} - {2EF696B9-7F4A-410F-AE5C-5301565C0F08} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0} + {5FF6CD6C-515A-4D55-97B6-62AD9BCB77EA} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0} + {D4C8F881-84D5-4A7B-8BDE-AB4E34A05374} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0} + {9E392830-805A-4AAF-932D-C493143EFACA} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0} + {B8E3A5CE-4E91-4F27-9B02-E0CAF7E10D72} = {3C7EA5F8-6E8C-4376-B499-2CAF596384B0} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {2783B8FD-EA3B-4D6B-9F81-662D289E02AA} diff --git a/cppwinrt/app.manifest b/cppwinrt/app.manifest new file mode 100644 index 000000000..69b366b27 --- /dev/null +++ b/cppwinrt/app.manifest @@ -0,0 +1,9 @@ + + + + + true + UTF-8 + + + \ No newline at end of file diff --git a/cppwinrt/cmd_reader.h b/cppwinrt/cmd_reader.h index 3dee748a3..e2787b7d4 100644 --- a/cppwinrt/cmd_reader.h +++ b/cppwinrt/cmd_reader.h @@ -3,7 +3,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -12,12 +14,16 @@ #include #include #include -#include + +#if defined(_WIN32) || defined(_WIN64) +#include #include -#include +#include +#endif namespace cppwinrt { +#if defined(_WIN32) || defined(_WIN64) struct registry_key { HKEY handle{}; @@ -71,17 +77,31 @@ namespace cppwinrt } } + enum class xml_requirement + { + required = 0, + optional + }; + inline void add_files_from_xml( std::set& files, std::string const& sdk_version, std::filesystem::path const& xml_path, - std::filesystem::path const& sdk_path) + std::filesystem::path const& sdk_path, + xml_requirement xml_path_requirement) { com_ptr stream; - check_xml(SHCreateStreamOnFileW( + auto streamResult = SHCreateStreamOnFileW( xml_path.c_str(), - STGM_READ, &stream.ptr)); + STGM_READ, &stream.ptr); + if (xml_path_requirement == xml_requirement::optional && + (streamResult == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) || + streamResult == HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND))) + { + return; + } + check_xml(streamResult); com_ptr reader; @@ -137,7 +157,9 @@ namespace cppwinrt HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0, - KEY_READ, + // https://task.ms/29349404 - The SDK sometimes stores the 64 bit location into KitsRoot10 which is wrong, + // this breaks 64-bit cppwinrt.exe, so work around this by forcing to use the WoW64 hive. + KEY_READ | KEY_WOW64_32KEY, &key)) { throw std::invalid_argument("Could not find the Windows SDK in the registry"); @@ -179,11 +201,10 @@ namespace cppwinrt inline std::string get_module_path() { std::string path(100, '?'); - DWORD actual_size{}; while (true) { - actual_size = GetModuleFileNameA(nullptr, path.data(), 1 + static_cast(path.size())); + DWORD actual_size = GetModuleFileNameA(nullptr, path.data(), 1 + static_cast(path.size())); if (actual_size < 1 + path.size()) { @@ -217,12 +238,12 @@ namespace cppwinrt } auto key = open_sdk(); - uint32_t index{}; + std::uint32_t index{}; std::array subkey; std::array version_parts{}; std::string result; - while (0 == RegEnumKeyA(key.handle, index++, subkey.data(), static_cast(subkey.size()))) + while (0 == RegEnumKeyA(key.handle, index++, subkey.data(), static_cast(subkey.size()))) { if (!std::regex_match(subkey.data(), match, rx)) { @@ -238,7 +259,7 @@ namespace cppwinrt char* next_part = subkey.data(); bool force_newer = false; - for (size_t i = 0; ; ++i) + for (std::size_t i = 0; ; ++i) { auto version_part = strtoul(next_part, &next_part, 10); @@ -276,6 +297,7 @@ namespace cppwinrt return result; } +#endif /* defined(_WIN32) || defined(_WIN64) */ [[noreturn]] inline void throw_invalid(std::string const& message) { @@ -291,19 +313,19 @@ namespace cppwinrt struct option { - static constexpr uint32_t no_min = 0; - static constexpr uint32_t no_max = UINT_MAX; + static constexpr std::uint32_t no_min = 0; + static constexpr std::uint32_t no_max = (std::numeric_limits::max)(); std::string_view name; - uint32_t min{ no_min }; - uint32_t max{ no_max }; + std::uint32_t min{ no_min }; + std::uint32_t max{ no_max }; std::string_view arg{}; std::string_view desc{}; }; struct reader { - template + template reader(C const argc, V const argv, const option(& options)[numOptions]) { #ifdef _DEBUG @@ -425,13 +447,17 @@ namespace cppwinrt } if (path == "local") { +#if defined(_WIN32) || defined(_WIN64) std::array local{}; #ifdef _WIN64 - ExpandEnvironmentStringsA("%windir%\\System32\\WinMetadata", local.data(), static_cast(local.size())); + ExpandEnvironmentStringsA("%windir%\\System32\\WinMetadata", local.data(), static_cast(local.size())); #else - ExpandEnvironmentStringsA("%windir%\\SysNative\\WinMetadata", local.data(), static_cast(local.size())); + ExpandEnvironmentStringsA("%windir%\\SysNative\\WinMetadata", local.data(), static_cast(local.size())); #endif add_directory(local.data()); +#else /* defined(_WIN32) || defined(_WIN64) */ + throw_invalid("Spec '", path, "' not supported outside of Windows"); +#endif /* defined(_WIN32) || defined(_WIN64) */ continue; } @@ -439,7 +465,11 @@ namespace cppwinrt if (path == "sdk" || path == "sdk+") { +#if defined(_WIN32) || defined(_WIN64) sdk_version = get_sdk_version(); +#else /* defined(_WIN32) || defined(_WIN64) */ + throw_invalid("Spec '", path, "' not supported outside of Windows"); +#endif /* defined(_WIN32) || defined(_WIN64) */ } else { @@ -454,13 +484,14 @@ namespace cppwinrt if (!sdk_version.empty()) { +#if defined(_WIN32) || defined(_WIN64) auto sdk_path = get_sdk_path(); auto xml_path = sdk_path; xml_path /= L"Platforms\\UAP"; xml_path /= sdk_version; xml_path /= L"Platform.xml"; - add_files_from_xml(files, sdk_version, xml_path, sdk_path); + add_files_from_xml(files, sdk_version, xml_path, sdk_path, xml_requirement::required); if (path.back() != '+') { @@ -472,8 +503,12 @@ namespace cppwinrt xml_path = item.path() / sdk_version; xml_path /= L"SDKManifest.xml"; - add_files_from_xml(files, sdk_version, xml_path, sdk_path); + // Not all Extension SDKs include an SDKManifest.xml file; ignore those which do not (e.g. WindowsIoT). + add_files_from_xml(files, sdk_version, xml_path, sdk_path, xml_requirement::optional); } +#else /* defined(_WIN32) || defined(_WIN64) */ + throw_invalid("Spec '", path, "' not supported outside of Windows"); +#endif /* defined(_WIN32) || defined(_WIN64) */ continue; } @@ -515,7 +550,11 @@ namespace cppwinrt template void extract_option(std::string_view arg, O const& options, L& last) { - if (arg[0] == '-' || arg[0] == '/') + if (arg[0] == '-' +#if defined(_WIN32) || defined(_WIN64) + || arg[0] == '/' +#endif + ) { arg.remove_prefix(1); last = find(options, arg); @@ -548,7 +587,7 @@ namespace cppwinrt std::filesystem::path response_path{ std::string{ arg } }; std::string extension = response_path.extension().generic_string(); std::transform(extension.begin(), extension.end(), extension.begin(), - [](auto c) { return static_cast(::tolower(c)); }); + [](auto c) { return static_cast(std::tolower(c)); }); // Check if misuse of @ prefix, so if directory or metadata file instead of response file. if (is_directory(response_path) || extension == ".winmd") @@ -559,12 +598,12 @@ namespace cppwinrt std::ifstream response_file(absolute(response_path)); while (getline(response_file, line_buf)) { - size_t argc = 0; + std::size_t argc = 0; std::vector argv; parse_command_line(line_buf.data(), argv, &argc); - for (size_t i = 0; i < argc; i++) + for (std::size_t i = 0; i < argc; i++) { extract_option(argv[i], options, last); } @@ -572,7 +611,7 @@ namespace cppwinrt } template - static void parse_command_line(Character* cmdstart, std::vector& argv, size_t* argument_count) + static void parse_command_line(Character* cmdstart, std::vector& argv, std::size_t* argument_count) { std::string arg; @@ -586,7 +625,7 @@ namespace cppwinrt first_arg = true; *argument_count = 0; - for (;;) + while (true) { if (*p) { @@ -604,7 +643,7 @@ namespace cppwinrt if (*p == '\0') break; - for (;;) + while (true) { copy_character = true; diff --git a/cppwinrt/code_writers.h b/cppwinrt/code_writers.h index ee082f717..a9aa5bc78 100644 --- a/cppwinrt/code_writers.h +++ b/cppwinrt/code_writers.h @@ -2,16 +2,30 @@ namespace cppwinrt { + struct finish_with + { + writer& w; + std::function finisher; + + finish_with(writer& w, std::function finisher) : w(w), finisher(std::move(finisher)) {} + finish_with(finish_with const&)= delete; + void operator=(finish_with const&) = delete; + + ~finish_with() { finisher(w); } + }; + + static void write_nothing(writer&) + { + } + static void write_preamble(writer& w) { if (settings.license) { w.write(R"(// C++/WinRT v% -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -)", CPPWINRT_VERSION_STRING); +% +)", CPPWINRT_VERSION_STRING, settings.license_template); } else { @@ -21,12 +35,42 @@ namespace cppwinrt } } + static void write_endif(writer& w, std::string_view macro = {}) + { + if (macro.empty()) + { + w.write("#endif\n"); + } + else + { + w.write("#endif // %\n", macro); + } + } + + // When modules are enabled, wraps a block of #include directives in + // #ifndef WINRT_IMPL_BUILD_MODULE ... #endif so that in module builds (where + // WINRT_IMPL_BUILD_MODULE is defined in the global module fragment), textual + // includes are suppressed — dependencies come via import instead. + [[nodiscard]] static finish_with wrap_module_aware_includes_guard(writer& w, bool modules_enabled) + { + if (modules_enabled) + { + w.write("#ifndef WINRT_IMPL_BUILD_MODULE\n"); + return { w, [](writer& w) { write_endif(w, "WINRT_IMPL_BUILD_MODULE"); } }; + } + else + { + return { w, write_nothing }; + } + } + static void write_version_assert(writer& w) { w.write_root_include("base"); auto format = R"(static_assert(winrt::check_version(CPPWINRT_VERSION, "%"), "Mismatched C++/WinRT headers."); +#define CPPWINRT_VERSION "%" )"; - w.write(format, CPPWINRT_VERSION_STRING); + w.write(format, CPPWINRT_VERSION_STRING, CPPWINRT_VERSION_STRING); } static void write_include_guard(writer& w) @@ -37,8 +81,15 @@ namespace cppwinrt w.write(format); } + static void write_close_file_guard(writer& w) + { + write_endif(w); + } + static void write_open_file_guard(writer& w, std::string_view const& file_name, char impl = 0) { + write_include_guard(w); + std::string mangled_name; for (auto&& c : file_name) @@ -59,20 +110,48 @@ namespace cppwinrt w.write(format, mangled_name, mangled_name); } - static void write_lean_and_mean(writer& w) + template + [[nodiscard]] static finish_with wrap_open_file_guard(writer& w, Args&&... args) { - auto format = R"(#ifndef WINRT_LEAN_AND_MEAN + write_open_file_guard(w, std::forward(args)...); + return { w, write_close_file_guard }; + } + + [[nodiscard]] static finish_with wrap_lean_and_mean(writer& w, bool is_lean_and_mean = true) + { + if (is_lean_and_mean) + { + auto format = R"(#ifndef WINRT_LEAN_AND_MEAN )"; - w.write(format); + w.write(format); + + return { w, [](writer& w) { write_endif(w, "WINRT_LEAN_AND_MEAN"); } }; + } + else + { + return { w, write_nothing }; + } } - static void write_endif(writer& w) + [[nodiscard]] static finish_with wrap_ifdef(writer& w, std::string_view macro) { - auto format = R"(#endif + auto format = R"(#ifdef % )"; - w.write(format); + w.write(format, macro); + + return { w, [macro = std::string(macro)](writer& w) { write_endif(w, macro); } }; + } + + [[nodiscard]] static finish_with wrap_ifndef(writer& w, std::string_view macro) + { + auto format = R"(#ifndef % +)"; + + w.write(format, macro); + + return { w, [macro = std::string(macro)](writer& w) { write_endif(w, macro); } }; } static void write_parent_depends(writer& w, cache const& c, std::string_view const& type_namespace) @@ -108,37 +187,54 @@ namespace cppwinrt } } - static void write_impl_namespace(writer& w) + static void write_close_namespace(writer& w) { - auto format = R"(namespace winrt::impl + auto format = R"(} +)"; + + w.write(format); + } + + [[nodiscard]] static finish_with wrap_impl_namespace(writer& w) + { + auto format = R"(WINRT_EXPORT namespace winrt::impl { )"; w.write(format); + + return { w, write_close_namespace }; } - static void write_std_namespace(writer& w) + [[nodiscard]] static finish_with wrap_std_namespace(writer& w) { w.write(R"(namespace std { )"); + + return { w, write_close_namespace }; } - static void write_type_namespace(writer& w, std::string_view const& ns) + [[nodiscard]] static finish_with wrap_type_namespace(writer& w, std::string_view const& ns) { auto format = R"(WINRT_EXPORT namespace winrt::@ { )"; w.write(format, ns); + + return { w, write_close_namespace }; } - static void write_close_namespace(writer& w) + [[nodiscard]] static finish_with wrap_type_namespace_without_export(writer& w, std::string_view const& ns) { - auto format = R"(} + auto format = R"(namespace winrt::@ +{ )"; - w.write(format); + w.write(format, ns); + + return { w, write_close_namespace }; } static void write_enum_field(writer& w, Field const& field) @@ -292,7 +388,7 @@ namespace cppwinrt return; } - auto format = R"( template <%> struct __declspec(empty_bases) %; + auto format = R"( template <%> struct WINRT_IMPL_EMPTY_BASES %; )"; w.write(format, @@ -305,17 +401,17 @@ namespace cppwinrt using std::get; w.write_printf("0x%08X,0x%04X,0x%04X,{ 0x%02X,0x%02X,0x%02X,0x%02X,0x%02X,0x%02X,0x%02X,0x%02X }", - get(get(args[0].value).value), - get(get(args[1].value).value), - get(get(args[2].value).value), - get(get(args[3].value).value), - get(get(args[4].value).value), - get(get(args[5].value).value), - get(get(args[6].value).value), - get(get(args[7].value).value), - get(get(args[8].value).value), - get(get(args[9].value).value), - get(get(args[10].value).value)); + get(get(args[0].value).value), + get(get(args[1].value).value), + get(get(args[2].value).value), + get(get(args[3].value).value), + get(get(args[4].value).value), + get(get(args[5].value).value), + get(get(args[6].value).value), + get(get(args[7].value).value), + get(get(args[8].value).value), + get(get(args[9].value).value), + get(get(args[10].value).value)); } static void write_guid_comment(writer& w, std::vector const& args) @@ -323,17 +419,17 @@ namespace cppwinrt using std::get; w.write_printf("%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X", - get(get(args[0].value).value), - get(get(args[1].value).value), - get(get(args[2].value).value), - get(get(args[3].value).value), - get(get(args[4].value).value), - get(get(args[5].value).value), - get(get(args[6].value).value), - get(get(args[7].value).value), - get(get(args[8].value).value), - get(get(args[9].value).value), - get(get(args[10].value).value)); + get(get(args[0].value).value), + get(get(args[1].value).value), + get(get(args[2].value).value), + get(get(args[3].value).value), + get(get(args[4].value).value), + get(get(args[5].value).value), + get(get(args[6].value).value), + get(get(args[7].value).value), + get(get(args[8].value).value), + get(get(args[9].value).value), + get(get(args[10].value).value)); } static void write_category(writer& w, TypeDef const& type, std::string_view const& category) @@ -361,7 +457,7 @@ namespace cppwinrt static void write_generic_names(writer& w, std::pair const& params) { - bool first{ true }; + bool first = true; for (auto&& param : params) { @@ -494,7 +590,7 @@ namespace cppwinrt static void write_abi_params(writer& w, method_signature const& method_signature) { - w.abi_types = true; + auto abi_guard = w.push_abi_types(true); separator s{ w }; for (auto&& [param, param_signature] : method_signature.params()) @@ -507,15 +603,15 @@ namespace cppwinrt if (param.Flags().In()) { - format = "uint32_t%, %"; + format = "std::uint32_t%, %"; } else if (param_signature->ByRef()) { - format = "uint32_t*%, %*"; + format = "std::uint32_t*%, %*"; } else { - format = "uint32_t%, %"; + format = "std::uint32_t%, %"; } w.write(format, bind(param), bind(param_signature->Type())); @@ -551,7 +647,7 @@ namespace cppwinrt if (type.is_szarray()) { - w.write("uint32_t* __%Size, %**", method_signature.return_param_name(), type); + w.write("std::uint32_t* __%Size, %**", method_signature.return_param_name(), type); } else { @@ -565,10 +661,15 @@ namespace cppwinrt } } - static void write_abi_args(writer& w, method_signature const& method_signature) + static void write_abi_args(writer& w, method_signature const& method_signature, bool start_comma) { separator s{ w }; + if (start_comma) + { + s(); + } + for (auto&& [param, param_signature] : method_signature.params()) { s(); @@ -689,7 +790,7 @@ namespace cppwinrt break; } - auto format = R"( virtual int32_t __stdcall %(%) noexcept = 0; + auto format = R"( virtual std::int32_t __stdcall %(%) noexcept = 0; )"; for (auto&& method : info.type.MethodList()) @@ -704,13 +805,12 @@ namespace cppwinrt { auto generics = type.GenericParam(); auto guard{ w.push_generic_params(generics) }; - w.abi_types = false; if (empty(generics)) { auto format = R"( template <> struct abi<%> { - struct __declspec(novtable) type : inspectable_abi + struct WINRT_IMPL_ABI_DECL type : inspectable_abi { )"; @@ -720,7 +820,7 @@ namespace cppwinrt { auto format = R"( template <%> struct abi<%> { - struct __declspec(novtable) type : inspectable_abi + struct WINRT_IMPL_ABI_DECL type : inspectable_abi { )"; @@ -730,9 +830,10 @@ namespace cppwinrt } - auto format = R"( virtual int32_t __stdcall %(%) noexcept = 0; + auto format = R"( virtual std::int32_t __stdcall %(%) noexcept = 0; )"; + auto abi_guard = w.push_abi_types(true); for (auto&& method : type.MethodList()) { try @@ -760,9 +861,9 @@ namespace cppwinrt { auto format = R"( template <%> struct abi<%> { - struct __declspec(novtable) type : unknown_abi + struct WINRT_IMPL_ABI_DECL type : unknown_abi { - virtual int32_t __stdcall Invoke(%) noexcept = 0; + virtual std::int32_t __stdcall Invoke(%) noexcept = 0; }; }; )"; @@ -771,7 +872,6 @@ namespace cppwinrt auto guard{ w.push_generic_params(generics) }; auto method = get_delegate_method(type); method_signature signature{ method }; - w.abi_types = false; w.write(format, bind(generics), @@ -786,7 +886,7 @@ namespace cppwinrt static void write_struct_abi(writer& w, TypeDef const& type) { - w.abi_types = true; + auto abi_guard = w.push_abi_types(true); auto format = R"( struct struct_% { @@ -933,13 +1033,12 @@ namespace cppwinrt static void write_consume_declaration(writer& w, MethodDef const& method) { method_signature signature{ method }; - w.async_types = signature.is_async(); + auto async_types_guard = w.push_async_types(signature.is_async()); auto method_name = get_name(method); auto type = method.Parent(); - w.write(" %WINRT_IMPL_AUTO(%) %(%) const%;\n", + w.write(" %auto %(%) const%;\n", is_get_overload(method) ? "[[nodiscard]] " : "", - signature.return_signature(), method_name, bind(signature), is_noexcept(method) ? " noexcept" : ""); @@ -947,7 +1046,7 @@ namespace cppwinrt if (is_add_overload(method)) { auto format = R"( using %_revoker = impl::event_revoker<%, &impl::abi_t<%>::remove_%>; - [[nodiscard]] %_revoker %(auto_revoke_t, %) const; + [[nodiscard]] auto %(auto_revoke_t, %) const; )"; w.write(format, @@ -956,11 +1055,8 @@ namespace cppwinrt type, method_name, method_name, - method_name, bind(signature)); } - - w.async_types = false; } static void write_fast_consume_declarations(writer& w, TypeDef const& default_interface) @@ -1000,19 +1096,16 @@ namespace cppwinrt if (category == param_category::array_type) { auto format = R"( - uint32_t %_impl_size{}; + std::uint32_t %_impl_size{}; %* %{};)"; - w.abi_types = true; - w.delegate_types = delegate_types; + auto abi_guard = w.push_abi_types(true); + auto delegate_guard = w.push_delegate_types(delegate_types); w.write(format, signature.return_param_name(), signature.return_signature(), signature.return_param_name()); - - w.abi_types = false; - w.delegate_types = false; } else if (category == param_category::object_type || category == param_category::string_type) { @@ -1074,43 +1167,59 @@ namespace cppwinrt { auto method_name = get_name(method); method_signature signature{ method }; - w.async_types = signature.is_async(); + auto async_types_guard = w.push_async_types(signature.is_async()); std::string_view format; if (is_noexcept(method)) { - format = R"( template WINRT_IMPL_AUTO(%) consume_%::%(%) const noexcept + if (is_remove_overload(method)) + { + // we intentionally ignore errors when unregistering event handlers to be consistent with event_revoker + // + // The `noexcept` versions will crash if check_hresult throws but that is no different than previous + // behavior where it would not check the cast result and nullptr crash. At least the exception will terminate + // immediately while preserving the error code and local variables. + format = R"( template auto consume_%::%(%) const noexcept + {% + consume_noexcept_remove_overload<%, D>(static_cast(this), &abi_t<%>::%%);% + } +)"; + } + else + { + format = R"( template auto consume_%::%(%) const noexcept {% - WINRT_VERIFY_(0, WINRT_IMPL_SHIM(%)->%(%));% + consume_noexcept<%, D>(static_cast(this), &abi_t<%>::%%);% } )"; + } } else { - format = R"( template WINRT_IMPL_AUTO(%) consume_%::%(%) const + format = R"( template auto consume_%::%(%) const {% - check_hresult(WINRT_IMPL_SHIM(%)->%(%));% + consume_general<%, D>(static_cast(this), &abi_t<%>::%%);% } )"; } w.write(format, bind(generics), - signature.return_signature(), type_impl_name, bind(generics), method_name, bind(signature), bind(signature, false), type, + type, get_abi_name(method), - bind(signature), + bind(signature, true), bind(signature)); if (is_add_overload(method)) { - format = R"( template typename consume_%::%_revoker consume_%::%(auto_revoke_t, %) const + format = R"( template auto consume_%::%(auto_revoke_t, %) const { return impl::make_event_revoker(this, %(%)); } @@ -1121,40 +1230,26 @@ namespace cppwinrt type_impl_name, bind(generics), method_name, - type_impl_name, - bind(generics), - method_name, bind(signature), method_name, method_name, bind(signature)); } - - w.async_types = false; } static void write_consume_fast_base_definition(writer& w, MethodDef const& method, TypeDef const& class_type, TypeDef const& base_type) { auto method_name = get_name(method); method_signature signature{ method }; - w.async_types = signature.is_async(); + auto async_types_guard = w.push_async_types(signature.is_async()); - // - // Note: this use of a lambda is a workaround for a Visual C++ compiler bug: - // https://developercommunity.visualstudio.com/content/problem/554130/incorrect-code-gen-when-invoking-a-conversion-oper.html - // Once fixed, revert the function body back to this: - // - // return static_cast<% const&>(*this).%(%); - // - - std::string_view format = R"( inline WINRT_IMPL_AUTO(%) %::%(%) const% + std::string_view format = R"( inline auto %::%(%) const% { - return [&](% const& winrt_impl_base) { return winrt_impl_base.%(%); }(*this); + return static_cast<% const&>(*this).%(%); } )"; w.write(format, - signature.return_signature(), class_type.TypeName(), method_name, bind(signature), @@ -1165,15 +1260,13 @@ namespace cppwinrt if (is_add_overload(method)) { - format = R"( inline %::%_revoker %::%(auto_revoke_t, %) const + format = R"( inline auto %::%(auto_revoke_t, %) const { return impl::make_event_revoker(this, %(%)); } )"; w.write(format, - class_type.TypeName(), - method_name, class_type.TypeName(), method_name, bind(signature), @@ -1181,8 +1274,6 @@ namespace cppwinrt method_name, bind(signature)); } - - w.async_types = false; } static void write_consume_definitions(writer& w, TypeDef const& type) @@ -1249,13 +1340,18 @@ namespace cppwinrt static_cast(*this) = nullptr; } - return *this; + return static_cast(*this); } auto operator*() const { return Current(); } + + void operator++(int) + { + ++(*this); + } )"); } else if (type_name == "Windows.Storage.Streams.IBuffer") @@ -1263,10 +1359,22 @@ namespace cppwinrt w.write(R"( auto data() const { - uint8_t* data{}; + std::uint8_t* data{}; static_cast(*this).template as()->Buffer(&data); return data; } +)"); + } + else if (type_name == "Windows.Foundation.IMemoryBufferReference") + { + w.write(R"( + auto data() const + { + std::uint8_t* data{}; + std::uint32_t capacity{}; + check_hresult(static_cast(*this).template as()->GetBuffer(&data, &capacity)); + return data; + } )"); } else if (type_name == "Windows.Foundation.Collections.IIterator`1") @@ -1279,13 +1387,18 @@ namespace cppwinrt static_cast(*this) = nullptr; } - return *this; + return static_cast(*this); } T operator*() const { return Current(); } + + void operator++(int) + { + ++(*this); + } )"); } else if (type_name == "Windows.Foundation.Collections.IKeyValuePair`2") @@ -1305,12 +1418,12 @@ namespace cppwinrt else if (type_name == "Windows.Foundation.Collections.IMapView`2") { w.write(R"( - auto TryLookup(param_type const& key) const noexcept + auto TryLookup(param_type const& key) const { if constexpr (std::is_base_of_v) { V result{ nullptr }; - WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMapView)->Lookup(get_abi(key), put_abi(result)); + impl::check_hresult_allow_bounds(WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMapView)->Lookup(get_abi(key), put_abi(result))); return result; } else @@ -1318,7 +1431,7 @@ namespace cppwinrt std::optional result; V value{ empty_value() }; - if (0 == WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMapView)->Lookup(get_abi(key), put_abi(value))) + if (0 == impl::check_hresult_allow_bounds(WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMapView)->Lookup(get_abi(key), put_abi(value)))) { result = std::move(value); } @@ -1331,12 +1444,12 @@ namespace cppwinrt else if (type_name == "Windows.Foundation.Collections.IMap`2") { w.write(R"( - auto TryLookup(param_type const& key) const noexcept + auto TryLookup(param_type const& key) const { if constexpr (std::is_base_of_v) { V result{ nullptr }; - WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMap)->Lookup(get_abi(key), put_abi(result)); + impl::check_hresult_allow_bounds(WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMap)->Lookup(get_abi(key), put_abi(result))); return result; } else @@ -1344,7 +1457,7 @@ namespace cppwinrt std::optional result; V value{ empty_value() }; - if (0 == WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMap)->Lookup(get_abi(key), put_abi(value))) + if (0 == impl::check_hresult_allow_bounds(WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMap)->Lookup(get_abi(key), put_abi(value)))) { result = std::move(value); } @@ -1352,30 +1465,61 @@ namespace cppwinrt return result; } } + + auto TryRemove(param_type const& key) const + { + return 0 == impl::check_hresult_allow_bounds(WINRT_IMPL_SHIM(Windows::Foundation::Collections::IMap)->Remove(get_abi(key))); + } )"); } else if (type_name == "Windows.Foundation.IAsyncAction") { w.write(R"( auto get() const; + // Synchronously waits without asserting that the calling thread is not an STA. + // Use only when the STA is not presenting UI and blocking is known to be safe. + auto get_unchecked() const; auto wait_for(Windows::Foundation::TimeSpan const& timeout) const; )"); } else if (type_name == "Windows.Foundation.IAsyncOperation`1") { w.write(R"( auto get() const; + // Synchronously waits without asserting that the calling thread is not an STA. + // Use only when the STA is not presenting UI and blocking is known to be safe. + auto get_unchecked() const; auto wait_for(Windows::Foundation::TimeSpan const& timeout) const; )"); } else if (type_name == "Windows.Foundation.IAsyncActionWithProgress`1") { w.write(R"( auto get() const; + // Synchronously waits without asserting that the calling thread is not an STA. + // Use only when the STA is not presenting UI and blocking is known to be safe. + auto get_unchecked() const; auto wait_for(Windows::Foundation::TimeSpan const& timeout) const; )"); } else if (type_name == "Windows.Foundation.IAsyncOperationWithProgress`2") { w.write(R"( auto get() const; + // Synchronously waits without asserting that the calling thread is not an STA. + // Use only when the STA is not presenting UI and blocking is known to be safe. + auto get_unchecked() const; auto wait_for(Windows::Foundation::TimeSpan const& timeout) const; +)"); + } + else if (type_name == "Windows.Foundation.Collections.IIterable`1") + { + w.write(R"( + auto begin() const; + auto end() const; +)"); + } + else if (type_name == "Windows.UI.Xaml.Interop.IBindableIterable") + { + w.write(R"( + auto begin() const; + auto end() const; )"); } } @@ -1387,22 +1531,46 @@ namespace cppwinrt if (type_name == "Windows.Foundation.Collections.IIterator`1") { w.write(R"( + using iterator_concept = std::input_iterator_tag; using iterator_category = std::input_iterator_tag; using value_type = T; - using difference_type = ptrdiff_t; - using pointer = T*; - using reference = T&; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = T; +)"); + } + else if (type_name == "Windows.UI.Xaml.Interop.IBindableIterator") + { + w.write(R"( + using iterator_concept = std::input_iterator_tag; + using iterator_category = std::input_iterator_tag; + using value_type = Windows::Foundation::IInspectable; + using difference_type = std::ptrdiff_t; + using pointer = void; + using reference = Windows::Foundation::IInspectable; )"); } else if (type_name == "Windows.Foundation.IReference`1") { - w.write(R"( IReference(T const& value) : IReference(impl::reference_traits::make(value)) + w.write(R"( IReference(T const& value) : IReference(impl::reference_traits::make(value)) { } - + IReference(std::optional const& value) : IReference(value ? IReference(value.value()) : nullptr) + { + } + operator std::optional() const + { + if (*this) + { + return this->Value(); + } + else + { + return std::nullopt; + } + } private: - - IReference(IInspectable const& value) : IReference(value.as>()) + IReference(IInspectable const& value) : IReference(value.as()) { } )"); @@ -1411,7 +1579,6 @@ namespace cppwinrt static void write_consume(writer& w, TypeDef const& type) { - w.abi_types = false; auto generics = type.GenericParam(); auto guard{ w.push_generic_params(generics) }; auto type_name = type.TypeName(); @@ -1473,7 +1640,6 @@ namespace cppwinrt static void write_produce_params(writer& w, method_signature const& signature) { - w.param_names = true; write_abi_params(w, signature); } @@ -1481,7 +1647,6 @@ namespace cppwinrt static void write_produce_cleanup_param(writer& w, T const& param_signature, std::string_view const& param_name, bool out) { TypeSig const& signature = param_signature.Type(); - w.abi_types = false; bool clear{}; bool optional{}; bool zero{}; @@ -1583,7 +1748,7 @@ namespace cppwinrt else if (optional) { auto format = R"( if (%) *% = nullptr; - Windows::Foundation::IInspectable winrt_impl_%; + winrt::Windows::Foundation::IInspectable winrt_impl_%; )"; w.write(format, param_name, param_name, param_name); @@ -1610,7 +1775,6 @@ namespace cppwinrt static void write_produce_args(writer& w, method_signature const& method_signature) { - w.abi_types = false; separator s{ w }; for (auto&& [param, param_signature] : method_signature.params()) @@ -1689,8 +1853,6 @@ namespace cppwinrt static void write_produce_upcall(writer& w, std::string_view const& upcall, method_signature const& method_signature) { - w.abi_types = false; - if (method_signature.return_signature()) { auto name = method_signature.return_param_name(); @@ -1725,18 +1887,48 @@ namespace cppwinrt { auto param_name = param.Name(); - w.write("\n if (%) *% = detach_abi(winrt_impl_%);", param_name, param_name, param_name); + w.write("\n if (%) *% = detach_abi(winrt_impl_%);", param_name, param_name, param_name); + } + } + } + + static void write_produce_upcall_TryLookup(writer& w, std::string_view const& upcall, method_signature const& method_signature) + { + auto name = method_signature.return_param_name(); + + w.write("auto out_param_val = %(%, trylookup_from_abi);", + upcall, + bind(method_signature)); + w.write(R"( + if (out_param_val.has_value()) + { + *% = detach_from<%>(std::move(*out_param_val)); + } + else + { + return impl::error_out_of_bounds; + } +)", + name, method_signature.return_signature()); + + for (auto&& [param, param_signature] : method_signature.params()) + { + if (param.Flags().Out() && !param_signature->Type().is_szarray() && is_object(param_signature->Type())) + { + auto param_name = param.Name(); + + w.write("\n if (%) *% = detach_abi(winrt_impl_%);", param_name, param_name, param_name); } } } - static void write_produce_method(writer& w, MethodDef const& method) + static void write_produce_method(writer& w, MethodDef const& method, TypeDef const& type) { std::string_view format; if (is_noexcept(method)) { - format = R"( int32_t __stdcall %(%) noexcept final + format = R"( std::int32_t __stdcall %(%) noexcept final { % typename D::abi_guard guard(this->shim()); % @@ -1746,7 +1938,7 @@ namespace cppwinrt } else { - format = R"( int32_t __stdcall %(%) noexcept final try + format = R"( std::int32_t __stdcall %(%) noexcept final try { % typename D::abi_guard guard(this->shim()); % @@ -1757,17 +1949,47 @@ namespace cppwinrt } method_signature signature{ method }; - w.async_types = signature.is_async(); + auto async_types_guard = w.push_async_types(signature.is_async()); std::string upcall = "this->shim()."; - upcall += get_name(method); + auto name = get_name(method); + upcall += name; - w.write(format, - get_abi_name(method), - bind(signature), - bind(signature), - bind(upcall, signature)); - - w.async_types = false; + auto typeName = type.TypeName(); + if (((typeName == "IMapView`2") || (typeName == "IMap`2")) + && (name == "Lookup")) + { + // Special-case IMap*::Lookup to look for a TryLookup here, to avoid extranous throw/originates + std::string tryLookupUpCall = "this->shim().TryLookup"; + format = R"( std::int32_t __stdcall %(%) noexcept final try + { +% typename D::abi_guard guard(this->shim()); + if constexpr (has_TryLookup_v) + { + % + } + else + { + % + } + return 0; + } + catch (...) { return to_hresult(); } +)"; + w.write(format, + get_abi_name(method), + bind(signature), + bind(signature), // clear_abi + bind(tryLookupUpCall, signature), + bind(upcall, signature)); + } + else + { + w.write(format, + get_abi_name(method), + bind(signature), + bind(signature), + bind(upcall, signature)); + } } static void write_fast_produce_methods(writer& w, TypeDef const& default_interface) @@ -1810,7 +2032,7 @@ namespace cppwinrt break; } - w.write_each(info.type.MethodList()); + w.write_each(info.type.MethodList(), info.type); } } @@ -1826,27 +2048,19 @@ namespace cppwinrt auto guard{ w.push_generic_params(generics) }; bool const lean_and_mean = !can_produce(type, c); - if (lean_and_mean) - { - write_lean_and_mean(w); - } + auto wrap = wrap_lean_and_mean(w, lean_and_mean); w.write(format, bind(generics), type, type, - bind_each(type.MethodList()), + bind_each(type.MethodList(), type), bind(type)); - - if (lean_and_mean) - { - write_endif(w); - } } static void write_dispatch_overridable_method(writer& w, MethodDef const& method) { - auto format = R"( auto %(%) + auto format = R"( auto %(%)% { if (auto overridable = this->shim_overridable()) { @@ -1862,6 +2076,7 @@ namespace cppwinrt w.write(format, get_name(method), bind(signature), + is_noexcept(method) ? " noexcept" : "", get_name(method), bind(signature), get_name(method), @@ -1871,7 +2086,7 @@ namespace cppwinrt static void write_dispatch_overridable(writer& w, TypeDef const& class_type) { auto format = R"(template -struct __declspec(empty_bases) produce_dispatch_to_overridable +struct WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable : produce_dispatch_to_overridable_base { %}; @@ -1891,7 +2106,7 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable static void write_interface_override_method(writer& w, MethodDef const& method, std::string_view const& interface_name) { - auto format = R"( template WINRT_IMPL_AUTO(%) %T::%(%) const + auto format = R"( template auto %T::%(%) const% { return shim().template try_as<%>().%(%); } @@ -1901,10 +2116,10 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable auto method_name = get_name(method); w.write(format, - signature.return_signature(), interface_name, method_name, bind(signature), + is_noexcept(method) ? " noexcept" : "", interface_name, method_name, bind(signature)); @@ -1920,7 +2135,7 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable w.write_each(info.type.MethodList(), name); } - }; + } } static void write_class_override_implements(writer& w, get_interfaces_t const& interfaces) @@ -1938,27 +2153,50 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable if (!found) { - w.write(", Windows::Foundation::IInspectable"); + w.write(", winrt::Windows::Foundation::IInspectable"); } } static void write_class_override_requires(writer& w, get_interfaces_t const& interfaces) { - bool found{}; - for (auto&& [name, info] : interfaces) { - if (!info.overridable) + if (!info.overridable && !info.is_protected) { w.write(", %", name); - found = true; } } } + static void write_class_override_protected_requires(writer& w, get_interfaces_t const& interfaces) + { + bool first = true; + + for (auto&& [name, info] : interfaces) + { + if (info.is_protected) + { + if (first) + { + first = false; + w.write(",\n protected impl::require'); + } + } + static void write_class_override_defaults(writer& w, get_interfaces_t const& interfaces) { - bool first{ true }; + bool first = true; for (auto&& [name, info] : interfaces) { @@ -1987,6 +2225,22 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable } } + static void write_class_override_friends(writer& w, get_interfaces_t const& interfaces) + { + for (auto&& [name, info] : interfaces) + { + if (info.is_protected) + { + w.write("\n friend impl::consume_t;", name); + w.write("\n friend impl::require_one;", name); + } + else if (info.overridable) + { + w.write("\n friend impl::produce;", name); + } + } + } + static void write_call_factory(writer& w, TypeDef const& type, TypeDef const& factory) { std::string factory_name; @@ -2088,7 +2342,7 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable D& shim() noexcept { return *static_cast(this); } D const& shim() const noexcept { return *static_cast(this); } public: - using % = winrt::%; + using % = %; % }; )"; @@ -2109,13 +2363,13 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable static void write_class_override_usings(writer& w, get_interfaces_t const& required_interfaces) { - std::map> method_usage; + std::map> method_usage; - for (auto&& [interface_name, info] : required_interfaces) + for (auto&& interface_desc : required_interfaces) { - for (auto&& method : info.type.MethodList()) + for (auto&& method : interface_desc.second.type.MethodList()) { - method_usage[get_name(method)].insert(interface_name); + method_usage[get_name(method)].insert(interface_desc); } } @@ -2126,9 +2380,11 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable continue; } - for (auto&& interface_name : interfaces) + for (auto&& [interface_name, info] : interfaces) { - w.write(" using impl::consume_t::%;\n", + w.write(info.overridable + ? " using %T::%;\n" + : " using impl::consume_t::%;\n", interface_name, method_name); } @@ -2157,10 +2413,10 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable auto format = R"( template struct %T : implements, - impl::require, + impl::require%, impl::base% { - using composable = %; + using composable = %;% protected: %% }; )"; @@ -2172,10 +2428,12 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable type_name, bind(interfaces), bind(interfaces), + bind(interfaces), type_name, bind(type), bind(interfaces), type_name, + bind(interfaces), bind(type, factories), bind(interfaces)); } @@ -2240,18 +2498,21 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable for (auto&& [interface_name, info] : get_interfaces(w, type)) { - if (info.defaulted && !info.base) + if (!info.is_protected && !info.overridable) { - for (auto&& method : info.type.MethodList()) + if (info.defaulted && !info.base) { - method_usage[get_name(method)].insert(default_interface_name); + for (auto&& method : info.type.MethodList()) + { + method_usage[get_name(method)].insert(default_interface_name); + } } - } - else - { - for (auto&& method : info.type.MethodList()) + else { - method_usage[get_name(method)].insert(interface_name); + for (auto&& method : info.type.MethodList()) + { + method_usage[get_name(method)].insert(interface_name); + } } } } @@ -2290,12 +2551,12 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable if (empty(generics)) { - auto format = R"( struct __declspec(empty_bases) % : - Windows::Foundation::IInspectable, + auto format = R"( struct WINRT_IMPL_EMPTY_BASES % : + winrt::Windows::Foundation::IInspectable, impl::consume_t<%>% { %(std::nullptr_t = nullptr) noexcept {} - %(void* ptr, take_ownership_from_abi_t) noexcept : Windows::Foundation::IInspectable(ptr, take_ownership_from_abi) {} + %(void* ptr, take_ownership_from_abi_t) noexcept : winrt::Windows::Foundation::IInspectable(ptr, take_ownership_from_abi) {} %% }; )"; @@ -2313,12 +2574,12 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable type_name = remove_tick(type_name); auto format = R"( template <%> - struct __declspec(empty_bases) % : - Windows::Foundation::IInspectable, + struct WINRT_IMPL_EMPTY_BASES % : + winrt::Windows::Foundation::IInspectable, impl::consume_t<%>% {% %(std::nullptr_t = nullptr) noexcept {} - %(void* ptr, take_ownership_from_abi_t) noexcept : Windows::Foundation::IInspectable(ptr, take_ownership_from_abi) {} + %(void* ptr, take_ownership_from_abi_t) noexcept : winrt::Windows::Foundation::IInspectable(ptr, take_ownership_from_abi) {} %% }; )"; @@ -2351,15 +2612,17 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable w.write(format, bind(generics)); } - auto format = R"( struct % : Windows::Foundation::IUnknown + auto format = R"( struct % : winrt::Windows::Foundation::IUnknown {% %(std::nullptr_t = nullptr) noexcept {} - %(void* ptr, take_ownership_from_abi_t) noexcept : Windows::Foundation::IUnknown(ptr, take_ownership_from_abi) {} + %(void* ptr, take_ownership_from_abi_t) noexcept : winrt::Windows::Foundation::IUnknown(ptr, take_ownership_from_abi) {} template %(L lambda); template %(F* function); template %(O* object, M method); template %(com_ptr&& object, M method); - template %(weak_ref&& object, M method); + template %(weak_ref&& object, LM&& lambda_or_method); + template %(std::shared_ptr&& object, M method); + template %(std::weak_ptr&& object, LM&& lambda_or_method); auto operator()(%) const; }; )"; @@ -2376,6 +2639,8 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable type_name, type_name, type_name, + type_name, + type_name, bind(signature)); } @@ -2385,7 +2650,7 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable { delegate(H&& handler) : implements_delegate<%, H>(std::forward(handler)) {} - int32_t __stdcall Invoke(%) noexcept final try + std::int32_t __stdcall Invoke(%) noexcept final try { % % return 0; @@ -2394,7 +2659,6 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable }; )"; - w.param_names = true; auto generics = type.GenericParam(); auto guard{ w.push_generic_params(generics) }; method_signature signature{ get_delegate_method(type) }; @@ -2434,8 +2698,22 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable %([o = std::move(object), method](auto&&... args) { return ((*o).*(method))(args...); }) { } - template <%> template %<%>::%(weak_ref&& object, M method) : - %([o = std::move(object), method](auto&&... args) { if (auto s = o.get()) { ((*s).*(method))(args...); } }) + template <%> template %<%>::%(weak_ref&& object, LM&& lambda_or_method) : + %([o = std::move(object), lm = std::forward(lambda_or_method)](auto&&... args) { if (auto s = o.get()) { + if constexpr (std::is_member_function_pointer_v) ((*s).*(lm))(args...); + else lm(args...); + } }) + { + } + template <%> template %<%>::%(std::shared_ptr&& object, M method) : + %([o = std::move(object), method](auto&&... args) { return ((*o).*(method))(args...); }) + { + } + template <%> template %<%>::%(std::weak_ptr&& object, LM&& lambda_or_method) : + %([o = std::move(object), lm = std::forward(lambda_or_method)](auto&&... args) { if (auto s = o.lock()) { + if constexpr (std::is_member_function_pointer_v) ((*s).*(lm))(args...); + else lm(args...); + } }) { } template <%> auto %<%>::operator()(%) const @@ -2477,11 +2755,21 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable bind(generics), type_name, bind_list(", ", generics), + type_name, + type_name, + bind(generics), + type_name, + bind_list(", ", generics), + type_name, + type_name, + bind(generics), + type_name, + bind_list(", ", generics), bind(signature), bind(signature, true), type_name, bind_list(", ", generics), - bind(signature), + bind(signature, false), bind(signature)); } else @@ -2502,8 +2790,22 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable %([o = std::move(object), method](auto&&... args) { return ((*o).*(method))(args...); }) { } - template %::%(weak_ref&& object, M method) : - %([o = std::move(object), method](auto&&... args) { if (auto s = o.get()) { ((*s).*(method))(args...); } }) + template %::%(weak_ref&& object, LM&& lambda_or_method) : + %([o = std::move(object), lm = std::forward(lambda_or_method)](auto&&... args) { if (auto s = o.get()) { + if constexpr (std::is_member_function_pointer_v) ((*s).*(lm))(args...); + else lm(args...); + } }) + { + } + template %::%(std::shared_ptr&& object, M method) : + %([o = std::move(object), method](auto&&... args) { return ((*o).*(method))(args...); }) + { + } + template %::%(std::weak_ptr&& object, LM&& lambda_or_method) : + %([o = std::move(object), lm = std::forward(lambda_or_method)](auto&&... args) { if (auto s = o.lock()) { + if constexpr (std::is_member_function_pointer_v) ((*s).*(lm))(args...); + else lm(args...); + } }) { } inline auto %::operator()(%) const @@ -2530,24 +2832,30 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable type_name, type_name, type_name, + type_name, + type_name, + type_name, + type_name, + type_name, + type_name, bind(signature), bind(signature, true), type_name, - bind(signature), + bind(signature, false), bind(signature)); } } static void write_struct_field(writer& w, std::pair const& field) { - w.write(" @ %;\n", + w.write(" @ % {};\n", field.second, field.first); } static void write_struct_equality(writer& w, std::vector> const& fields) { - for (size_t i = 0; i != fields.size(); ++i) + for (std::size_t i = 0; i != fields.size(); ++i) { w.write(" left.% == right.%", fields[i].first, fields[i].first); @@ -2610,9 +2918,11 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable auto depends = [](writer& w, complex_struct const& left, complex_struct const& right) { + auto right_type = w.write_temp("%", right.type); + std::string right_as_ref = std::string("winrt::Windows::Foundation::IReference<") + right_type + ">"; for (auto&& field : left.fields) { - if (w.write_temp("%", right.type) == field.second) + if (right_type == field.second || right_as_ref == field.second) { return true; } @@ -2621,9 +2931,9 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable return false; }; - for (size_t left = 0; left < structs.size(); ++left) + for (std::size_t left = 0; left < structs.size(); ++left) { - for (size_t right = left + 1; right < structs.size(); ++right) + for (std::size_t right = left + 1; right < structs.size(); ++right) { if (depends(w, structs[left], structs[right])) { @@ -2660,7 +2970,7 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable for (auto&& field : type.fields) { - if (field.second.find(':') == std::string::npos) + if (field.second.find(':') == std::string::npos || starts_with(field.second, "std::")) { continue; } @@ -2677,11 +2987,11 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable static void write_class_requires(writer& w, TypeDef const& type) { - bool first{ true }; + bool first = true; for (auto&& [interface_name, info] : get_interfaces(w, type)) { - if (!info.defaulted || info.base) + if ((!info.defaulted || info.base) && (!info.is_protected && !info.overridable)) { if (first) { @@ -2701,7 +3011,7 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable static void write_fast_class_requires(writer& w, TypeDef const& type) { - bool first{ true }; + bool first = true; for (auto&& [interface_name, info] : get_interfaces(w, type)) { @@ -2725,7 +3035,7 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable static void write_class_base(writer& w, TypeDef const& type) { - bool first{ true }; + bool first = true; for (auto&& base : get_bases(type)) { @@ -2796,7 +3106,6 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable static void write_constructor_declarations(writer& w, TypeDef const& type, std::map const& factories) { - w.async_types = false; auto type_name = type.TypeName(); for (auto&& [factory_name, factory] : factories) @@ -2839,8 +3148,6 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable static void write_constructor_definition(writer& w, MethodDef const& method, TypeDef const& type, TypeDef const& factory) { - w.async_types = false; - auto type_name = type.TypeName(); method_signature signature{ method }; @@ -2870,7 +3177,7 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable auto format = R"( inline %::%(%) { - Windows::Foundation::IInspectable %, %; + winrt::Windows::Foundation::IInspectable %, %; *this = % { return f.%(%%%, %); }); } )"; @@ -2897,13 +3204,15 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable return; } + auto is_opt_type = settings.component_opt && settings.component_filter.includes(type); + for (auto&& method : factory.second.type.MethodList()) { method_signature signature{ method }; auto method_name = get_name(method); - w.async_types = signature.is_async(); + auto async_types_guard = w.push_async_types(signature.is_async()); - if (settings.component_opt && settings.component_filter.includes(type)) + if (is_opt_type) { w.write(" %static % %(%);\n", is_get_overload(method) ? "[[nodiscard]] " : "", @@ -2921,22 +3230,35 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable if (is_add_overload(method)) { - auto format = R"( using %_revoker = impl::factory_event_revoker<%, &impl::abi_t<%>::remove_%>; - [[nodiscard]] static %_revoker %(auto_revoke_t, %); + { + auto format = R"( using %_revoker = impl::factory_event_revoker<%, &impl::abi_t<%>::remove_%>; )"; + w.write(format, + method_name, + factory.second.type, + factory.second.type, + method_name); + } - w.write(format, - method_name, - factory.second.type, - factory.second.type, - method_name, - method_name, - method_name, - bind(signature)); + if (is_opt_type) + { + auto format = R"( [[nodiscard]] static %_revoker %(auto_revoke_t, %); +)"; + w.write(format, + method_name, + method_name, + bind(signature)); + } + else + { + auto format = R"( [[nodiscard]] static auto %(auto_revoke_t, %); +)"; + w.write(format, + method_name, + bind(signature)); + } } } - - w.async_types = false; } static void write_static_definitions(writer& w, MethodDef const& method, TypeDef const& type, TypeDef const& factory) @@ -2944,7 +3266,7 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable auto type_name = type.TypeName(); method_signature signature{ method }; auto method_name = get_name(method); - w.async_types = signature.is_async(); + auto async_types_guard = w.push_async_types(signature.is_async()); { auto format = R"( inline auto %::%(%) @@ -2963,26 +3285,24 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable if (is_add_overload(method)) { - auto format = R"( inline %::%_revoker %::%(auto_revoke_t, %) + auto format = R"( inline auto %::%(auto_revoke_t, %) { auto f = get_activation_factory<%, %>(); - return { f, f.%(%) }; + return %::%_revoker{ f, f.%(%) }; } )"; w.write(format, - type_name, - method_name, type_name, method_name, bind(signature), type_name, factory, + type_name, + method_name, method_name, bind(signature)); } - - w.async_types = false; } static void write_class_definitions(writer& w, TypeDef const& type) @@ -3005,7 +3325,7 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable if (has_fastabi(type)) { format = R"( inline %::%() : - %(impl::call_factory_cast<%(*)(Windows::Foundation::IActivationFactory const&), %>([](Windows::Foundation::IActivationFactory const& f) { return impl::fast_activate<%>(f); })) + %(impl::call_factory_cast<%(*)(winrt::Windows::Foundation::IActivationFactory const&), %>([](winrt::Windows::Foundation::IActivationFactory const& f) { return impl::fast_activate<%>(f); })) { } )"; @@ -3013,7 +3333,7 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable else { format = R"( inline %::%() : - %(impl::call_factory_cast<%(*)(Windows::Foundation::IActivationFactory const&), %>([](Windows::Foundation::IActivationFactory const& f) { return f.template ActivateInstance<%>(); })) + %(impl::call_factory_cast<%(*)(winrt::Windows::Foundation::IActivationFactory const&), %>([](winrt::Windows::Foundation::IActivationFactory const& f) { return f.template ActivateInstance<%>(); })) { } )"; @@ -3048,7 +3368,7 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable auto type_name = type.TypeName(); auto factories = get_factories(w, type); - auto format = R"( struct __declspec(empty_bases) % : %%% + auto format = R"( struct WINRT_IMPL_EMPTY_BASES % : %%% { %(std::nullptr_t) noexcept {} %(void* ptr, take_ownership_from_abi_t) noexcept : %(ptr, take_ownership_from_abi) {} @@ -3073,7 +3393,7 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable auto type_name = type.TypeName(); auto factories = get_factories(w, type); - auto format = R"( struct __declspec(empty_bases) % : %% + auto format = R"( struct WINRT_IMPL_EMPTY_BASES % : %% { %(std::nullptr_t) noexcept {} %(void* ptr, take_ownership_from_abi_t) noexcept : %(ptr, take_ownership_from_abi) {} @@ -3132,25 +3452,33 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable { auto generics = type.GenericParam(); - w.write(" template<%> struct hash : winrt::impl::hash_base {};\n", + w.write(" template<%> struct hash<%> : winrt::impl::hash_base {};\n", bind(generics), type); } - static void write_namespace_special(writer& w, std::string_view const& namespace_name, cache const& c) + static void write_std_formatter(writer& w, TypeDef const& type) { - if (namespace_name == "Windows.Foundation") + if (implements_interface(type, "Windows.Foundation.IStringable")) { - if (c.find("Windows.Foundation.PropertyValue")) - { - w.write(strings::base_reference_produce); - } - if (c.find("Windows.Foundation.Deferral")) - { - w.write(strings::base_deferral); - } + auto generics = type.GenericParam(); + + w.write(" template<%> struct formatter<%, wchar_t> : formatter {};\n", + bind(generics), + type); + } + } + static void write_namespace_special(writer& w, std::string_view const& namespace_name) + { + if (namespace_name == "Windows.Foundation") + { + w.write(strings::base_reference_produce); + w.write(strings::base_deferral); w.write(strings::base_coroutine_foundation); + w.write(strings::base_stringable_to_hstring); + w.write(strings::base_stringable_format); + w.write(strings::base_stringable_streams); } else if (namespace_name == "Windows.Foundation.Collections") { @@ -3168,10 +3496,6 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable { w.write(strings::base_coroutine_system); } - else if (namespace_name == "Microsoft.System") - { - w.write(strings::base_coroutine_system_winui); - } else if (namespace_name == "Windows.UI.Core") { w.write(strings::base_coroutine_ui_core); @@ -3180,5 +3504,22 @@ struct __declspec(empty_bases) produce_dispatch_to_overridable { w.write(strings::base_xaml_typename); } + else if (namespace_name == "Windows.UI.Xaml.Markup") + { + w.write(strings::base_xaml_component_connector); + } + else if (namespace_name == "Microsoft.UI.Xaml.Markup") + { + w.write(strings::base_xaml_component_connector_winui); + } + } + + static void write_namespace_special_1(writer& w, std::string_view const& namespace_name) + { + if (namespace_name == "Windows.Foundation") + { + w.write(strings::base_reference_produce_1); + w.write(strings::base_stringable_format_1); + } } } diff --git a/cppwinrt/component_writers.h b/cppwinrt/component_writers.h index ab48276f9..94e20e53e 100644 --- a/cppwinrt/component_writers.h +++ b/cppwinrt/component_writers.h @@ -27,7 +27,7 @@ namespace cppwinrt } } - bool first{ true }; + bool first = true; for (auto&& name : interfaces) { @@ -45,7 +45,7 @@ namespace cppwinrt static void write_component_class_base(writer& w, TypeDef const& type) { - bool first{ true }; + bool first = true; for (auto&& base : get_bases(type)) { @@ -136,7 +136,10 @@ namespace cppwinrt static void write_module_g_cpp(writer& w, std::vector const& classes) { - w.write_root_include("base"); + if (!settings.modules) + { + w.write_root_include("base"); + } auto format = R"(% bool __stdcall %_can_unload_now() noexcept { @@ -172,9 +175,12 @@ void* __stdcall %_get_activation_factory([[maybe_unused]] std::wstring_view cons } format = R"( -int32_t __stdcall WINRT_CanUnloadNow() noexcept +std::int32_t __stdcall WINRT_CanUnloadNow() noexcept { #ifdef _WRL_MODULE_H_ +#ifdef _MSC_VER +#pragma warning(suppress: 4324) // structure was padded due to alignment specifier +#endif if (!::Microsoft::WRL::Module<::Microsoft::WRL::InProc>::GetModule().Terminate()) { return 1; @@ -184,7 +190,7 @@ int32_t __stdcall WINRT_CanUnloadNow() noexcept return %_can_unload_now() ? 0 : 1; } -int32_t __stdcall WINRT_GetActivationFactory(void* classId, void** factory) noexcept try +std::int32_t __stdcall WINRT_GetActivationFactory(void* classId, void** factory) noexcept try { std::wstring_view const name{ *reinterpret_cast(&classId) }; *factory = %_get_activation_factory(name); @@ -195,6 +201,7 @@ int32_t __stdcall WINRT_GetActivationFactory(void* classId, void** factory) noex } #ifdef _WRL_MODULE_H_ +#pragma warning(suppress: 4324) // structure was padded due to alignment specifier return ::Microsoft::WRL::Module<::Microsoft::WRL::InProc>::GetModule().GetActivationFactory(static_cast(classId), reinterpret_cast<::IActivationFactory**>(factory)); #else return winrt::hresult_class_not_available(name).to_abi(); @@ -289,7 +296,7 @@ catch (...) { return winrt::to_hresult(); } bind(signature)); } - void write_component_static_forwarder(writer& w, MethodDef const& method) + static void write_component_static_forwarder(writer& w, MethodDef const& method) { auto format = R"( auto %(%) { @@ -347,7 +354,7 @@ catch (...) { return winrt::to_hresult(); } if (!default_constructor) { - w.write(R"( [[noreturn]] Windows::Foundation::IInspectable ActivateInstance() const + w.write(R"( [[noreturn]] winrt::Windows::Foundation::IInspectable ActivateInstance() const { throw hresult_not_implemented(); } @@ -393,7 +400,7 @@ catch (...) { return winrt::to_hresult(); } return; } - write_type_namespace(w, type_namespace); + auto wrap_type = wrap_type_namespace_without_export(w, type_namespace); for (auto&&[factory_name, factory] : get_factories(w, type)) { @@ -498,16 +505,18 @@ catch (...) { return winrt::to_hresult(); } { auto format = R"( % %::%(%) { - return @::implementation::%::%(%); + %@::implementation::%::%(%); } )"; + bool ignore_return = is_put_overload(method) || !signature.return_signature(); w.write(format, signature.return_signature(), type_name, method_name, bind(signature), + ignore_return ? "" : "return ", type_namespace, type_name, method_name, @@ -519,7 +528,7 @@ catch (...) { return winrt::to_hresult(); } auto format = R"( %::%_revoker %::%(auto_revoke_t, %) { auto f = make().as<%>(); - return { f, f.%(%) }; + return %::%_revoker{ f, f.%(%) }; } )"; @@ -532,14 +541,14 @@ catch (...) { return winrt::to_hresult(); } type_namespace, type_name, factory_name, + type_name, + method_name, method_name, bind(signature)); } } } } - - write_close_namespace(w); } static void write_component_override_dispatch_base(writer& w, TypeDef const& type) @@ -634,15 +643,15 @@ catch (...) { return winrt::to_hresult(); } } )"; - size_t offset = get_bases(type).size(); + std::size_t offset = get_bases(type).size(); auto interfaces = get_interfaces(w, type); for (auto&& [name, info] : interfaces) { if (!info.base && info.is_default) { - auto methods = info.type.MethodList(); - offset += methods.second - methods.first; + auto [first, second] = info.type.MethodList(); + offset += second - first; break; } } @@ -679,7 +688,7 @@ catch (...) { return winrt::to_hresult(); } if (has_base) { auto format = R"( - int32_t query_interface_tearoff(guid const& id, void** result) const noexcept override + std::int32_t query_interface_tearoff(guid const& id, void** result) const noexcept override {% return B::query_interface_tearoff(id, result); } @@ -695,7 +704,7 @@ catch (...) { return winrt::to_hresult(); } } auto format = R"( - int32_t query_interface_tearoff(guid const& id, void** result) const noexcept override + std::int32_t query_interface_tearoff(guid const& id, void** result) const noexcept override {% return impl::error_no_interface; } @@ -741,14 +750,14 @@ catch (...) { return winrt::to_hresult(); } auto format = R"(namespace winrt::@::implementation { template - struct __declspec(empty_bases) %_base : implements%%% + struct WINRT_IMPL_EMPTY_BASES %_base : implements%%%% { using base_type = %_base; using class_type = @::%; using implements_type = typename %_base::implements_type; using implements_type::implements_type; - % - hstring GetRuntimeClassName() const + %% + hstring GetRuntimeClassName() const override { return L"%.%"; } @@ -762,6 +771,8 @@ catch (...) { return winrt::to_hresult(); } std::string base_type_argument; std::string no_module_lock; std::string external_requires; + std::string external_protected_requires; + std::string friends; if (base_type) { @@ -771,8 +782,10 @@ catch (...) { return winrt::to_hresult(); } { composable_base_name = w.write_temp("using composable_base = %;", base_type); auto base_interfaces = get_interfaces(w, base_type); - uint32_t base_interfaces_count{}; + std::uint32_t base_interfaces_count{}; + std::uint32_t protected_base_interfaces_count{}; external_requires = ",\n impl::require(type), bind(type), type_name, @@ -820,6 +860,7 @@ catch (...) { return winrt::to_hresult(); } type_name, type_name, composable_base_name, + friends, type_namespace, type_name, bind(type), @@ -833,7 +874,7 @@ catch (...) { return winrt::to_hresult(); } auto format = R"(namespace winrt::@::factory_implementation { template - struct __declspec(empty_bases) %T : implements + struct WINRT_IMPL_EMPTY_BASES %T : implements { using instance_type = @::%; @@ -860,7 +901,9 @@ catch (...) { return winrt::to_hresult(); } { auto format = R"( #if defined(WINRT_FORCE_INCLUDE_%_XAML_G_H) || __has_include("%.xaml.g.h") + #include "%.xaml.g.h" + #else namespace winrt::@::implementation @@ -873,7 +916,7 @@ namespace winrt::@::implementation )"; std::string upper(type_name); - std::transform(upper.begin(), upper.end(), upper.begin(), [](char c) {return static_cast(::toupper(c)); }); + std::transform(upper.begin(), upper.end(), upper.begin(), [](char c) {return static_cast(std::toupper(c)); }); auto include_path = get_generated_component_filename(type); @@ -990,9 +1033,24 @@ namespace winrt::@::implementation static void write_generated_static_assert(writer& w) { auto format = R"( -// Note: Remove this static_assert after copying these generated source files to your project. -// This assertion exists to avoid compiling these generated source files directly. -static_assert(false, "Do not compile generated C++/WinRT source files directly"); +// WARNING: This file is automatically generated by a tool. Do not directly +// add this file to your project, as any changes you make will be lost. +// This file is a stub you can use as a starting point for your implementation. +// +// To add a copy of this file to your project: +// 1. Copy this file from its original location to the location where you store +// your other source files (e.g. the project root). +// 2. Add the copied file to your project. In Visual Studio, you can use +// Project -> Add Existing Item. +// 3. Delete this comment and the 'static_assert' (below) from the copied file. +// Do not modify the original file. +// +// To update an existing file in your project: +// 1. Copy the relevant changes from this file and merge them into the copy +// you made previously. +// +// This assertion helps prevent accidental modification of generated files. +static_assert(false, "This file is generated by a tool and will be overwritten. Open this error and view the comment for assistance."); )"; w.write(format); @@ -1185,7 +1243,7 @@ namespace winrt::@::implementation static void write_component_fast_abi_thunk(writer& w) { - for (uint32_t slot = 6; slot < 1024; ++slot) + for (std::uint32_t slot = 6; slot < 1024; ++slot) { auto format = R"( extern "C" void __stdcall winrt_ff_thunk%(); )"; @@ -1196,7 +1254,7 @@ namespace winrt::@::implementation static void write_component_fast_abi_vtable(writer& w) { - for (uint32_t slot = 6; slot < 1024; ++slot) + for (std::uint32_t slot = 6; slot < 1024; ++slot) { auto format = R"( #if WINRT_FAST_ABI_SIZE > % @@ -1208,4 +1266,4 @@ namespace winrt::@::implementation slot); } } -} \ No newline at end of file +} diff --git a/cppwinrt/cppwinrt.vcxproj b/cppwinrt/cppwinrt.vcxproj index 871316366..b8beed890 100644 --- a/cppwinrt/cppwinrt.vcxproj +++ b/cppwinrt/cppwinrt.vcxproj @@ -1,11 +1,7 @@ - + - - Debug - ARM - Debug ARM64 @@ -14,10 +10,6 @@ Debug Win32 - - Release - ARM - Release ARM64 @@ -54,9 +46,9 @@ + - @@ -68,21 +60,30 @@ + + + + + + + + + @@ -107,6 +108,9 @@ + + + @@ -114,17 +118,12 @@ 15.0 {D613FB39-5035-4043-91E2-BAB323908AF4} cppwinrt - 10.0 - + Application true - - Application - true - Application true @@ -134,11 +133,6 @@ false true - - Application - false - true - Application false @@ -161,18 +155,12 @@ - - - - - - @@ -183,42 +171,13 @@ - - $(OutDir)temp\$(ProjectName)\ - - - - - $(OutDir)temp\$(ProjectName)\ - - - $(OutDir)temp\$(ProjectName)\ - - - $(OutDir)temp\$(ProjectName)\ - Disabled ..\inc;$(OutputPath);$(WinMDPackageDir); MultiThreadedDebug - - - Console - - - $(OutputPath)prebuild.exe ..\strings $(OutputPath) - - - - - - - - - Disabled - ..\inc;$(OutputPath);$(WinMDPackageDir); - MultiThreadedDebug + Level4 + true Console @@ -236,12 +195,14 @@ Disabled ..\inc;$(OutputPath);$(WinMDPackageDir); MultiThreadedDebug + Level4 + true Console - $(OutputPath)prebuild.exe ..\strings $(OutputPath) + $(CppWinRTDir)prebuild.exe ..\strings $(OutputPath) @@ -253,6 +214,8 @@ Disabled ..\inc;$(OutputPath);$(WinMDPackageDir); MultiThreadedDebug + Level4 + true Console @@ -272,32 +235,15 @@ true ..\inc;$(OutputPath);$(WinMDPackageDir); MultiThreaded + Guard + Level4 + true Console true true - - - $(OutputPath)prebuild.exe ..\strings $(OutputPath) - - - - - - - - - MaxSpeed - true - true - ..\inc;$(OutputPath);$(WinMDPackageDir); - MultiThreaded - - - Console - true - true + /DEBUGTYPE:CV,FIXUP %(AdditionalOptions) $(OutputPath)prebuild.exe ..\strings $(OutputPath) @@ -314,14 +260,18 @@ true ..\inc;$(OutputPath);$(WinMDPackageDir); MultiThreaded + Guard + Level4 + true Console true true + /DEBUGTYPE:CV,FIXUP %(AdditionalOptions) - $(OutputPath)prebuild.exe ..\strings $(OutputPath) + $(CppWinRTDir)prebuild.exe ..\strings $(OutputPath) @@ -335,11 +285,15 @@ true ..\inc;$(OutputPath);$(WinMDPackageDir); MultiThreaded + Guard + Level4 + true Console true true + /DEBUGTYPE:CV,FIXUP %(AdditionalOptions) $(OutputPath)prebuild.exe ..\strings $(OutputPath) @@ -355,6 +309,6 @@ 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/cppwinrt/cppwinrt.vcxproj.filters b/cppwinrt/cppwinrt.vcxproj.filters index 069061a3b..96129ab20 100644 --- a/cppwinrt/cppwinrt.vcxproj.filters +++ b/cppwinrt/cppwinrt.vcxproj.filters @@ -64,9 +64,6 @@ strings - - strings - strings @@ -115,6 +112,9 @@ strings + + strings + strings @@ -136,6 +136,9 @@ strings + + strings + strings @@ -157,9 +160,33 @@ strings + + strings + + + strings + + + strings + + + strings + + + strings + + + strings + strings + + strings + + + strings + @@ -172,4 +199,7 @@ + + + \ No newline at end of file diff --git a/cppwinrt/file_writers.h b/cppwinrt/file_writers.h index 42fb86556..fd9833cb1 100644 --- a/cppwinrt/file_writers.h +++ b/cppwinrt/file_writers.h @@ -6,41 +6,50 @@ namespace cppwinrt { writer w; write_preamble(w); - write_open_file_guard(w, "BASE"); - - w.write(strings::base_includes); - w.write(strings::base_macros); - w.write(strings::base_types); - w.write(strings::base_extern); - w.write(strings::base_meta); - w.write(strings::base_identity); - w.write(strings::base_handle); - w.write(strings::base_lock); - w.write(strings::base_abi); - w.write(strings::base_windows); - w.write(strings::base_com_ptr); - w.write(strings::base_string); - w.write(strings::base_string_input); - w.write(strings::base_string_operators); - w.write(strings::base_array); - w.write(strings::base_weak_ref); - w.write(strings::base_agile_ref); - w.write(strings::base_error); - w.write(strings::base_marshaler); - w.write(strings::base_delegate); - w.write(strings::base_events); - w.write(strings::base_activation); - w.write(strings::base_implements); - w.write(strings::base_composable); - w.write(strings::base_foundation); - w.write(strings::base_chrono); - w.write(strings::base_security); - w.write(strings::base_std_hash); - w.write(strings::base_coroutine_threadpool); - w.write(strings::base_natvis); - w.write(strings::base_version, CPPWINRT_VERSION_STRING); - - write_endif(w); + w.write(strings::base_version_odr, CPPWINRT_VERSION_STRING); + { + auto wrap_file_guard = wrap_open_file_guard(w, "BASE"); + auto wrap_import = wrap_ifndef(w, "WINRT_IMPORT_MODULE"); + + { + auto wrap_includes = wrap_module_aware_includes_guard(w, true); + w.write(strings::base_includes); + w.write(strings::base_detect_numerics); + w.write(strings::base_include_numerics); + } + w.write_root_include("base_macros"); + w.write(strings::base_source_location); + w.write(strings::base_types); + w.write(strings::base_extern); + w.write(strings::base_meta); + w.write(strings::base_identity); + w.write(strings::base_handle); + w.write(strings::base_lock); + w.write(strings::base_abi); + w.write(strings::base_windows); + w.write(strings::base_com_ptr); + w.write(strings::base_string); + w.write(strings::base_string_input); + w.write(strings::base_string_operators); + w.write(strings::base_array); + w.write(strings::base_weak_ref); + w.write(strings::base_agile_ref); + w.write(strings::base_error); + w.write(strings::base_marshaler); + w.write(strings::base_delegate); + w.write(strings::base_events); + w.write(strings::base_activation); + w.write(strings::base_implements); + w.write(strings::base_composable); + w.write(strings::base_foundation); + w.write(strings::base_chrono); + w.write(strings::base_security); + w.write(strings::base_std_hash); + w.write(strings::base_iterator); + w.write(strings::base_coroutine_threadpool); + w.write(strings::base_natvis); + w.write(strings::base_version); + } w.flush_to_file(settings.output_folder + "winrt/base.h"); } @@ -48,166 +57,211 @@ namespace cppwinrt { writer w; write_preamble(w); - write_open_file_guard(w, "FAST_FORWARD"); + { + auto wrap_file_guard = wrap_open_file_guard(w, "FAST_FORWARD"); - auto const fast_abi_size = get_fastabi_size(w, classes); + auto const fast_abi_size = get_fastabi_size(w, classes); - w.write(strings::base_fast_forward, - fast_abi_size, - fast_abi_size, - bind(), - bind()); + w.write(strings::base_fast_forward, + fast_abi_size, + fast_abi_size, + bind(), + bind()); - write_endif(w); + } w.flush_to_file(settings.output_folder + "winrt/fast_forward.h"); } - static void write_namespace_0_h(std::string_view const& ns, cache::namespace_members const& members) + static void collect_writer_deps(writer const& w, std::set& out) + { + for (auto&& [dep_ns, _] : w.depends) + { + out.insert(std::string(dep_ns)); + } + } + + static void write_namespace_0_h(std::string_view const& ns, cache::namespace_members const& members, std::set* out_deps = nullptr) { writer w; w.type_namespace = ns; - write_type_namespace(w, ns); - w.write_each(members.enums); - w.write_each(members.interfaces); - w.write_each(members.classes); - w.write_each(members.structs); - w.write_each(members.delegates); - write_close_namespace(w); - write_impl_namespace(w); - w.write_each(members.interfaces, "interface_category"); - w.write_each(members.classes, "class_category"); - w.write_each(members.enums, "enum_category"); - w.write_each(members.structs); - w.write_each(members.delegates, "delegate_category"); - - // Class names are always required for activation. - // Class, enum, and struct names are required for producing GUIDs for generic types. - // Interface and delegates names are required for Xaml compatibility. - w.write_each(members.classes); - w.write_each(members.enums); - w.write_each(members.structs); - w.write_each(members.interfaces); - w.write_each(members.delegates); - - w.write_each(members.interfaces); - w.write_each(members.delegates); - w.write_each(members.classes); - w.write_each(members.interfaces); - w.write_each(members.delegates); - w.write_each(members.interfaces); - w.write_each(members.structs); - write_close_namespace(w); - - write_endif(w); + { + auto wrap_type = wrap_type_namespace(w, ns); + w.write_each(members.enums); + w.write_each(members.interfaces); + w.write_each(members.classes); + w.write_each(members.structs); + w.write_each(members.delegates); + w.write_each(members.contracts); + } + { + auto wrap_impl = wrap_impl_namespace(w); + w.write_each(members.interfaces, "interface_category"); + w.write_each(members.classes, "class_category"); + w.write_each(members.enums, "enum_category"); + w.write_each(members.structs); + w.write_each(members.delegates, "delegate_category"); + + // Class names are always required for activation. + // Class, enum, and struct names are required for producing GUIDs for generic types. + // Interface and delegates names are required for Xaml compatibility. + // Contract names are used by IsApiContractPresent. + w.write_each(members.classes); + w.write_each(members.enums); + w.write_each(members.structs); + w.write_each(members.interfaces); + w.write_each(members.delegates); + w.write_each(members.contracts); + + w.write_each(members.interfaces); + w.write_each(members.delegates); + w.write_each(members.classes); + w.write_each(members.interfaces); + w.write_each(members.delegates); + w.write_each(members.interfaces); + w.write_each(members.structs); + } + + write_close_file_guard(w); w.swap(); write_preamble(w); write_open_file_guard(w, ns, '0'); for (auto&& depends : w.depends) { - write_type_namespace(w, depends.first); + auto wrap_type = wrap_type_namespace(w, depends.first); w.write_each(depends.second); - write_close_namespace(w); } + if (out_deps) collect_writer_deps(w, *out_deps); w.save_header('0'); } - static void write_namespace_1_h(std::string_view const& ns, cache::namespace_members const& members) + static void write_namespace_1_h(std::string_view const& ns, cache::namespace_members const& members, std::set* out_deps = nullptr) { writer w; w.type_namespace = ns; - write_type_namespace(w, ns); - w.write_each(members.interfaces); - write_close_namespace(w); + { + auto wrap_type = wrap_type_namespace(w, ns); + w.write_each(members.interfaces); + } + write_namespace_special_1(w, ns); - write_endif(w); + write_close_file_guard(w); w.swap(); write_preamble(w); write_open_file_guard(w, ns, '1'); - for (auto&& depends : w.depends) { - w.write_depends(depends.first, '0'); - } + auto wrap_includes = wrap_module_aware_includes_guard(w, true); + for (auto&& depends : w.depends) + { + w.write_depends(depends.first, '0'); + } - w.write_depends(w.type_namespace, '0'); + w.write_depends(w.type_namespace, '0'); + } + if (out_deps) collect_writer_deps(w, *out_deps); w.save_header('1'); } - static void write_namespace_2_h(std::string_view const& ns, cache::namespace_members const& members) + static void write_namespace_2_h(std::string_view const& ns, cache::namespace_members const& members, std::set* out_deps = nullptr) { writer w; w.type_namespace = ns; - write_type_namespace(w, ns); - w.write_each(members.delegates); - bool const promote = write_structs(w, members.structs); - w.write_each(members.classes); - w.write_each(members.classes); - write_close_namespace(w); + bool promote; + { + auto wrap_type = wrap_type_namespace(w, ns); + w.write_each(members.delegates); + promote = write_structs(w, members.structs); + w.write_each(members.classes); + w.write_each(members.classes); + } - write_endif(w); + write_close_file_guard(w); w.swap(); write_preamble(w); write_open_file_guard(w, ns, '2'); char const impl = promote ? '2' : '1'; - for (auto&& depends : w.depends) { - w.write_depends(depends.first, impl); - } + auto wrap_includes = wrap_module_aware_includes_guard(w, true); + for (auto&& depends : w.depends) + { + w.write_depends(depends.first, impl); + } - w.write_depends(w.type_namespace, '1'); + w.write_depends(w.type_namespace, '1'); + } + if (out_deps) collect_writer_deps(w, *out_deps); w.save_header('2'); } - static void write_namespace_h(cache const& c, std::string_view const& ns, cache::namespace_members const& members) + static void write_namespace_h(cache const& c, std::string_view const& ns, cache::namespace_members const& members, std::set* out_deps = nullptr) { writer w; w.type_namespace = ns; - write_impl_namespace(w); - w.write_each(members.interfaces); - w.write_each(members.delegates); - w.write_each(members.interfaces, c); - w.write_each(members.classes); - write_close_namespace(w); - - write_type_namespace(w, ns); - w.write_each(members.enums); - w.write_each(members.classes); - w.write_each(members.classes); - w.write_each(members.delegates); - w.write_each(members.classes); - w.write_each(members.classes); - write_close_namespace(w); - - write_std_namespace(w); - write_lean_and_mean(w); - w.write_each(members.interfaces); - w.write_each(members.classes); - write_endif(w); - write_close_namespace(w); - - write_namespace_special(w, ns, c); - - write_endif(w); + { + auto wrap_impl = wrap_impl_namespace(w); + w.write_each(members.interfaces); + w.param_names = true; + w.write_each(members.delegates); + w.write_each(members.interfaces, c); + w.write_each(members.classes); + } + { + auto wrap_type = wrap_type_namespace(w, ns); + w.write_each(members.enums); + w.write_each(members.classes); + w.write_each(members.classes); + w.write_each(members.delegates); + w.write_each(members.classes); + w.write_each(members.classes); + } + { + auto wrap_std = wrap_std_namespace(w); + + { + auto wrap_lean = wrap_lean_and_mean(w); + w.write_each(members.interfaces); + w.write_each(members.classes); + } + { + auto wrap_format = wrap_ifdef(w, "__cpp_lib_format"); + w.write_each(members.interfaces); + w.write_each(members.classes); + } + } + + write_namespace_special(w, ns); + + write_close_file_guard(w); + // The #ifndef WINRT_IMPORT_MODULE / #endif pair spans across w.swap(). + // The body is written first (above), then swap() prepends the header prefix (below). + // In the final output, #ifndef opens before the includes and #endif closes after + // the file guard, mirroring the write_open_file_guard / write_close_file_guard pair. + w.write("#endif // WINRT_IMPORT_MODULE\n"); w.swap(); write_preamble(w); write_open_file_guard(w, ns); - write_version_assert(w); - write_parent_depends(w, c, ns); - - for (auto&& depends : w.depends) + w.write("#ifndef WINRT_IMPORT_MODULE\n\n"); { - w.write_depends(depends.first, '2'); - } + auto wrap_includes = wrap_module_aware_includes_guard(w, true); + write_version_assert(w); + write_parent_depends(w, c, ns); - w.write_depends(w.type_namespace, '2'); + for (auto&& depends : w.depends) + { + w.write_depends(depends.first, '2'); + } + + w.write_depends(w.type_namespace, '2'); + } + if (out_deps) collect_writer_deps(w, *out_deps); w.save_header(); } @@ -216,6 +270,28 @@ namespace cppwinrt writer w; write_preamble(w); write_pch(w); + + if (settings.modules) + { + // In module builds, import std and winrt_base instead of #include "winrt/base.h". + // std is needed for std::wstring_view, std::equal, std::int32_t used in + // the activation factory lookup code. + w.write("\nimport std;\n"); + w.write("import winrt_base;\n"); + + // Collect all unique namespaces from the component classes + std::set namespaces; + for (auto&& type : classes) + { + namespaces.insert(std::string(type.TypeNamespace())); + } + for (auto&& ns : namespaces) + { + w.write("import winrt.%;\n", ns); + } + w.write("\n"); + } + write_module_g_cpp(w, classes); w.flush_to_file(settings.output_folder + "module.g.cpp"); } @@ -230,9 +306,19 @@ namespace cppwinrt write_preamble(w); write_include_guard(w); - for (auto&& depends : w.depends) { - w.write_depends(depends.first); + auto wrap = wrap_ifdef(w, "WINRT_IMPORT_MODULE"); + w.write_root_include("base_macros"); + for (auto&& depends : w.depends) + { + w.write("import winrt.%;\n", depends.first); + } + w.write("#else // WINRT_IMPORT_MODULE\n"); + + for (auto&& depends : w.depends) + { + w.write_depends(depends.first); + } } auto filename = settings.output_folder + get_generated_component_filename(type) + ".g.h"; @@ -296,7 +382,269 @@ namespace cppwinrt writer w; write_pch(w); + + if (settings.modules) + { + // The .g.h handles its own imports, but the implementation .h + // needs the types available in scope, so we import them here. + writer dep_scanner; + dep_scanner.add_depends(type); + write_component_g_h(dep_scanner, type); + + w.write("\n#define WINRT_IMPORT_MODULE\n"); + for (auto&& depends : dep_scanner.depends) + { + w.write("import winrt.%;\n", depends.first); + } + w.write("\n"); + } + write_component_cpp(w, type); w.flush_to_file(path); } + + // --- Per-namespace C++20 module interface unit (.ixx) writers --- + + // Emits the common global module fragment used by all generated .ixx files. + // Defines WINRT_IMPL_BUILD_MODULE so generated headers switch WINRT_EXPORT + // to 'export extern "C++"' and suppress textual #includes of dependencies + // (dependencies arrive via module imports instead). + // Includes minimal headers needed for macros, intrinsics, and debug assertions. + static void write_module_preamble(writer& w) + { + write_preamble(w); + w.write(strings::base_module_ixx_preamble); + w.write_root_include("base_macros"); + } + + // Emits $(out)/winrt/base_macros.h + // This header provides the core macros shared between header and module builds. + // In header builds, base.h includes base_macros.h inline (via the prebuild-embedded string). + // In module builds, each .ixx file includes this in its global module fragment. + static void write_macros_h() + { + writer w; + write_preamble(w); + w.write(strings::base_macros, CPPWINRT_VERSION_STRING); + w.flush_to_file(settings.output_folder + "winrt/base_macros.h"); + } + + static void write_base_ixx() + { + writer w; + write_module_preamble(w); + w.write(strings::base_module_base_ixx); + w.write(strings::base_detect_numerics); + w.write("\n"); + w.write_root_include("base"); + w.flush_to_file(settings.output_folder + "winrt/winrt_base.ixx"); + } + + static void write_numerics_ixx() + { + writer w; + write_module_preamble(w); + // GMF: detect numerics and pre-include directxmath so it's not pulled + // into the module purview by windowsnumerics.impl.h + w.write(strings::base_detect_numerics); + { + auto wrap = wrap_ifdef(w, "WINRT_IMPL_NUMERICS"); + w.write("#include \n"); + } + w.write(strings::base_module_numerics_ixx); + // Module declaration + w.write("\nexport module winrt_numerics;\n"); + // Include windowsnumerics.impl.h in the module purview (exports the types). + // directxmath.h is already included in the GMF above, so the #include inside + // base_include_numerics is a no-op (header guard). + { + auto wrap = wrap_ifdef(w, "_MSC_VER"); + w.write("#pragma warning(push)\n"); + w.write("#pragma warning(disable : 5244)\n"); + } + w.write(strings::base_include_numerics); + { + auto wrap = wrap_ifdef(w, "_MSC_VER"); + w.write("#pragma warning(pop)\n"); + } + w.flush_to_file(settings.output_folder + "winrt/winrt_numerics.ixx"); + } + + // Emits a per-namespace module interface unit for namespaces that are NOT + // part of a dependency cycle (standalone module). + // Output: $(out)/winrt/winrt..ixx (export module winrt.;) + // + // The generated .ixx: + // 1. Starts with the global module fragment (WINRT_IMPL_BUILD_MODULE, minimal includes) + // 2. Declares 'export module winrt.;' + // 3. Imports std and re-exports winrt_base + // 4. Imports each dependent namespace module (computed from type references in headers) + // 5. Includes the impl headers (*.0.h, *.1.h, *.2.h) and public header (.h) + // in the module purview, where WINRT_EXPORT causes declarations to be exported + static void write_namespace_ixx( + std::string_view const& ns, + std::set const& deps) + { + writer w; + write_module_preamble(w); + + // Module declaration + w.write("export module winrt.%;\n\n", ns); + + // Document dependencies + w.write("// Module dependencies:\n"); + w.write("// - std\n"); + w.write("// - winrt_base (re-exported)\n"); + if (deps.empty()) + { + w.write("// - (no additional namespace imports)\n"); + } + else + { + for (auto& dep : deps) + { + w.write("// - winrt.%\n", dep); + } + } + w.write("\n"); + + // Import std and base + w.write("import std;\n"); + w.write("export import winrt_base;\n"); + + // Import dependency namespace modules + for (auto& dep : deps) + { + w.write("import winrt.%;\n", dep); + } + + // Version mismatch check: ensure this namespace module was generated by the + // same version of cppwinrt.exe as the winrt_base module it imports. + // winrt::cppwinrt_version is exported from winrt_base; CPPWINRT_VERSION is + // the macro from this module's own base_macros.h in the global module fragment. + w.write("\nstatic_assert(winrt::check_version(winrt::cppwinrt_version, CPPWINRT_VERSION), \"Mismatched C++/WinRT headers.\");\n\n"); + + // Include namespace headers in module purview + w.write_depends(ns, '0'); + w.write_depends(ns, '1'); + w.write_depends(ns, '2'); + w.write_root_include(ns); + + w.flush_to_file(settings.output_folder + "winrt/winrt." + std::string(ns) + ".ixx"); + } + + // Emits the SCC (Strongly Connected Component) owner module interface unit. + // When multiple namespaces form a dependency cycle, they cannot each have their + // own independent module (circular imports are illegal in C++20 modules). + // Instead, one namespace is chosen as the "owner" (alphabetically first in the SCC), + // and ALL cyclic namespaces' declarations are consolidated into this single module. + // The other namespaces in the SCC get thin re-export stubs (see write_namespace_reexport_ixx). + // + // Output: $(out)/winrt/winrt..ixx (export module winrt.;) + // + // The owner module: + // 1. Imports external dependencies (deps outside the SCC) + // 2. Forward-declares all projected types for ALL SCC namespaces before any + // impl headers — this breaks the type reference cycles + // 3. Includes impl headers in stable phase order: all *.0.h, then all *.1.h, + // then all *.2.h, then all public headers — preserving the original header + // layering while keeping SCC compilation deterministic + static void write_namespace_scc_owner_ixx( + cache const& c, + std::string_view const& owner, + std::vector const& scc_members, + std::set const& external_deps) + { + writer w; + write_module_preamble(w); + + // Module declaration (owner namespace) + w.write("// This module is an SCC owner (cycle breaker). The following namespaces\n"); + w.write("// form a dependency cycle and are consolidated into this single module:\n"); + for (auto& ns : scc_members) + { + w.write("// - %\n", ns); + } + w.write("// Other SCC namespaces are emitted as re-export stubs.\n\n"); + w.write("export module winrt.%;\n\n", owner); + + // Import std and base + w.write("import std;\n"); + w.write("export import winrt_base;\n"); + + // Import external dependency modules (outside the SCC) + for (auto& dep : external_deps) + { + w.write("import winrt.%;\n", dep); + } + + // Version mismatch check: ensure this namespace module was generated by the + // same version of cppwinrt.exe as the winrt_base module it imports. + // winrt::cppwinrt_version is exported from winrt_base; CPPWINRT_VERSION is + // the macro from this module's own base_macros.h in the global module fragment. + w.write("\nstatic_assert(winrt::check_version(winrt::cppwinrt_version, CPPWINRT_VERSION), \"Mismatched C++/WinRT headers.\");\n"); + + // Forward declarations for all projected types in this SCC. + // This is required because SCC members have cyclic type references, + // and generated headers suppress dependent #includes when WINRT_IMPL_BUILD_MODULE + // is defined. Forward declarations provide the names needed before definitions. + for (auto& ns : scc_members) + { + auto found = c.namespaces().find(ns); + if (found == c.namespaces().end()) + { + continue; + } + auto& members = found->second; + + auto wrap_type = wrap_type_namespace(w, ns); + w.write_each(members.enums); + w.write_each(members.interfaces); + w.write_each(members.classes); + w.write_each(members.structs); + w.write_each(members.delegates); + w.write_each(members.contracts); + } + + // Include all SCC members' headers in stable phase order. + // All *.0.h (forward decls + ABIs), then all *.1.h (interfaces), + // then all *.2.h (delegates/structs/classes), then all public headers. + // This preserves the original header layering while keeping compilation deterministic. + for (auto& ns : scc_members) + { + w.write_depends(ns, '0'); + } + for (auto& ns : scc_members) + { + w.write_depends(ns, '1'); + } + for (auto& ns : scc_members) + { + w.write_depends(ns, '2'); + } + for (auto& ns : scc_members) + { + w.write_root_include(ns); + } + + w.flush_to_file(settings.output_folder + "winrt/winrt." + std::string(owner) + ".ixx"); + } + + // Emits a thin re-export stub module for SCC non-owner namespaces. + // This allows 'import winrt.;' to work even though the actual declarations + // live in the SCC owner module. The stub simply re-exports the owner. + // Output: $(out)/winrt/winrt..ixx (export module winrt.; export import winrt.;) + static void write_namespace_reexport_ixx( + std::string_view const& ns, + std::string_view const& owner) + { + writer w; + write_preamble(w); + w.write("\n// NOTE: This module does not define declarations of its own.\n"); + w.write("// It re-exports all declarations from the 'winrt.%' module. This is used to break cycles in the\n", owner); + w.write("// WinRT namespace module dependency graph (SCC owner consolidation).\n\n"); + w.write("export module winrt.%;\n", ns); + w.write("export import winrt.%;\n", owner); + w.flush_to_file(settings.output_folder + "winrt/winrt." + std::string(ns) + ".ixx"); + } } diff --git a/cppwinrt/helpers.h b/cppwinrt/helpers.h index a8aeab9e8..2dc152a74 100644 --- a/cppwinrt/helpers.h +++ b/cppwinrt/helpers.h @@ -31,7 +31,7 @@ namespace cppwinrt ++params.first; } - for (uint32_t i{}; i != size(m_signature.Params()); ++i) + for (std::uint32_t i{}; i != size(m_signature.Params()); ++i) { m_params.emplace_back(params.first + i, &m_signature.Params().first[i]); } @@ -80,7 +80,7 @@ namespace cppwinrt // by the caller and callee. The exception to this rule is property setters where the callee may simply store a // reference to the collection. The collection thus becomes async in the sense that it is expected to remain // valid beyond the duration of the call. - + if (is_put_overload(m_method)) { return true; @@ -127,7 +127,7 @@ namespace cppwinrt struct separator { writer& w; - bool first{ true }; + bool first = true; void operator()() { @@ -148,10 +148,54 @@ namespace cppwinrt return static_cast(get_attribute(row, type_namespace, type_name)); } + namespace impl + { + template + struct variant_index; + + template + struct variant_index + { + static constexpr bool found = std::is_same_v; + static constexpr std::size_t value = std::conditional_t, + variant_index>::value + (found ? 0 : 1); + }; + } + + template + struct variant_index; + + template + struct variant_index, T> : impl::variant_index + { + }; + + template + constexpr std::size_t variant_index_v = variant_index::value; + template - auto get_attribute_value(CustomAttribute const& attribute, uint32_t const arg) + auto get_integer_attribute(FixedArgSig const& signature) { - return std::get(std::get(attribute.Value().FixedArgs()[arg].value).value); + auto variant = std::get(signature.value).value; + switch (variant.index()) + { + case variant_index_v>: return static_cast(std::get>(variant)); + case variant_index_v>: return static_cast(std::get>(variant)); + default: return std::get(variant); // Likely throws, but that's intentional + } + } + + template + auto get_attribute_value(FixedArgSig const& signature) + { + return std::get(std::get(signature.value).value); + } + + template + auto get_attribute_value(CustomAttribute const& attribute, std::uint32_t const arg) + { + return get_attribute_value(attribute.Value().FixedArgs()[arg]); } static auto get_abi_name(MethodDef const& method) @@ -200,7 +244,7 @@ namespace cppwinrt static bool has_fastabi(TypeDef const& type) { - return settings.fastabi && has_attribute(type, "Windows.Foundation.Metadata", "FastAbiAttribute"); + return settings.fastabi&& has_attribute(type, "Windows.Foundation.Metadata", "FastAbiAttribute"); } static bool is_always_disabled(TypeDef const& type) @@ -283,45 +327,217 @@ namespace cppwinrt return bases; } - static std::pair get_version(TypeDef const& type) + struct contract_version { - uint32_t version{}; + std::string_view name; + std::uint32_t version; + }; + struct previous_contract + { + std::string_view contract_from; + std::string_view contract_to; + std::uint32_t version_low; + std::uint32_t version_high; + }; + + struct contract_history + { + contract_version current_contract; + + // Sorted such that the first entry is the first contract the type was introduced in + std::vector previous_contracts; + }; + + static contract_version decode_contract_version_attribute(CustomAttribute const& attribute) + { + // ContractVersionAttribute has three constructors, but only two we care about here: + // .ctor(string contract, uint32 version) + // .ctor(System.Type contract, uint32 version) + auto signature = attribute.Value(); + auto& args = signature.FixedArgs(); + assert(args.size() == 2); + + contract_version result{}; + result.version = get_integer_attribute(args[1]); + call(std::get(args[0].value).value, + [&](ElemSig::SystemType t) + { + result.name = t.name; + }, + [&](std::string_view name) + { + result.name = name; + }, + [](auto&&) + { + assert(false); + }); + + return result; + } + + static previous_contract decode_previous_contract_attribute(CustomAttribute const& attribute) + { + // PreviousContractVersionAttribute has two constructors: + // .ctor(string fromContract, uint32 versionLow, uint32 versionHigh) + // .ctor(string fromContract, uint32 versionLow, uint32 versionHigh, string contractTo) + auto signature = attribute.Value(); + auto& args = signature.FixedArgs(); + assert(args.size() >= 3); + + previous_contract result{}; + result.contract_from = get_attribute_value(args[0]); + result.version_low = get_integer_attribute(args[1]); + result.version_high = get_integer_attribute(args[2]); + if (args.size() == 4) + { + result.contract_to = get_attribute_value(args[3]); + } + + return result; + } + + static contract_version get_initial_contract_version(TypeDef const& type) + { + // Most types don't have previous contracts, so optimize for that scenario to avoid unnecessary allocations + contract_version current_contract{}; + + // The initial contract, assuming the type has moved contracts, is the only contract name that doesn't appear as + // a "to contract" argument to a PreviousContractVersionAttribute. Note that this assumes that a type does not + // "return" to a prior contract, however this is a restriction enforced by midlrt + std::vector previous_contracts; + std::vector to_contracts; for (auto&& attribute : type.CustomAttribute()) { - auto name = attribute.TypeNamespaceAndName(); + auto [ns, name] = attribute.TypeNamespaceAndName(); + if (ns != "Windows.Foundation.Metadata") + { + continue; + } + + if (name == "ContractVersionAttribute") + { + assert(current_contract.name.empty()); + current_contract = decode_contract_version_attribute(attribute); + } + else if (name == "PreviousContractVersionAttribute") + { + auto prev = decode_previous_contract_attribute(attribute); + + // If this contract was the target of an earlier contract change, we know this isn't the initial one + if (std::find(to_contracts.begin(), to_contracts.end(), prev.contract_from) == to_contracts.end()) + { + previous_contracts.push_back(contract_version{ prev.contract_from, prev.version_low }); + } + + if (!prev.contract_to.empty()) + { + auto itr = std::find_if(previous_contracts.begin(), previous_contracts.end(), [&](auto const& ver) + { + return ver.name == prev.contract_to; + }); + if (itr != previous_contracts.end()) + { + *itr = previous_contracts.back(); + previous_contracts.pop_back(); + } + + to_contracts.push_back(prev.contract_to); + } + } + else if (name == "VersionAttribute") + { + // Prefer contract versioning, if present. Otherwise, use an empty contract name to indicate that this + // is not a contract version + if (current_contract.name.empty()) + { + current_contract.version = get_attribute_value(attribute, 0); + } + } + } + + if (!previous_contracts.empty()) + { + assert(previous_contracts.size() == 1); + return previous_contracts[0]; + } + + return current_contract; + } - if (name.first != "Windows.Foundation.Metadata") + static contract_history get_contract_history(TypeDef const& type) + { + contract_history result{}; + for (auto&& attribute : type.CustomAttribute()) + { + auto [ns, name] = attribute.TypeNamespaceAndName(); + if (ns != "Windows.Foundation.Metadata") { continue; } - if (name.second == "ContractVersionAttribute") + if (name == "ContractVersionAttribute") { - version = get_attribute_value(attribute, 1); - break; + assert(result.current_contract.name.empty()); + result.current_contract = decode_contract_version_attribute(attribute); } + else if (name == "PreviousContractVersionAttribute") + { + result.previous_contracts.push_back(decode_previous_contract_attribute(attribute)); + } + // We could report the version that the type was introduced if the type is not contract versioned, however + // that information is not useful to us anywhere, so just skip it + } - if (name.second == "VersionAttribute") + if (result.previous_contracts.empty()) + { + return result; + } + assert(!result.current_contract.name.empty()); + + // There's no guarantee that the contract history will be sorted in metadata (in fact it's unlikely to be) + for (auto& prev : result.previous_contracts) + { + if (prev.contract_to.empty() || (prev.contract_to == result.current_contract.name)) { - version = get_attribute_value(attribute, 0); + // No 'to' contract indicates that this was the last contract before the current one + prev.contract_to = result.current_contract.name; + std::swap(prev, result.previous_contracts.back()); break; } } + assert(result.previous_contracts.back().contract_to == result.current_contract.name); - return { HIWORD(version), LOWORD(version) }; + for (std::size_t size = result.previous_contracts.size() - 1; size; --size) + { + auto& last = result.previous_contracts[size]; + auto itr = std::find_if(result.previous_contracts.begin(), result.previous_contracts.begin() + size, [&](auto const& prev) + { + return prev.contract_to == last.contract_from; + }); + assert(itr != result.previous_contracts.end()); + std::swap(*itr, result.previous_contracts[size - 1]); + } + + return result; } struct interface_info { TypeDef type; bool is_default{}; + bool is_protected{}; bool defaulted{}; bool overridable{}; bool base{}; bool exclusive{}; bool fastabi{}; - std::pair version{}; + // A pair of (relativeContract, version) where 'relativeContract' is the contract the interface was introduced + // in relative to the contract history of the class. E.g. if a class goes from contract 'A' to 'B' to 'C', + // 'relativeContract' would be '0' for an interface introduced in contract 'A', '1' for an interface introduced + // in contract 'B', etc. This is only set/valid for 'fastabi' interfaces + std::pair relative_version{}; std::vector> generic_param_stack{}; }; @@ -362,6 +578,7 @@ namespace cppwinrt auto type = impl.Interface(); auto name = w.write_temp("%", type); info.is_default = has_attribute(impl, "Windows.Foundation.Metadata", "DefaultAttribute"); + info.is_protected = has_attribute(impl, "Windows.Foundation.Metadata", "ProtectedAttribute"); info.defaulted = !base && (defaulted || info.is_default); { @@ -421,7 +638,6 @@ namespace cppwinrt } info.exclusive = has_attribute(info.type, "Windows.Foundation.Metadata", "ExclusiveToAttribute"); - info.version = get_version(info.type); get_interfaces_impl(w, result, info.defaulted, info.overridable, base, info.generic_param_stack, info.type.InterfaceImpl()); insert_or_assign(result, name, std::move(info)); } @@ -443,10 +659,32 @@ namespace cppwinrt return result; } - auto count = std::count_if(result.begin(), result.end(), [](auto&& pair) + auto history = get_contract_history(type); + std::size_t count = 0; + for (auto& pair : result) { - return pair.second.exclusive && !pair.second.base && !pair.second.overridable; - }); + if (pair.second.exclusive && !pair.second.base && !pair.second.overridable) + { + ++count; + + auto introduced = get_initial_contract_version(pair.second.type); + pair.second.relative_version.second = introduced.version; + + auto itr = std::find_if(history.previous_contracts.begin(), history.previous_contracts.end(), [&](previous_contract const& prev) + { + return prev.contract_from == introduced.name; + }); + if (itr != history.previous_contracts.end()) + { + pair.second.relative_version.first = static_cast(itr - history.previous_contracts.begin()); + } + else + { + assert(history.current_contract.name == introduced.name); + pair.second.relative_version.first = static_cast(history.previous_contracts.size()); + } + } + } std::partial_sort(result.begin(), result.begin() + count, result.end(), [](auto&& left_pair, auto&& right_pair) { @@ -482,9 +720,9 @@ namespace cppwinrt return left_enabled; } - if (left.version != right.version) + if (left.relative_version != right.relative_version) { - return left.version < right.version; + return left.relative_version < right.relative_version; } return left_pair.first < right_pair.first; @@ -498,6 +736,27 @@ namespace cppwinrt return result; } + static bool implements_interface(TypeDef const& type, std::string_view const& name) + { + for (auto&& impl : type.InterfaceImpl()) + { + const auto iface = impl.Interface(); + if (iface.type() != TypeDefOrRef::TypeSpec && type_name(iface) == name) + { + return true; + } + } + + if (auto base = get_base_class(type)) + { + return implements_interface(base, name); + } + else + { + return false; + } + } + bool has_fastabi_tearoffs(writer& w, TypeDef const& type) { for (auto&& [name, info] : get_interfaces(w, type)) @@ -604,7 +863,7 @@ namespace cppwinrt { if (auto visibility = std::get_if(&std::get(arg.value).value)) { - info.visible = std::get(visibility->value) == 2; + info.visible = std::get(visibility->value) == 2; break; } } diff --git a/cppwinrt/main.cpp b/cppwinrt/main.cpp index b46e08f58..33bb9248e 100644 --- a/cppwinrt/main.cpp +++ b/cppwinrt/main.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include +#include #include "strings.h" #include "settings.h" #include "type_writers.h" @@ -25,7 +26,7 @@ namespace cppwinrt { "verbose", 0, 0, {}, "Show detailed progress information" }, { "overwrite", 0, 0, {}, "Overwrite generated component files" }, { "prefix", 0, 0, {}, "Use dotted namespace convention for component files (defaults to folders)" }, - { "pch", 0, 1, "", "Specify name of precompiled header file (defaults to pch.h)" }, + { "pch", 0, 1, "", "Specify name of precompiled header file (defaults to pch.h; use '.' to disable)" }, { "include", 0, option::no_max, "", "One or more prefixes to include in input" }, { "exclude", 0, option::no_max, "", "One or more prefixes to exclude from input" }, { "base", 0, 0, {}, "Generate base.h unconditionally" }, @@ -34,10 +35,14 @@ namespace cppwinrt { "?", 0, option::no_max, {}, {} }, { "library", 0, 1, "", "Specify library prefix (defaults to winrt)" }, { "filter" }, // One or more prefixes to include in input (same as -include) - { "license", 0, 0 }, // Generate license comment + { "license", 0, 1, "[]", "Generate license comment from template file" }, { "brackets", 0, 0 }, // Use angle brackets for #includes (defaults to quotes) { "fastabi", 0, 0 }, // Enable support for the Fast ABI { "ignore_velocity", 0, 0 }, // Ignore feature staging metadata and always include implementations + { "synchronous", 0, 0 }, // Instructs cppwinrt to run on a single thread to avoid file system issues in batch builds + { "modules", 0, 0, {}, "Generate per-namespace C++20 module interface units (.ixx)" }, + { "module_include", 0, option::no_max, "", "Filter which namespaces are included in module .ixx generation" }, + { "module_exclude", 0, option::no_max, "", "Filter which namespaces are excluded from module .ixx generation" }, }; static void print_usage(writer& w) @@ -69,10 +74,14 @@ Copyright (c) Microsoft Corporation. All rights reserved. Where is one or more of: path Path to winmd file or recursively scanned folder - local Local ^%WinDir^%\System32\WinMetadata folder +)" +#if defined(_WIN32) || defined(_WIN64) +R"( local Local ^%WinDir^%\System32\WinMetadata folder sdk[+] Current version of Windows SDK [with extensions] 10.0.12345.0[+] Specific version of Windows SDK [with extensions] -)"; +)" +#endif + ; w.write(format, CPPWINRT_VERSION_STRING, bind_each(printOption, options)); } @@ -80,6 +89,7 @@ Where is one or more of: { settings.verbose = args.exists("verbose"); settings.fastabi = args.exists("fastabi"); + settings.modules = args.exists("modules"); settings.input = args.files("input", database::is_database); settings.reference = args.files("reference", database::is_database); @@ -87,13 +97,22 @@ Where is one or more of: settings.component = args.exists("component"); settings.base = args.exists("base"); + for (auto&& ns : args.values("module_include")) + { + settings.module_include.insert(ns); + } + for (auto&& ns : args.values("module_exclude")) + { + settings.module_exclude.insert(ns); + } + settings.license = args.exists("license"); settings.brackets = args.exists("brackets"); - path output_folder = args.value("output"); + path output_folder = args.value("output", "."); create_directories(output_folder / "winrt/impl"); settings.output_folder = canonical(output_folder).string(); - settings.output_folder += '\\'; + settings.output_folder += std::filesystem::path::preferred_separator; for (auto && include : args.values("include")) { @@ -110,6 +129,40 @@ Where is one or more of: settings.exclude.insert(exclude); } + if (settings.license) + { + std::string license_arg = args.value("license"); + if (license_arg.empty()) + { + settings.license_template = R"(// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +)"; + } + else + { + std::filesystem::path template_path{ license_arg }; + std::ifstream template_file(absolute(template_path)); + if (template_file.fail()) + { + throw_invalid("Cannot read license template file '", absolute(template_path).string() + "'"); + } + std::string line_buf; + while (getline(template_file, line_buf)) + { + if (line_buf.empty()) + { + settings.license_template += "//\n"; + } + else + { + settings.license_template += "// "; + settings.license_template += line_buf; + settings.license_template += "\n"; + } + } + } + } + if (settings.component) { settings.component_overwrite = args.exists("overwrite"); @@ -145,7 +198,7 @@ Where is one or more of: { create_directories(component); settings.component_folder = canonical(component).string(); - settings.component_folder += '\\'; + settings.component_folder += std::filesystem::path::preferred_separator; } } } @@ -160,6 +213,13 @@ Where is one or more of: static void build_filters(cache const& c) { + // Build module_filter from -module_include / -module_exclude args. + // This controls which namespaces get .ixx files without affecting header generation. + if (!settings.module_include.empty() || !settings.module_exclude.empty()) + { + settings.module_filter = { settings.module_include, settings.module_exclude }; + } + if (settings.reference.empty()) { return; @@ -242,6 +302,79 @@ Where is one or more of: c.remove_type("Windows.Foundation.Numerics", "Vector4"); } + // Tarjan's algorithm for finding strongly connected components in the + // namespace dependency graph. Namespaces in an SCC have cyclic deps and + // must be combined into a single module. + static std::vector> find_sccs( + std::map, std::less<>> const& graph) + { + struct context + { + std::map index_of; + std::map lowlink; + std::map on_stack; + std::vector stack; + int next_index = 0; + std::vector> result; + + void strongconnect(std::string const& v, + std::map, std::less<>> const& g) + { + index_of[v] = next_index; + lowlink[v] = next_index; + next_index++; + stack.push_back(v); + on_stack[v] = true; + + auto it = g.find(v); + if (it != g.end()) + { + for (auto& w : it->second) + { + if (g.find(w) == g.end()) + { + continue; // dep not in graph (not a projected namespace) + } + + if (index_of.find(w) == index_of.end()) + { + strongconnect(w, g); + lowlink[v] = (std::min)(lowlink[v], lowlink[w]); + } + else if (on_stack[w]) + { + lowlink[v] = (std::min)(lowlink[v], index_of[w]); + } + } + } + + if (lowlink[v] == index_of[v]) + { + std::vector scc; + std::string w; + do + { + w = stack.back(); + stack.pop_back(); + on_stack[w] = false; + scc.push_back(std::move(w)); + } while (scc.back() != v); + result.push_back(std::move(scc)); + } + } + }; + + context ctx; + for (auto& [node, _] : graph) + { + if (ctx.index_of.find(node) == ctx.index_of.end()) + { + ctx.strongconnect(node, graph); + } + } + return std::move(ctx.result); + } + static int run(int const argc, char** argv) { int result{}; @@ -259,7 +392,7 @@ Where is one or more of: } process_args(args); - cache c{ get_files_to_cache() }; + cache c{ get_files_to_cache(), [](TypeDef const& type) { return type.Flags().WindowsRuntime(); } }; remove_foundation_types(c); build_filters(c); settings.base = settings.base || (!settings.component && settings.projection_filter.empty()); @@ -267,7 +400,19 @@ Where is one or more of: if (settings.verbose) { - w.write(" tool: %\n", canonical(path(argv[0]).replace_extension("exe")).string()); + { + char* path = argv[0]; +#if defined(_WIN32) || defined(_WIN64) + char path_buf[32768]; + DWORD path_size = GetModuleFileNameA(nullptr, path_buf, sizeof(path_buf)); + if (path_size) + { + path_buf[sizeof(path_buf) - 1] = 0; + path = path_buf; + } +#endif + w.write(" tool: %\n", path); + } w.write(" ver: %\n", CPPWINRT_VERSION_STRING); for (auto&& file : settings.input) @@ -290,11 +435,31 @@ Where is one or more of: w.flush_to_console(); task_group group; - writer ixx; - write_preamble(ixx); - ixx.write("module;\n"); - ixx.write(strings::base_includes); - ixx.write("\nexport module winrt;\n#define WINRT_EXPORT export\n\n"); + group.synchronous(args.exists("synchronous")); + + // Dependency collection for per-namespace modules (v2) + std::map, std::less<>> ns_deps_map; + std::set projected_namespaces; + std::mutex ns_deps_mutex; + + // First pass: determine which namespaces will be in the module. + // This includes namespaces from this invocation AND those from other invocations + // (e.g., platform namespaces when building a component). The module_filter + // tells us which namespaces have modules across all invocations. + if (settings.modules) + { + for (auto&& [ns, members] : c.namespaces()) + { + if (!has_projected_types(members)) + { + continue; + } + if (settings.module_filter.empty() || settings.module_filter.includes(members)) + { + projected_namespaces.insert(std::string(ns)); + } + } + } for (auto&&[ns, members] : c.namespaces()) { @@ -303,21 +468,29 @@ Where is one or more of: continue; } - ixx.write("#include \"winrt/%.h\"\n", ns); - group.add([&, &ns = ns, &members = members] { - write_namespace_0_h(ns, members); - write_namespace_1_h(ns, members); - write_namespace_2_h(ns, members); - write_namespace_h(c, ns, members); + bool in_module = projected_namespaces.count(std::string(ns)) > 0; + std::set ns_deps; + auto* deps_ptr = (settings.modules && in_module) ? &ns_deps : nullptr; + + write_namespace_0_h(ns, members, deps_ptr); + write_namespace_1_h(ns, members, deps_ptr); + write_namespace_2_h(ns, members, deps_ptr); + write_namespace_h(c, ns, members, deps_ptr); + + if (settings.modules && in_module) + { + std::lock_guard lock(ns_deps_mutex); + ns_deps_map[std::string(ns)] = std::move(ns_deps); + } }); } if (settings.base) { write_base_h(); - ixx.flush_to_file(settings.output_folder + "winrt/winrt.ixx"); + write_macros_h(); } if (settings.component) @@ -352,6 +525,64 @@ Where is one or more of: group.get(); + // Generate per-namespace module interface files (.ixx) + // + // Each projected namespace gets its own C++20 named module (winrt.). + // Namespaces that form dependency cycles are detected using Tarjan's SCC algorithm + // and consolidated: one namespace "owns" the SCC module (containing all declarations), + // while others get thin re-export stubs so 'import winrt.;' always works. + // + // Base infrastructure modules (winrt_base, winrt_numerics) are only generated + // for platform projection builds (-base flag). + if (settings.modules) + { + if (settings.base) + { + write_numerics_ixx(); + write_base_ixx(); + } + + // Tarjan's SCC algorithm for cyclic namespace dependencies + auto sccs = find_sccs(ns_deps_map); + + for (auto& scc : sccs) + { + if (scc.size() == 1) + { + // Standalone namespace module + auto& ns = scc[0]; + write_namespace_ixx(ns, ns_deps_map[ns]); + } + else + { + // SCC: choose owner (alphabetically first), others re-export + std::sort(scc.begin(), scc.end()); + auto& owner = scc[0]; + + // External deps = union of all SCC members' deps, minus SCC members themselves + std::set external_deps; + std::set scc_set(scc.begin(), scc.end()); + for (auto& ns : scc) + { + for (auto& dep : ns_deps_map[ns]) + { + if (!scc_set.count(dep)) + { + external_deps.insert(dep); + } + } + } + + write_namespace_scc_owner_ixx(c, owner, scc, external_deps); + + for (size_t i = 1; i < scc.size(); ++i) + { + write_namespace_reexport_ixx(scc[i], owner); + } + } + } + } + if (settings.verbose) { w.write(" time: %ms\n", get_elapsed_time(start)); diff --git a/cppwinrt/packages.config b/cppwinrt/packages.config index f87c72f28..9b3264e46 100644 --- a/cppwinrt/packages.config +++ b/cppwinrt/packages.config @@ -1,4 +1,4 @@  - + \ No newline at end of file diff --git a/cppwinrt/pch.h b/cppwinrt/pch.h index 335324767..e6a31443e 100644 --- a/cppwinrt/pch.h +++ b/cppwinrt/pch.h @@ -1,5 +1,6 @@ #pragma once +#include #include "cmd_reader.h" #include #include "task_group.h" diff --git a/cppwinrt/settings.h b/cppwinrt/settings.h index 7655f8cc2..110e64917 100644 --- a/cppwinrt/settings.h +++ b/cppwinrt/settings.h @@ -10,6 +10,7 @@ namespace cppwinrt std::string output_folder; bool base{}; bool license{}; + std::string license_template; bool brackets{}; bool verbose{}; bool component{}; @@ -30,6 +31,12 @@ namespace cppwinrt bool fastabi{}; std::map fastabi_cache; + + bool modules{}; // Generate per-namespace C++20 module interface units (.ixx) + + std::set module_include; + std::set module_exclude; + winmd::reader::filter module_filter; }; extern settings_type settings; diff --git a/cppwinrt/task_group.h b/cppwinrt/task_group.h index 4e5d94b5f..800b8eb40 100644 --- a/cppwinrt/task_group.h +++ b/cppwinrt/task_group.h @@ -17,14 +17,22 @@ namespace cppwinrt } } + void synchronous(bool synchronous) noexcept + { + m_synchronous = synchronous; + } + template void add(T&& callback) { -#if defined(_DEBUG) - callback(); -#else - m_tasks.push_back(std::async(std::forward(callback))); -#endif + if (m_synchronous) + { + callback(); + } + else + { + m_tasks.push_back(std::async(std::forward(callback))); + } } void get() @@ -45,5 +53,6 @@ namespace cppwinrt private: std::vector> m_tasks; + bool m_synchronous{}; }; } diff --git a/cppwinrt/text_writer.h b/cppwinrt/text_writer.h index 5045b9395..c6fc380b3 100644 --- a/cppwinrt/text_writer.h +++ b/cppwinrt/text_writer.h @@ -1,18 +1,38 @@ #pragma once #include +#include #include #include #include #include #include +#include namespace cppwinrt { inline std::string file_to_string(std::string const& filename) { - std::ifstream file(filename, std::ios::binary); - return static_cast(std::stringstream() << file.rdbuf()).str(); + std::ifstream file(filename, std::ios::binary | std::ios::ate); + if (!file) { return{}; } + + const auto stream_size = file.tellg(); + if (stream_size == std::ifstream::pos_type(-1)) + { + return {}; + } + + file.seekg(0); + + auto size = static_cast(stream_size); + std::string result(size, '\0'); + file.read(result.data(), size); + if (!file) + { + result.resize(static_cast(file.gcount())); + } + + return result; } template @@ -65,7 +85,7 @@ namespace cppwinrt #if defined(_DEBUG) if (debug_trace) { - ::printf("%.*s", static_cast(value.size()), value.data()); + std::printf("%.*s", static_cast(value.size()), value.data()); } #endif } @@ -77,7 +97,7 @@ namespace cppwinrt #if defined(_DEBUG) if (debug_trace) { - ::printf("%c", value); + std::printf("%c", value); } #endif } @@ -103,22 +123,32 @@ namespace cppwinrt f(*static_cast(this)); } - void write(int32_t const value) + void write(int const value) { write(std::to_string(value)); } - void write(uint32_t const value) + void write(unsigned int const value) { write(std::to_string(value)); } - void write(int64_t const value) + void write(signed long const value) { write(std::to_string(value)); } - void write(uint64_t const value) + void write(unsigned long const value) + { + write(std::to_string(value)); + } + + void write(signed long long const value) + { + write(std::to_string(value)); + } + + void write(unsigned long long const value) { write(std::to_string(value)); } @@ -127,7 +157,16 @@ namespace cppwinrt void write_printf(char const* format, Args const&... args) { char buffer[128]; - size_t const size = sprintf_s(buffer, format, args...); +#if defined(_WIN32) || defined(_WIN64) + std::size_t const size = sprintf_s(buffer, format, args...); +#else + std::size_t size = std::snprintf(buffer, sizeof(buffer), format, args...); + if (size > sizeof(buffer) - 1) + { + std::fprintf(stderr, "\n*** WARNING: writer_base::write_printf -- buffer too small\n"); + size = sizeof(buffer) - 1; + } +#endif write(std::string_view{ buffer, size }); } @@ -147,8 +186,8 @@ namespace cppwinrt void flush_to_console(bool to_stdout = true) noexcept { - fprintf(to_stdout ? stdout : stderr, "%.*s", static_cast(m_first.size()), m_first.data()); - fprintf(to_stdout ? stdout : stderr, "%.*s", static_cast(m_second.size()), m_second.data()); + std::fprintf(to_stdout ? stdout : stderr, "%.*s", static_cast(m_first.size()), m_first.data()); + std::fprintf(to_stdout ? stdout : stderr, "%.*s", static_cast(m_second.size()), m_second.data()); m_first.clear(); m_second.clear(); } @@ -157,9 +196,18 @@ namespace cppwinrt { if (!file_equal(filename)) { - std::ofstream file{ filename, std::ios::out | std::ios::binary }; - file.write(m_first.data(), m_first.size()); - file.write(m_second.data(), m_second.size()); + std::ofstream file; + file.exceptions(std::ofstream::failbit | std::ofstream::badbit); + try + { + file.open(filename, std::ios::out | std::ios::binary); + file.write(m_first.data(), m_first.size()); + file.write(m_second.data(), m_second.size()); + } + catch (std::ofstream::failure const& e) + { + throw std::filesystem::filesystem_error(e.what(), filename, std::io_errc::stream); + } } m_first.clear(); m_second.clear(); @@ -188,18 +236,15 @@ namespace cppwinrt bool file_equal(std::string const& filename) const { - if (!std::filesystem::exists(filename)) + // Non-throwing file_size returns uintmax_t(-1) on errors, which shouldn't ever be the size of m_first or m_second + std::error_code ec; + if (std::filesystem::file_size(filename, ec) != m_first.size() + m_second.size()) { return false; } auto file = file_to_string(filename); - if (file.size() != m_first.size() + m_second.size()) - { - return false; - } - if (!std::equal(m_first.begin(), m_first.end(), file.begin(), file.begin() + m_first.size())) { return false; @@ -214,9 +259,9 @@ namespace cppwinrt private: - static constexpr uint32_t count_placeholders(std::string_view const& format) noexcept + static constexpr std::uint32_t count_placeholders(std::string_view const& format) noexcept { - uint32_t count{}; + std::uint32_t count{}; bool escape{}; for (auto c : format) @@ -303,7 +348,7 @@ namespace cppwinrt { struct indent_guard { - indent_guard(indented_writer_base& w, int32_t offset = 1) noexcept : m_writer(w), m_offset(offset) + indent_guard(indented_writer_base& w, std::int32_t offset = 1) noexcept : m_writer(w), m_offset(offset) { m_writer.m_indent += m_offset; } @@ -315,13 +360,13 @@ namespace cppwinrt private: indented_writer_base& m_writer; - int32_t m_offset{}; + std::int32_t m_offset{}; }; void write_indent() { - for (int32_t i = 0; i < m_indent; i++) + for (std::int32_t i = 0; i < m_indent; i++) { writer_base::write_impl(" "); } @@ -389,7 +434,7 @@ namespace cppwinrt return result; } - int32_t m_indent{}; + std::int32_t m_indent{}; }; @@ -452,7 +497,7 @@ namespace cppwinrt { return [&](auto& writer) { - bool first{ true }; + bool first = true; for (auto&& item : list) { @@ -475,7 +520,7 @@ namespace cppwinrt { return [&](auto& writer) { - bool first{ true }; + bool first = true; for (auto&& item : list) { diff --git a/cppwinrt/type_writers.h b/cppwinrt/type_writers.h index fffc96e3b..df17450f1 100644 --- a/cppwinrt/type_writers.h +++ b/cppwinrt/type_writers.h @@ -92,6 +92,18 @@ namespace cppwinrt return result; } + static bool transform_special_numeric_type(std::string_view& name) + { + if (name == "Matrix3x2") { name = "float3x2"; return true; } + else if (name == "Matrix4x4") { name = "float4x4"; return true; } + else if (name == "Plane") { name = "plane"; return true; } + else if (name == "Quaternion") { name = "quaternion"; return true; } + else if (name == "Vector2") { name = "float2"; return true; } + else if (name == "Vector3") { name = "float3"; return true; } + else if (name == "Vector4") { name = "float4"; return true; } + return false; + } + struct writer : writer_base { using writer_base::write; @@ -143,6 +155,27 @@ namespace cppwinrt writer* owner; }; + template + struct member_value_guard + { + writer* const owner; + T writer::* const member; + T const previous; + explicit member_value_guard(writer* arg, T writer::* ptr, T value) : + owner(arg), member(ptr), previous(std::exchange(owner->*member, value)) + { + } + + ~member_value_guard() + { + owner->*member = previous; + } + + member_value_guard(member_value_guard const&) = delete; + member_value_guard& operator=(member_value_guard const&) = delete; + + }; + void add_depends(TypeDef const& type) { auto ns = type.TypeNamespace(); @@ -184,12 +217,27 @@ namespace cppwinrt return generic_param_guard{ this }; } - void write_value(int32_t value) + [[nodiscard]] auto push_abi_types(bool value) + { + return member_value_guard(this, &writer::abi_types, value); + } + + [[nodiscard]] auto push_async_types(bool value) + { + return member_value_guard(this, &writer::async_types, value); + } + + [[nodiscard]] auto push_delegate_types(bool value) + { + return member_value_guard(this, &writer::delegate_types, value); + } + + void write_value(std::int32_t value) { write_printf("%d", value); } - void write_value(uint32_t value) + void write_value(std::uint32_t value) { write_printf("%#0x", value); } @@ -237,12 +285,10 @@ namespace cppwinrt if (!empty(generics)) { - write("@::%<%>", ns, remove_tick(name), bind_list(", ", generics)); + write("winrt::@::%<%>", ns, remove_tick(name), bind_list(", ", generics)); return; } - // TODO: get rid of all these renames once parity with cppwinrt.exe has been reached... - if (name == "EventRegistrationToken" && ns == "Windows.Foundation") { write("winrt::event_token"); @@ -255,27 +301,19 @@ namespace cppwinrt { auto category = get_category(type); - if (ns == "Windows.Foundation.Numerics") + if (ns == "Windows.Foundation.Numerics" && transform_special_numeric_type(name)) { - if (name == "Matrix3x2") { name = "float3x2"; } - else if (name == "Matrix4x4") { name = "float4x4"; } - else if (name == "Plane") { name = "plane"; } - else if (name == "Quaternion") { name = "quaternion"; } - else if (name == "Vector2") { name = "float2"; } - else if (name == "Vector3") { name = "float3"; } - else if (name == "Vector4") { name = "float4"; } - - write("@::%", ns, name); + write("winrt::@::%", ns, name); } else if (category == category::struct_type) { if ((name == "DateTime" || name == "TimeSpan") && ns == "Windows.Foundation") { - write("int64_t"); + write("std::int64_t"); } else if ((name == "Point" || name == "Size" || name == "Rect") && ns == "Windows.Foundation") { - write("@::%", ns, name); + write("winrt::@::%", ns, name); } else if (delegate_types) { @@ -307,11 +345,11 @@ namespace cppwinrt else if (name == "Vector3") { name = "float3"; } else if (name == "Vector4") { name = "float4"; } - write("@::%", ns, name); + write("winrt::@::%", ns, name); } else { - write("@::%", ns, name); + write("winrt::@::%", ns, name); } } } @@ -364,14 +402,14 @@ namespace cppwinrt if (consume_types) { - static constexpr std::string_view iterable("Windows::Foundation::Collections::IIterable<"sv); - static constexpr std::string_view vector_view("Windows::Foundation::Collections::IVectorView<"sv); - static constexpr std::string_view map_view("Windows::Foundation::Collections::IMapView<"sv); - static constexpr std::string_view vector("Windows::Foundation::Collections::IVector<"sv); - static constexpr std::string_view map("Windows::Foundation::Collections::IMap<"sv); + static constexpr std::string_view iterable("winrt::Windows::Foundation::Collections::IIterable<"sv); + static constexpr std::string_view vector_view("winrt::Windows::Foundation::Collections::IVectorView<"sv); + static constexpr std::string_view map_view("winrt::Windows::Foundation::Collections::IMapView<"sv); + static constexpr std::string_view vector("winrt::Windows::Foundation::Collections::IVector<"sv); + static constexpr std::string_view map("winrt::Windows::Foundation::Collections::IMap<"sv); consume_types = false; - auto full_name = write_temp("@::%<%>", ns, name, bind_list(", ", type.GenericArgs())); + auto full_name = write_temp("winrt::@::%<%>", ns, name, bind_list(", ", type.GenericArgs())); consume_types = true; if (starts_with(full_name, iterable)) @@ -423,7 +461,7 @@ namespace cppwinrt } else { - write("@::%<%>", ns, name, bind_list(", ", type.GenericArgs())); + write("winrt::@::%<%>", ns, name, bind_list(", ", type.GenericArgs())); } } } @@ -435,14 +473,14 @@ namespace cppwinrt { if (type == ElementType::Boolean) { write("bool"); } else if (type == ElementType::Char) { write("char16_t"); } - else if (type == ElementType::I1) { write("int8_t"); } - else if (type == ElementType::U1) { write("uint8_t"); } - else if (type == ElementType::I2) { write("int16_t"); } - else if (type == ElementType::U2) { write("uint16_t"); } - else if (type == ElementType::I4) { write("int32_t"); } - else if (type == ElementType::U4) { write("uint32_t"); } - else if (type == ElementType::I8) { write("int64_t"); } - else if (type == ElementType::U8) { write("uint64_t"); } + else if (type == ElementType::I1) { write("std::int8_t"); } + else if (type == ElementType::U1) { write("std::uint8_t"); } + else if (type == ElementType::I2) { write("std::int16_t"); } + else if (type == ElementType::U2) { write("std::uint16_t"); } + else if (type == ElementType::I4) { write("std::int32_t"); } + else if (type == ElementType::U4) { write("std::uint32_t"); } + else if (type == ElementType::I8) { write("std::int64_t"); } + else if (type == ElementType::U8) { write("std::uint64_t"); } else if (type == ElementType::R4) { write("float"); } else if (type == ElementType::R8) { write("double"); } else if (type == ElementType::String) @@ -468,7 +506,7 @@ namespace cppwinrt } else { - write("Windows::Foundation::IInspectable"); + write("winrt::Windows::Foundation::IInspectable"); } } else diff --git a/cross-mingw-toolchain.cmake b/cross-mingw-toolchain.cmake new file mode 100644 index 000000000..23156e11b --- /dev/null +++ b/cross-mingw-toolchain.cmake @@ -0,0 +1,42 @@ +# This is a cmake-toolchain(5) file that can be used to cross-build +# cppwinrt.exe from Linux or other operating systems using a mingw-w64 cross +# toolchain. This should work with both GCC-based and llvm-mingw toolchains. +# +# Example usage with external toolchain: +# +# $ cmake -S . -B build/cross_x64/ \ +# --toolchain cross-mingw-toolchain.cmake \ +# -DMINGW_BIN_PATH=/opt/llvm-mingw/bin \ +# -DCMAKE_BUILD_TYPE=RelWithDebInfo \ +# -DCMAKE_INSTALL_PREFIX=$PWD/install/ +# +# Example usage with toolchain installed system-wide: +# +# $ cmake -S . -B build/cross_i686/ \ +# --toolchain cross-mingw-toolchain.cmake \ +# -DCMAKE_SYSTEM_PROCESSOR=i686 \ +# -DCMAKE_BUILD_TYPE=RelWithDebInfo \ +# -DCMAKE_INSTALL_PREFIX=$PWD/install/ + + +set(CMAKE_SYSTEM_NAME Windows) +if(NOT DEFINED CMAKE_SYSTEM_PROCESSOR) + set(CMAKE_SYSTEM_PROCESSOR x86_64) +endif() + +set(TOOLCHAIN_PREFIX ${CMAKE_SYSTEM_PROCESSOR}-w64-mingw32) + +if(DEFINED MINGW_BIN_PATH) + set(TOOLCHAIN_PREFIX "${MINGW_BIN_PATH}/${TOOLCHAIN_PREFIX}") +endif() + +set(CMAKE_C_COMPILER "${TOOLCHAIN_PREFIX}-gcc") +set(CMAKE_CXX_COMPILER "${TOOLCHAIN_PREFIX}-g++") +set(CMAKE_RC_COMPILER "${TOOLCHAIN_PREFIX}-windres") + +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) + +unset(TOOLCHAIN_PREFIX) diff --git a/CODE_OF_CONDUCT.md b/docs/code_of_conduct.md similarity index 93% rename from CODE_OF_CONDUCT.md rename to docs/code_of_conduct.md index f9ba8cf65..6257f2e76 100644 --- a/CODE_OF_CONDUCT.md +++ b/docs/code_of_conduct.md @@ -6,4 +6,4 @@ Resources: - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) -- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns +- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns \ No newline at end of file diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 000000000..9671ac0f7 --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,26 @@ +## Contributing + +Thanks for your interest in C++/WinRT! You are welcome to contribute by filing issues for problems you encounter. +Project maintainers will consider changes that improve compatibility or fix bugs. + +The following process is required in order to have a pull request considered: + +* File an issue for any change you would like to propose. This can start a discussion so we can agree on an approach +before you invest a large amount of time. Due to the large number of dependent projects, contributions that include +compatibility risk or added complexity will generally be rejected. + +* Contributors will need the help of a project maintainer to verify the change with the Windows operating system +build system. If a project maintainer is available to provide guidance and mentorship for the change, a pull request +may be opened to begin the formal review process. + +Most contributions require you to agree to a Contributor License Agreement (CLA) declaring that +you have the right to, and actually do, grant us the rights to use your contribution. For details, +visit https://cla.opensource.microsoft.com. + +When you submit a pull request, a CLA bot will automatically determine whether you need to provide +a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions +provided by the bot. You will only need to do this once across all repos using our CLA. + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/docs/modules-design.md b/docs/modules-design.md new file mode 100644 index 000000000..8327231a7 --- /dev/null +++ b/docs/modules-design.md @@ -0,0 +1,190 @@ +# C++/WinRT Per-Namespace Modules: Design & Internals + +This document describes the design and implementation of per-namespace C++20 module support in C++/WinRT. It is intended for cppwinrt maintainers and contributors. + +## Architecture Overview + +The module system generates one C++20 named module per WinRT namespace. Each module encapsulates the same content as the traditional header files but exports declarations via `WINRT_EXPORT` in module purview. + +``` +┌─────────────────────────────────────────────────────────────────┐ +│ winrt_numerics.ixx ── export module winrt_numerics; │ +│ winrt_base.ixx ── export module winrt_base; │ +│ export import winrt_numerics; │ +│ winrt.Windows.Foundation.ixx │ +│ ── export module winrt.Windows.Foundation; │ +│ import std; export import winrt_base; │ +│ import winrt.Windows.Foundation.Collections; │ +│ #include "winrt/impl/Windows.Foundation.0.h" │ +│ ... │ +│ #include "winrt/Windows.Foundation.h" │ +└─────────────────────────────────────────────────────────────────┘ +``` + +## Key Design Decisions + +### Unconditional Guards + +Module guards (`WINRT_IMPL_BUILD_MODULE`, `WINRT_IMPORT_MODULE`) are emitted unconditionally in generated projection headers — they are always present regardless of whether `-modules` was passed to cppwinrt.exe. The `-modules` flag controls `.ixx` generation and whether generated component files (`module.g.cpp`, stub `.cpp`) use module imports. This means: + +- Projection headers generated without `-modules` still work correctly when later compiled inside a module interface unit +- No regeneration of projection headers needed when switching between header and module consumption + +### WINRT_EXPORT Macro + +`WINRT_EXPORT` is defined in `base_macros.h` (generated as `winrt/base_macros.h`): +- When `WINRT_IMPL_BUILD_MODULE` is defined (inside `.ixx` compilation): `export extern "C++"` +- Otherwise (header mode): empty + +All `namespace winrt::impl` and `namespace std` blocks use `WINRT_EXPORT` so they export correctly from modules. The `extern "C++"` wrapping enables include-before-import compatibility (same technique as MSVC STL). + +### Per-Namespace vs Monolithic + +Unlike the v1 approach (single `import winrt;`), v2 generates one module per namespace. This provides: + +- **Finer granularity**: Import only what you need +- **Better parallelism**: Independent modules compile in parallel +- **Component module support**: Component namespaces get their own modules + +The trade-off is handling dependency cycles between namespaces (see SCC below). + +This is enforced via the MSBuild `CppWinRTConsumeModule` metadata on ProjectReference — it only points at the platform module builder, not at component projects. + +## Code Generator Pipeline + +### Entry Point: `main.cpp` + +1. **Namespace enumeration**: First pass determines which namespaces are module-eligible using `module_filter` (from `-module_include`/`-module_exclude`) + +2. **Header generation**: Standard header generation with optional dependency collection. When `-modules` is active and a namespace is in the module, `write_namespace_*_h()` functions populate `ns_deps` sets via the `deps_ptr` parameter + +3. **Dependency graph construction**: After all headers are generated, `ns_deps_map` contains the full namespace dependency graph + +4. **SCC detection**: Tarjan's algorithm (`find_sccs()`) identifies strongly-connected components + +5. **Module generation**: For each SCC: + - Size 1: `write_namespace_ixx()` — standalone module + - Size > 1: `write_namespace_scc_owner_ixx()` for the owner (alphabetically first) + `write_namespace_reexport_ixx()` for others + +### CLI Options + +| Flag | Description | +|-|-| +| `-modules` | Enable `.ixx` generation | +| `-module_include ...` | Only generate modules for these namespace prefixes | +| `-module_exclude ...` | Skip these namespace prefixes | + +The `module_filter` is populated from these flags and checked against ALL cache namespaces (not just the projection filter). This is important for component builds where the platform namespaces are not being projected but their modules exist from a prior builder invocation. + +### Generated Files + +| File | When Generated | Purpose | +|-|-|-| +| `winrt/base_macros.h` | Always with `-base` | Macros for module builds (WINRT_EXPORT, etc.) | +| `winrt/winrt_base.ixx` | `-modules -base` | Core types module | +| `winrt/winrt_numerics.ixx` | `-modules -base` | Numerics module | +| `winrt/winrt..ixx` | `-modules` | Per-namespace module | + +## SCC (Strongly Connected Components) + +### The Problem + +WinRT namespaces have cyclic dependencies. For example: +- `Windows.Foundation` depends on `Windows.Foundation.Collections` (via `IVector`, `IMap`, etc.) +- `Windows.Foundation.Collections` depends on `Windows.Foundation` (via `IAsyncOperation`, `Uri`, etc.) + +C++20 modules cannot have circular imports. If module A imports module B, then module B cannot import module A. + +### The Solution: SCC Consolidation + +Tarjan's algorithm identifies groups of namespaces that form dependency cycles. These groups (SCCs) are consolidated: + +1. **Owner selection**: The alphabetically first namespace in the SCC becomes the "owner" +2. **Owner module**: Contains ALL declarations from ALL SCC namespaces. Forward-declares all types first, then includes headers in phase order (all `*.0.h`, then `*.1.h`, then `*.2.h`, then public headers) +3. **Re-export stubs**: Other SCC members get thin `.ixx` files that just re-export the owner module + +This means `import winrt.Windows.Foundation;` and `import winrt.Windows.Foundation.Collections;` both work — they resolve to the same underlying module. + +### Example Generated Files + +**Owner** (`winrt.Windows.Foundation.ixx`): +```cpp +module; +#define WINRT_IMPL_BUILD_MODULE +#include "winrt/base_macros.h" +// ... + +// This module is an SCC owner (cycle breaker). The following namespaces +// form a dependency cycle and are consolidated into this single module: +// - Windows.Foundation +// - Windows.Foundation.Collections +// Other SCC namespaces are emitted as re-export stubs. + +export module winrt.Windows.Foundation; + +import std; +export import winrt_base; + +// Forward declarations for all SCC namespaces... +// #include all impl headers in phase order... +``` + +**Re-export stub** (`winrt.Windows.Foundation.Collections.ixx`): +```cpp +// NOTE: This module does not define declarations of its own. +// It re-exports all declarations from the 'winrt.Windows.Foundation' module. +export module winrt.Windows.Foundation.Collections; +export import winrt.Windows.Foundation; +``` + +## MSBuild Integration + +### Targets Flow + +``` +CppWinRTResolveModuleReferences (resolves IFC paths from ProjectReference metadata) + ↓ +CppWinRTMakePlatformProjection (generates headers + .ixx for platform types) +CppWinRTMakeReferenceProjection (generates headers + .ixx for referenced WinMDs) +CppWinRTMakeComponentProjection (generates headers + .ixx for component types) + ↓ +CppWinRTAddModuleInterfaces (discovers .ixx files, adds to ClCompile items) + ↓ +FixupCLCompileOptions (MSVC module dependency scanner processes .ixx) + ↓ +ClCompile (compiles .ixx → .ifc + .obj) +``` + +### Key Properties + +- `CppWinRTBuildModule`: Enables `-modules` for all three projections (platform, reference, component), causing `.ixx` generation and compilation. +- `CppWinRTConsumeModule` (ProjectReference metadata): Per-reference opt-in for IFC consumption. When set, suppresses `-modules` on the platform projection so the consumer uses pre-built IFCs from the referenced project instead of generating its own. +- `_CppWinRTConsumesPlatformModules`: Internal property set by `CppWinRTResolveModuleReferences` when any ProjectReference has `CppWinRTConsumeModule=true`. Controls whether the platform projection receives `-modules`. + +### Cross-Project IFC Resolution + +MSVC's module dependency scanner uses `/ifcSearchDir` for within-project module resolution. For cross-project modules, the scanner generates explicit `/reference "module.name=path.ifc"` entries based on the dependency scan results. The `/ifcSearchDir` pointing to the builder's `$(IntDir)` allows the scanner to find the pre-built IFCs. + +## Dependency Collection + +During header generation, when `-modules` is active, each `write_namespace_*_h()` function receives a `deps_ptr` parameter. The writer's `w.depends` map is inspected to find referenced namespaces. Only namespaces that: +1. Exist in the cache +2. Have projected types +3. Are in the module namespace set (or set is empty) + +are added to the dependency set. Self-references are excluded. The union of dependencies from all four header files (`*.0.h`, `*.1.h`, `*.2.h`, `.h`) gives the complete dependency set for a namespace module. + +## Testing + +### test/test_cpp20_module/ (in-repo) + +Standalone test built by the main solution. Uses a PreBuildEvent to run cppwinrt.exe with `-modules -base -module_include "Windows.Foundation"`. Tests URI, events, collections, and coroutines. + +### test/nuget/ (NuGet integration) + +Multi-project solution: +- **TestModuleBuilder**: Static library that pre-builds platform modules +- **TestModuleComponent1**: Component DLL (Greeter class), consumes builder's modules +- **TestModuleComponent2**: Component DLL (GreeterGroup), depends on Component1 +- **TestModuleConsumerApp**: Console app, consumes builder + both components +- **TestModuleApp**: Single-project that builds and consumes its own modules diff --git a/SECURITY.md b/docs/security.md similarity index 72% rename from SECURITY.md rename to docs/security.md index e0dfff56a..b8a28b1c5 100644 --- a/SECURITY.md +++ b/docs/security.md @@ -1,20 +1,20 @@ - + ## Security -Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). -If you believe you have found a security vulnerability in any Microsoft-owned repository that meets Microsoft's [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)) of a security vulnerability, please report it to us as described below. +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. ## Reporting Security Issues **Please do not report security vulnerabilities through public GitHub issues.** -Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report). +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). -If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc). +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). -You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: @@ -28,7 +28,7 @@ Please include the requested information listed below (as much as you can provid This information will help us triage your report more quickly. -If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs. +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. ## Preferred Languages @@ -36,6 +36,6 @@ We prefer all communications to be in English. ## Policy -Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd). +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). - + \ No newline at end of file diff --git a/fast_fwd/arm/thunks.asm b/fast_fwd/arm/thunks.asm deleted file mode 100644 index 9010908b6..000000000 --- a/fast_fwd/arm/thunks.asm +++ /dev/null @@ -1,58 +0,0 @@ -; ARM fast forwarder thunk implementations -; Calling convention: https://docs.microsoft.com/en-us/cpp/build/overview-of-arm-abi-conventions - -#include "ksarm.h" - - IMPORT __guard_check_icall_fptr - - TEXTAREA - - CFG_ALIGN - NESTED_ENTRY InvokeForwarder - - ; Save enregistered args and return address - PROLOG_PUSH {r0-r4, lr} - - ; Replace forwarder abi with owner abi - ldr r1, [r0, #4] - str r1, [sp] - - ; Add offset and index (on stack) - ldr r2, [sp, #24] - ldr r3, [r0, #8] - add r2, r2, r3 - - ; Get method address from owner abi vtable - ldr r0, [r1] - ldr r4, [r0, r2, lsl #2] - mov r0, r4 - - ; Verify indirect call target - mov32 r12, __guard_check_icall_fptr - ldr r12, [r12] - blx r12 - - ; Restore method address, return address, and args - mov r12, r4 - EPILOG_POP {r0-r4, lr} - EPILOG_NOP add sp, #4 - - ; Jump to method - EPILOG_NOP bx r12 - - NESTED_END InvokeForwarder - - ; Define thunks - MACRO - WINRT_FF_THUNK $i - LEAF_ENTRY winrt_ff_thunk$i - ; Note: no scratch registers available (r12/IP is used by CFG), must use stack - mov r12, $i - push r12 - ldr pc, =InvokeForwarder - LEAF_END winrt_ff_thunk$i - MEND - -#include "thunks.inc" - - END \ No newline at end of file diff --git a/fast_fwd/arm64/thunks.asm b/fast_fwd/arm64/thunks.asm index be128ecac..0fe5252a0 100644 --- a/fast_fwd/arm64/thunks.asm +++ b/fast_fwd/arm64/thunks.asm @@ -11,10 +11,9 @@ NESTED_ENTRY InvokeForwarder ; Save enregistered args - PROLOG_SAVE_REG_PAIR fp, lr, #-64! - PROLOG_SAVE_REG_PAIR x19, x20, #16 - PROLOG_NOP stp x0, x1, [sp, #32] - PROLOG_NOP stp x2, x3, [sp, #48] + PROLOG_SAVE_REG_PAIR fp, lr, #-48! + PROLOG_NOP stp x0, x1, [sp, #16] + PROLOG_NOP stp x2, x3, [sp, #32] ; Replace forwarder abi with owner abi ldr x1, [x0, #8] @@ -26,23 +25,20 @@ ; Get method address from owner abi vtable ldr x0, [x1] - ldr x19, [x0, x12, lsl #3] - mov x0, x19 + ldr x15, [x0, x12, lsl #3] ; Verify indirect call target adrp x12, __guard_check_icall_fptr ldr x12, [x12, __guard_check_icall_fptr] blr x12 - ; Restore method address, return address, and args - mov x12, x19 - EPILOG_NOP ldp x2, x3, [sp, #48] - EPILOG_NOP ldp x0, x1, [sp, #32] - EPILOG_RESTORE_REG_PAIR x19, x20, #16 - EPILOG_RESTORE_REG_PAIR fp, lr, #64! + ; Restore return address, and args + EPILOG_NOP ldp x2, x3, [sp, #32] + EPILOG_NOP ldp x0, x1, [sp, #16] + EPILOG_RESTORE_REG_PAIR fp, lr, #48! ; Jump to method - EPILOG_NOP br x12 + EPILOG_NOP br x15 NESTED_END InvokeForwarder diff --git a/fast_fwd/fast_fwd.vcxproj b/fast_fwd/fast_fwd.vcxproj index dbd337e66..b9452d149 100644 --- a/fast_fwd/fast_fwd.vcxproj +++ b/fast_fwd/fast_fwd.vcxproj @@ -17,14 +17,6 @@ Release x64 - - Debug - ARM - - - Release - ARM - Debug ARM64 @@ -39,9 +31,8 @@ {A63B3AD1-AB7B-461E-9FFF-2447F5BCD459} Win32Proj fastfwd - 10.0 - + StaticLibrary true @@ -80,16 +71,10 @@ - true + true false !$(Platform_Arm) - - cppwinrt_fast_forwarder - - - cppwinrt_fast_forwarder - cppwinrt_fast_forwarder @@ -120,12 +105,11 @@ Document src true - true - true + false Document src diff --git a/mingw-support/xmllite.def b/mingw-support/xmllite.def new file mode 100644 index 000000000..c96291d16 --- /dev/null +++ b/mingw-support/xmllite.def @@ -0,0 +1,8 @@ +LIBRARY "XmlLite.dll" +EXPORTS +CreateXmlReader +CreateXmlReaderInputWithEncodingCodePage +CreateXmlReaderInputWithEncodingName +CreateXmlWriter +CreateXmlWriterOutputWithEncodingCodePage +CreateXmlWriterOutputWithEncodingName diff --git a/mingw-support/xmllite_i386.def b/mingw-support/xmllite_i386.def new file mode 100644 index 000000000..79921fce7 --- /dev/null +++ b/mingw-support/xmllite_i386.def @@ -0,0 +1,8 @@ +LIBRARY "XmlLite.dll" +EXPORTS +CreateXmlReader@12 +CreateXmlReaderInputWithEncodingCodePage@24 +CreateXmlReaderInputWithEncodingName@24 +CreateXmlWriter@12 +CreateXmlWriterOutputWithEncodingCodePage@16 +CreateXmlWriterOutputWithEncodingName@16 diff --git a/natvis/cppwinrt.natvis b/natvis/cppwinrt.natvis index 0b96d027d..7fad2e26c 100644 --- a/natvis/cppwinrt.natvis +++ b/natvis/cppwinrt.natvis @@ -14,12 +14,29 @@ null + + + + null + null + + #{A,nvoXb}{R,nvoXb}{G,nvoXb}{B,nvoXb} + + + {{size = {m_size}, {m_data,[m_size]}}} + + + m_size + m_data + + + {m_ptr} @@ -33,6 +50,9 @@ {value,hr} + + {m_code} + {m_handle.m_value,sh} m_handle.m_value,sh diff --git a/natvis/cppwinrt_visualizer.cpp b/natvis/cppwinrt_visualizer.cpp index 2fa5cb4be..4b0a0c370 100644 --- a/natvis/cppwinrt_visualizer.cpp +++ b/natvis/cppwinrt_visualizer.cpp @@ -11,8 +11,39 @@ using namespace std::filesystem; using namespace winrt; using namespace winmd::reader; -std::vector db_files; -std::unique_ptr db; +namespace +{ + std::vector db_files; + std::unique_ptr db_cache; + coded_index guid_TypeRef{}; +} + +coded_index FindGuidType() +{ + if (!guid_TypeRef) + { + // There is no definitive TypeDef for System.Guid. But there are a variety of TypeRefs scattered about + // This one should be relatively quick to find + auto pv = db_cache->find("Windows.Foundation", "IPropertyValue"); + for (auto&& method : pv.MethodList()) + { + if (method.Name() == "GetGuid") + { + auto const& sig = method.Signature(); + auto const& type = sig.ReturnType().Type().Type(); + XLANG_ASSERT(std::holds_alternative>(type)); + if (std::holds_alternative>(type)) + { + guid_TypeRef = std::get>(type); + XLANG_ASSERT(guid_TypeRef.type() == TypeDefOrRef::TypeRef); + XLANG_ASSERT(guid_TypeRef.TypeRef().TypeNamespace() == "System"); + XLANG_ASSERT(guid_TypeRef.TypeRef().TypeName() == "Guid"); + } + } + } + } + return guid_TypeRef; +} void MetadataDiagnostic(DkmProcess* process, std::wstring const& status, std::filesystem::path const& path) { @@ -93,14 +124,21 @@ void LoadMetadata(DkmProcess* process, WCHAR const* processPath, std::string_vie { auto winmd_path = path{ processPath }; auto probe_file = std::string{ typeName }; - do + while (true) { winmd_path.replace_filename(probe_file + ".winmd"); MetadataDiagnostic(process, L"Looking for ", winmd_path); if (FindMetadata(process, winmd_path)) { MetadataDiagnostic(process, L"Loaded ", winmd_path); - db_files.push_back(winmd_path.string()); + + auto const path_string = winmd_path.string(); + + if (std::find(db_files.begin(), db_files.end(), path_string) == db_files.end()) + { + db_cache->add_database(path_string, [](TypeDef const& type) { return type.Flags().WindowsRuntime(); }); + db_files.push_back(path_string); + } } auto pos = probe_file.rfind('.'); if (pos == std::string::npos) @@ -108,18 +146,18 @@ void LoadMetadata(DkmProcess* process, WCHAR const* processPath, std::string_vie break; } probe_file = probe_file.substr(0, pos); - } while (true); - db.reset(new cache(db_files)); + } } -TypeDef FindType(DkmProcess* process, std::string_view const& typeName) +TypeDef FindSimpleType(DkmProcess* process, std::string_view const& typeName) { - auto type = db->find(typeName); + XLANG_ASSERT(typeName.find('<') == std::string_view::npos); + auto type = db_cache->find(typeName); if (!type) { auto processPath = process->Path()->Value(); LoadMetadata(process, processPath, typeName); - type = db->find(typeName); + type = db_cache->find(typeName); if (!type) { NatvisDiagnostic(process, @@ -129,6 +167,104 @@ TypeDef FindType(DkmProcess* process, std::string_view const& typeName) return type; } +TypeDef FindSimpleType(DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName) +{ + XLANG_ASSERT(typeName.find('<') == std::string_view::npos); + auto type = db_cache->find(typeNamespace, typeName); + if (!type) + { + std::string fullName(typeNamespace); + fullName.append("."); + fullName.append(typeName); + FindSimpleType(process, fullName); + } + return type; +} + +std::vector ParseTypeName(std::string_view name) +{ + DWORD count; + HSTRING* parts; + auto wide_name = winrt::to_hstring(name); + winrt::check_hresult(::RoParseTypeName(static_cast(get_abi(wide_name)), &count, &parts)); + + winrt::com_array wide_parts{ parts, count, winrt::take_ownership_from_abi }; + std::vector result; + for (auto&& part : wide_parts) + { + result.push_back(winrt::to_string(part)); + } + return result; +} + +template sent> +TypeSig ResolveGenericTypePart(DkmProcess* process, iter& it, sent const& end) +{ + constexpr std::pair elementNames[] = { + {"Boolean", ElementType::Boolean}, + {"Int8", ElementType::I1}, + {"Int16", ElementType::I2}, + {"Int32", ElementType::I4}, + {"Int64", ElementType::I8}, + {"UInt8", ElementType::U1}, + {"UInt16", ElementType::U2}, + {"UInt32", ElementType::U4}, + {"UInt64", ElementType::U8}, + {"Single", ElementType::R4}, + {"Double", ElementType::R8}, + {"String", ElementType::String}, + {"Char16", ElementType::Char}, + {"Object", ElementType::Object} + }; + std::string_view partName = *it; + auto basic_type_pos = std::find_if(std::begin(elementNames), std::end(elementNames), [&partName](auto&& elem) { return elem.first == partName; }); + if (basic_type_pos != std::end(elementNames)) + { + return TypeSig{ basic_type_pos->second }; + } + + if (partName == "Guid") + { + return TypeSig{ FindGuidType() }; + } + + TypeDef type = FindSimpleType(process, partName); + auto tickPos = partName.rfind('`'); + if (tickPos == partName.npos) + { + return TypeSig{ type.coded_index() }; + } + + int paramCount = 0; + std::from_chars(partName.data() + tickPos + 1, partName.data() + partName.size(), paramCount); + std::vector genericArgs; + for (int i = 0; i < paramCount; ++i) + { + genericArgs.push_back(ResolveGenericTypePart(process, ++it, end)); + } + return TypeSig{ GenericTypeInstSig{ type.coded_index(), std::move(genericArgs) } }; +} + +TypeSig ResolveGenericType(DkmProcess* process, std::string_view genericName) +{ + auto parts = ParseTypeName(genericName); + auto begin = parts.begin(); + return ResolveGenericTypePart(process, begin, parts.end()); +} + +TypeSig FindType(DkmProcess* process, std::string_view const& typeName) +{ + auto paramIndex = typeName.find('<'); + if (paramIndex == std::string_view::npos) + { + return TypeSig{ FindSimpleType(process, typeName).coded_index() }; + } + else + { + return ResolveGenericType(process, typeName); + } +} + cppwinrt_visualizer::cppwinrt_visualizer() { try @@ -146,7 +282,7 @@ cppwinrt_visualizer::cppwinrt_visualizer() db_files.push_back(file.path().string()); } } - db.reset(new cache(db_files)); + db_cache.reset(new cache(db_files, [](TypeDef const& type) { return type.Flags().WindowsRuntime(); })); } catch (...) { @@ -168,13 +304,14 @@ cppwinrt_visualizer::cppwinrt_visualizer() cppwinrt_visualizer::~cppwinrt_visualizer() { ClearTypeResolver(); + guid_TypeRef = {}; db_files.clear(); - db.reset(); + db_cache.reset(); } HRESULT cppwinrt_visualizer::EvaluateVisualizedExpression( _In_ DkmVisualizedExpression* pVisualizedExpression, - _Deref_out_ DkmEvaluationResult** ppResultObject + _COM_Outptr_result_maybenull_ DkmEvaluationResult** ppResultObject ) { try @@ -188,31 +325,37 @@ HRESULT cppwinrt_visualizer::EvaluateVisualizedExpression( IF_FAIL_RET(pTypeSymbol->get_name(&bstrTypeName)); // Visualize top-level C++/WinRT objects containing ABI pointers - bool isAbiObject; + ObjectType objectType; if (wcscmp(bstrTypeName, L"winrt::Windows::Foundation::IInspectable") == 0) { - isAbiObject = false; + objectType = ObjectType::Projection; } // Visualize nested object properties via raw ABI pointers else if ((wcscmp(bstrTypeName, L"winrt::impl::IInspectable") == 0) || (wcscmp(bstrTypeName, L"winrt::impl::inspectable_abi") == 0)) { - isAbiObject = true; + objectType = ObjectType::Abi; + } + // Visualize C++/WinRT object implementations + else if (wcsncmp(bstrTypeName, L"winrt::impl::producer<", wcslen(L"winrt::impl::producer<")) == 0) + { + objectType = ObjectType::Abi; } // Visualize all raw IInspectable pointers else if (wcscmp(bstrTypeName, L"IInspectable") == 0) { - isAbiObject = true; + objectType = ObjectType::Abi; } else { // unrecognized type NatvisDiagnostic(pVisualizedExpression, std::wstring(L"Unrecognized type: ") + (LPWSTR)bstrTypeName, NatvisDiagnosticLevel::Error); + *ppResultObject = nullptr; return S_OK; } - IF_FAIL_RET(object_visualizer::CreateEvaluationResult(pVisualizedExpression, isAbiObject, ppResultObject)); + IF_FAIL_RET(object_visualizer::CreateEvaluationResult(pVisualizedExpression, objectType, ppResultObject)); return S_OK; } diff --git a/natvis/cppwinrt_visualizer.h b/natvis/cppwinrt_visualizer.h index 5f12ec39a..924dc3cbc 100644 --- a/natvis/cppwinrt_visualizer.h +++ b/natvis/cppwinrt_visualizer.h @@ -8,7 +8,7 @@ struct cppwinrt_visualizer : winrt::implements - + + + Debug + ARM64 + Debug Win32 + + Release + ARM64 + Release Win32 @@ -27,14 +35,6 @@ cppwinrtvisualizer $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)packages\)) - - v142 - 10.0 - - - v141 - 10.0.17763.0 - DynamicLibrary @@ -49,11 +49,20 @@ DynamicLibrary true + + DynamicLibrary + true + DynamicLibrary false true + + DynamicLibrary + false + true + $(VSInstallDir)DIA SDK\include @@ -71,40 +80,34 @@ + + + + + + - + true - x86\$(Configuration)\ - x86\$(Configuration)\ - - true - x64\$(Configuration)\ - x64\$(Configuration)\ - - + false - x86\$(Configuration)\ - x86\$(Configuration)\ - - false - x64\$(Configuration)\ - x64\$(Configuration)\ + + $(CppWinRTPlatform)\$(Configuration)\$(Deployment)\ Use Level4 + true Disabled - false - WIN32;_DEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + VSDEBUGENG_USE_CPP11_SCOPED_ENUMS;WIN32;_DEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) $(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories) - stdcpp17 - /await + stdcpp20 pch.h @@ -125,13 +128,36 @@ Use Level4 + true + Disabled + VSDEBUGENG_USE_CPP11_SCOPED_ENUMS;_DEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + $(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories) + stdcpp20 + pch.h + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + $(IntDir);%(AdditionalIncludeDirectories) + + + Windows + DebugFull + advapi32.lib;shell32.lib;windowsapp.lib;$(VsDebugEng_Lib);%(AdditionalDependencies) + .\cppwinrtvisualizer.def + vsdebugeng.dll + + + + + Use + Level4 + true Disabled - false - _DEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + VSDEBUGENG_USE_CPP11_SCOPED_ENUMS;_DEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) $(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories) - stdcpp17 + stdcpp20 pch.h - /await _DEBUG;%(PreprocessorDefinitions) @@ -150,15 +176,15 @@ Use Level4 + true MaxSpeed true true - false - WIN32;NDEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + VSDEBUGENG_USE_CPP11_SCOPED_ENUMS;WIN32;NDEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) $(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories) - stdcpp17 + stdcpp20 pch.h - /await + Guard _DEBUG;%(PreprocessorDefinitions) @@ -173,21 +199,22 @@ advapi32.lib;shell32.lib;windowsapp.lib;$(VsDebugEng_Lib);%(AdditionalDependencies) .\cppwinrtvisualizer.def vsdebugeng.dll + /DEBUGTYPE:CV,FIXUP %(AdditionalOptions) Use Level4 + true MaxSpeed true true - false - NDEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + VSDEBUGENG_USE_CPP11_SCOPED_ENUMS;NDEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) $(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories) - stdcpp17 + stdcpp20 pch.h - /await + Guard _DEBUG;%(PreprocessorDefinitions) @@ -202,13 +229,49 @@ advapi32.lib;shell32.lib;windowsapp.lib;$(VsDebugEng_Lib);%(AdditionalDependencies) .\cppwinrtvisualizer.def vsdebugeng.dll + /DEBUGTYPE:CV,FIXUP %(AdditionalOptions) + + + Use + Level4 + true + MaxSpeed + true + true + VSDEBUGENG_USE_CPP11_SCOPED_ENUMS;NDEBUG;VISUALIZER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions) + $(IntDir);..\cppwinrt;..\strings;$(DIASDKInc);%(AdditionalIncludeDirectories) + stdcpp20 + pch.h + Guard + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + $(IntDir);%(AdditionalIncludeDirectories) + + + Windows + true + true + DebugFull + advapi32.lib;shell32.lib;windowsapp.lib;$(VsDebugEng_Lib);%(AdditionalDependencies) + .\cppwinrtvisualizer.def + vsdebugeng.dll + /DEBUGTYPE:CV,FIXUP %(AdditionalOptions) + + + + + %(AdditionalOptions) /DCOMPONENT_DEPLOYMENT + + - - + + @@ -220,8 +283,10 @@ Create Create + Create Create Create + Create @@ -238,9 +303,7 @@ Designer - - - + Designer @@ -258,6 +321,6 @@ - + \ No newline at end of file diff --git a/natvis/object_visualizer.cpp b/natvis/object_visualizer.cpp index 31cd5d0f3..d953a1c03 100644 --- a/natvis/object_visualizer.cpp +++ b/natvis/object_visualizer.cpp @@ -99,18 +99,18 @@ static HRESULT EvaluatePropertyExpression( _In_ PropertyData const& prop, _In_ DkmVisualizedExpression* pExpression, _In_ DkmPointerValueHome* pObject, - bool isAbiObject, + ObjectType objectType, _Out_ com_ptr& pEvaluationResult ) { wchar_t abiAddress[40]; auto process = pExpression->RuntimeInstance()->Process(); bool is64Bit = ((process->SystemInformation()->Flags() & DefaultPort::DkmSystemInformationFlags::Is64Bit) != 0); - swprintf_s(abiAddress, is64Bit ? L"%s0x%I64x" : L"%s0x%08x", isAbiObject ? L"(::IUnknown*)" : L"*(::IUnknown**)", pObject->Address()); + swprintf_s(abiAddress, is64Bit ? L"%s0x%I64x" : L"%s0x%08x", objectType == ObjectType::Abi ? L"(::IUnknown*)" : L"*(::IUnknown**)", pObject->Address()); wchar_t wszEvalText[500]; std::wstring propCast; PCWSTR propField; - if (prop.category < PropertyCategory::Value) + if (IsBuiltIn(prop.category)) { propField = g_categoryData[(int)prop.category].propField; } @@ -174,12 +174,12 @@ static HRESULT EvaluatePropertyString( _In_ PropertyData const& prop, _In_ DkmVisualizedExpression* pExpression, _In_ DkmPointerValueHome* pObject, - bool isAbiObject, + ObjectType objectType, _Out_ com_ptr& pValue ) { com_ptr pEvaluationResult; - IF_FAIL_RET(EvaluatePropertyExpression(prop, pExpression, pObject, isAbiObject, pEvaluationResult)); + IF_FAIL_RET(EvaluatePropertyExpression(prop, pExpression, pObject, objectType, pEvaluationResult)); if (pEvaluationResult->TagValue() != DkmEvaluationResult::Tag::SuccessResult) { return E_FAIL; @@ -195,11 +195,11 @@ static HRESULT EvaluatePropertyString( static std::string GetRuntimeClass( _In_ DkmVisualizedExpression* pExpression, _In_ DkmPointerValueHome* pObject, - bool isAbiObject + ObjectType objectType ) { com_ptr pValue; - EvaluatePropertyString({ IID_IInspectable, -2, PropertyCategory::String }, pExpression, pObject, isAbiObject, pValue); + EvaluatePropertyString({ IID_IInspectable, -2, PropertyCategory::String }, pExpression, pObject, objectType, pValue); if (!pValue || pValue->Length() == 0) { return ""; @@ -210,11 +210,11 @@ static std::string GetRuntimeClass( static HRESULT ObjectToString( _In_ DkmVisualizedExpression* pExpression, _In_ DkmPointerValueHome* pObject, - bool isAbiObject, + ObjectType objectType, _Out_ com_ptr& pValue ) { - if (SUCCEEDED(EvaluatePropertyString({ IID_IStringable, 0, PropertyCategory::String }, pExpression, pObject, isAbiObject, pValue))) + if (SUCCEEDED(EvaluatePropertyString({ IID_IStringable, 0, PropertyCategory::String }, pExpression, pObject, objectType, pValue))) { if (pValue && pValue->Length() > 0) { @@ -224,7 +224,7 @@ static HRESULT ObjectToString( // WINRT_abi_val returned 0, which may be success or failure (due to VirtualQuery validation) // Call back for the runtime class name to determine which it was - if (!GetRuntimeClass(pExpression, pObject, isAbiObject).empty()) + if (!GetRuntimeClass(pExpression, pObject, objectType).empty()) { return DkmString::Create(L"", pValue.put()); } @@ -238,7 +238,7 @@ static HRESULT ObjectToString( static HRESULT CreateChildVisualizedExpression( _In_ PropertyData const& prop, _In_ DkmVisualizedExpression* pParent, - bool isAbiObject, + ObjectType objectType, _Deref_out_ DkmChildVisualizedExpression** ppResult ) { @@ -247,7 +247,7 @@ static HRESULT CreateChildVisualizedExpression( com_ptr pEvaluationResult; auto valueHome = make_com_ptr(pParent->ValueHome()); com_ptr pParentPointer = valueHome.as(); - IF_FAIL_RET(EvaluatePropertyExpression(prop, pParent, pParentPointer.get(), isAbiObject, pEvaluationResult)); + IF_FAIL_RET(EvaluatePropertyExpression(prop, pParent, pParentPointer.get(), objectType, pEvaluationResult)); if (pEvaluationResult->TagValue() != DkmEvaluationResult::Tag::SuccessResult) { return E_FAIL; @@ -264,7 +264,7 @@ static HRESULT CreateChildVisualizedExpression( { isNonNullObject = true; IF_FAIL_RET(DkmPointerValueHome::Create(childObjectAddress, pChildPointer.put())); - IF_FAIL_RET(ObjectToString(pParent, pChildPointer.get(), true, pValue)); + IF_FAIL_RET(ObjectToString(pParent, pChildPointer.get(), ObjectType::Abi, pValue)); } } if(!isNonNullObject) @@ -278,7 +278,7 @@ static HRESULT CreateChildVisualizedExpression( IF_FAIL_RET(DkmString::Create(prop.displayName.c_str(), pDisplayName.put())); PCWSTR displayType; - if (prop.category < PropertyCategory::Value) + if (IsBuiltIn(prop.category)) { displayType = g_categoryData[(int)prop.category].displayType; } @@ -326,7 +326,7 @@ static HRESULT CreateChildVisualizedExpression( if (isNonNullObject) { - com_ptr pObjectVisualizer = make_self(pChildVisualizedExpression.get(), true); + com_ptr pObjectVisualizer = make_self(pChildVisualizedExpression.get(), ObjectType::Abi); IF_FAIL_RET(pChildVisualizedExpression->SetDataItem(DkmDataCreationDisposition::CreateNew, pObjectVisualizer.get())); } else @@ -340,18 +340,295 @@ static HRESULT CreateChildVisualizedExpression( return S_OK; } -struct property_type +std::optional GetPropertyCategory( + Microsoft::VisualStudio::Debugger::DkmProcess* process, + TypeSig const& owningType, + TypeSig const& propertyType +) +{ + std::optional propCategory; + if (auto pElementType = std::get_if(&propertyType.Type())) + { + if ((ElementType::Boolean <= *pElementType) && (*pElementType <= ElementType::String)) + { + propCategory = (PropertyCategory)(static_cast::type>(*pElementType) - + static_cast::type>(ElementType::Boolean)); + } + else if (*pElementType == ElementType::Object) + { + // result = PropertyCategory::Class; + } + } + else if (auto pIndex = std::get_if>(&propertyType.Type())) + { + auto type = ResolveType(process, *pIndex); + if (type) + { + if (get_category(type) == category::class_type || get_category(type) == category::interface_type) + { + propCategory = PropertyCategory::Class; + } + else + { + propCategory = PropertyCategory::Value; + } + } + else if (pIndex->type() == TypeDefOrRef::TypeRef) + { + auto typeRef = pIndex->TypeRef(); + if (typeRef.TypeNamespace() == "System" && typeRef.TypeName() == "Guid") + { + propCategory = PropertyCategory::Guid; + } + } + } + else if (auto pGenericInst = std::get_if(&propertyType.Type())) + { + XLANG_ASSERT(get_category(ResolveType(process, pGenericInst->GenericType())) == category::interface_type); + propCategory = PropertyCategory::Class; + } + else if (auto pGenericIndex = std::get_if(&propertyType.Type())) + { + if (auto pOwner = std::get_if(&owningType.Type())) + { + auto const& index = pGenericIndex->index; + auto const& genericArgs = pOwner->GenericArgs(); + propCategory = GetPropertyCategory(process, owningType, genericArgs.first[index]); + } + else + { + NatvisDiagnostic(process, L"Can't resolve GenericTypeIndex property on non-generic Type", NatvisDiagnosticLevel::Warning); + } + } + else + { + NatvisDiagnostic(process, L"Unsupported TypeSig encountered", NatvisDiagnosticLevel::Warning); + } + return propCategory; +} + +struct writer { - MethodDef get; - MethodDef set; + std::vector generic_params; + + std::string result; + + void write(char c) + { + result.push_back(c); + } + + void write(std::string_view const& str) + { + for (auto c : str) + { + if (c == '.') + { + write(':'); + write(':'); + } + else if (c != '`') + { + write(c); + } + else + { + return; + } + } + } + + void write(ElementType type) + { + switch (type) + { + case ElementType::Boolean: + write("bool"); + break; + case ElementType::Char: + write("wchar_t"); + break; + case ElementType::I1: + write("int8_t"); + break; + case ElementType::U1: + write("uint8_t"); + break; + case ElementType::I2: + write("int16_t"); + break; + case ElementType::U2: + write("uint16_t"); + break; + case ElementType::I4: + write("int32_t"); + break; + case ElementType::U4: + write("uint32_t"); + break; + case ElementType::I8: + write("int64_t"); + break; + case ElementType::U8: + write("uint64_t"); + break; + case ElementType::R4: + write("float"); + break; + case ElementType::R8: + write("double"); + break; + case ElementType::String: + write("winrt::hstring"); + break; + case ElementType::Object: + write("winrt::Windows::Foundation::IInspectable"); + break; + default: + XLANG_ASSERT(false); + break; + }; + } + + void write_namespace_and_type(std::string_view ns, std::string_view name) + { + if (ns == "System") + { + if (name == "Guid") + { + ns = ""; + name = "guid"; + } + } + else if (ns == "Windows.Foundation") + { + if (name == "EventRegistrationToken") + { + ns = ""; + name = "event_token"; + } + else if (name == "HResult") + { + ns = ""; + name = "hresult"; + } + } + else if (ns == "Windows.Foundation.Numerics") + { + if (name == "Matrix3x2") { name = "float3x2"; } + else if (name == "Matrix4x4") { name = "float4x4"; } + else if (name == "Plane") { name = "plane"; } + else if (name == "Quaternion") { name = "quarternion"; } + else if (name == "Vector2") { name = "float2"; } + else if (name == "Vector3") { name = "float3"; } + else if (name == "Vector4") { name = "float4"; } + } + + write("winrt::"); + if (!ns.empty()) + { + write(ns); + write("::"); + } + write(name); + } + + void write(TypeRef const& type) + { + write_namespace_and_type(type.TypeNamespace(), type.TypeName()); + } + + void write(TypeDef const& type) + { + write_namespace_and_type(type.TypeNamespace(), type.TypeName()); + } + + void write(TypeSpec const& type) + { + write(type.Signature().GenericTypeInst()); + } + + void write(coded_index type) + { + switch (type.type()) + { + case TypeDefOrRef::TypeDef: + write(type.TypeDef()); + break; + case TypeDefOrRef::TypeRef: + write(type.TypeRef()); + break; + case TypeDefOrRef::TypeSpec: + write(type.TypeSpec()); + break; + } + } + + void write(GenericTypeInstSig const& type) + { + write(type.GenericType()); + bool first = true; + write("<"); + for (auto&& elem : type.GenericArgs()) + { + if (first) + { + first = false; + } + else + { + write(", "); + } + write(elem); + } + write(">"); + } + + void write(GenericTypeIndex const& var) + { + write(generic_params[var.index]); + } + + void write(GenericMethodTypeIndex const&) + { + // Nothing + } + + void write(TypeSig const& type) + { + std::visit([this](auto&& arg) + { + write(arg); + }, type.Type()); + } }; +std::wstring string_to_wstring(std::string_view const& str) +{ + int const size = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.size()), nullptr, 0); + if (size == 0) + { + return {}; + } + + std::wstring result(size, L'?'); + [[maybe_unused]] auto size_result = MultiByteToWideChar(CP_UTF8, 0, str.data(), static_cast(str.size()), result.data(), size); + XLANG_ASSERT(size == size_result); + return result; +} + void GetInterfaceData( - coded_index index, + Microsoft::VisualStudio::Debugger::DkmProcess* process, + TypeSig const& typeSig, _Inout_ std::vector& propertyData, _Out_ bool& isStringable ){ - auto [type, propIid] = ResolveTypeInterface(index); + auto [type, propIid] = ResolveTypeInterface(process, typeSig); + + if (!type) + { + return; + } if (propIid == IID_IStringable) { @@ -370,94 +647,35 @@ void GetInterfaceData( continue; } - PropertyCategory propCategory; - std::wstring propAbiType; - std::wstring propDisplayType; - - auto retType = method.Signature().ReturnType(); - std::visit(overloaded{ - [&](ElementType type) + std::optional propCategory = GetPropertyCategory(process, typeSig, method.Signature().ReturnType().Type()); + if (propCategory) + { + std::wstring propAbiType; + std::wstring propDisplayType; + if (!IsBuiltIn(*propCategory)) { - if ((type < ElementType::Boolean) || (type > ElementType::String)) + writer writer; + if (auto pGenericTypeInst = std::get_if(&typeSig.Type())) { - return; + auto const& genericArgs = pGenericTypeInst->GenericArgs(); + writer.generic_params.assign(genericArgs.first, genericArgs.second); } - propCategory = (PropertyCategory)(static_cast::type>(type) - - static_cast::type>(ElementType::Boolean)); - }, - [&](coded_index const& index) - { - auto type = ResolveType(index); - auto typeName = type.TypeName(); - if (typeName == "GUID"sv) + writer.write(method.Signature().ReturnType().Type()); + propDisplayType = string_to_wstring(writer.result); + + if (*propCategory == PropertyCategory::Class) { - propCategory = PropertyCategory::Guid; + propAbiType = L"winrt::impl::inspectable_abi*"; } else { - auto ns = std::string(type.TypeNamespace()); - auto name = std::string(type.TypeName()); - - // Map numeric type names - if (ns == "Windows.Foundation.Numerics") - { - if (name == "Matrix3x2") { name = "float3x2"; } - else if (name == "Matrix4x4") { name = "float4x4"; } - else if (name == "Plane") { name = "plane"; } - else if (name == "Quaternion") { name = "quaternion"; } - else if (name == "Vector2") { name = "float2"; } - else if (name == "Vector3") { name = "float3"; } - else if (name == "Vector4") { name = "float4"; } - } - - // Types come back from winmd files with '.', need to be '::' - // Ex. Windows.Foundation.Uri needs to be Windows::Foundation::Uri - auto fullTypeName = ns + "::" + name; - wchar_t cppTypename[500]; - size_t i, j; - for (i = 0, j = 0; i < (fullTypeName.length() + 1); i++, j++) - { - if (fullTypeName[i] == L'.') - { - cppTypename[j++] = L':'; - cppTypename[j] = L':'; - } - else - { - cppTypename[j] = fullTypeName[i]; - } - } - - propDisplayType = std::wstring(L"winrt::") + cppTypename; - if(get_category(type) == category::class_type) - { - propCategory = PropertyCategory::Class; - propAbiType = L"winrt::impl::inspectable_abi*"; - } - else - { - propCategory = PropertyCategory::Value; - propAbiType = propDisplayType; - } + propAbiType = propDisplayType; } - }, - [&](GenericTypeIndex /*var*/) - { - throw_invalid("Generics are not yet supported"); - }, - [&](GenericMethodTypeIndex /*var*/) - { - throw_invalid("Generic methods not supported."); - }, - [&](GenericTypeInstSig const& /*type*/) - { - throw_invalid("Generics are not yet supported"); } - }, retType.Type().Type()); - auto propName = method.Name().substr(4); - std::wstring propDisplayName(propName.cbegin(), propName.cend()); - propertyData.push_back({ propIid, propIndex, propCategory, propAbiType, propDisplayType, propDisplayName }); + auto propName = method.Name().substr(4); + propertyData.emplace_back(propIid, propIndex, *propCategory, std::move(propAbiType), std::move(propDisplayType), string_to_wstring(propName)); + } } } @@ -465,7 +683,7 @@ void object_visualizer::GetPropertyData() { auto valueHome = make_com_ptr(m_pVisualizedExpression->ValueHome()); com_ptr pObject = valueHome.as(); - auto rc = GetRuntimeClass(m_pVisualizedExpression.get(), pObject.get(), m_isAbiObject); + auto rc = GetRuntimeClass(m_pVisualizedExpression.get(), pObject.get(), m_objectType); if (rc.empty()) { return; @@ -475,10 +693,55 @@ void object_visualizer::GetPropertyData() GetTypeProperties(process, std::string_view{ rc.data() + 2, rc.length() - 3 }); } +GenericTypeInstSig ReplaceGenericIndices(GenericTypeInstSig const& sig, std::vector const& genericArgs) +{ + std::vector replacementArgs; + for (auto&& arg : sig.GenericArgs()) + { + if (auto pGenericSig = std::get_if(&arg.Type())) + { + replacementArgs.emplace_back(ReplaceGenericIndices(*pGenericSig, genericArgs)); + } + else if (auto pGenericIndex = std::get_if(&arg.Type())) + { + replacementArgs.push_back(genericArgs[pGenericIndex->index]); + } + else + { + replacementArgs.push_back(arg); + } + } + return GenericTypeInstSig{ sig.GenericType(), std::move(replacementArgs) }; +} + +TypeSig ExpandInterfaceImplForType(coded_index impl, TypeSig const& type) +{ + if (auto pGenericInst = std::get_if(&type.Type())) + { + if (impl.type() == TypeDefOrRef::TypeSpec) + { + auto const& genericArgs = pGenericInst->GenericArgs(); + auto newSig = ReplaceGenericIndices(impl.TypeSpec().Signature().GenericTypeInst(), std::vector{ genericArgs.first, genericArgs.second }); + return TypeSig{ newSig }; + } + } + return TypeSig{ impl }; +} + void object_visualizer::GetTypeProperties(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& type_name) { // TODO: add support for direct generic interface implementations (e.g., key_value_pair) - auto type = FindType(process, type_name); + auto typeSig = FindType(process, type_name); + TypeDef type{}; + if (auto const* index = std::get_if>(&typeSig.Type())) + { + type = ResolveType(process, *index); + } + else if (auto const* genericInst = std::get_if(&typeSig.Type())) + { + type = ResolveType(process, genericInst->GenericType()); + } + if (!type) { return; @@ -498,7 +761,7 @@ void object_visualizer::GetTypeProperties(Microsoft::VisualStudio::Debugger::Dkm auto impls = type.InterfaceImpl(); for (auto&& impl : impls) { - GetInterfaceData(impl.Interface(), m_propertyData, m_isStringable); + GetInterfaceData(process, ExpandInterfaceImplForType(impl.Interface(), typeSig), m_propertyData, m_isStringable); } } else if (get_category(type) == category::interface_type) @@ -506,15 +769,15 @@ void object_visualizer::GetTypeProperties(Microsoft::VisualStudio::Debugger::Dkm auto impls = type.InterfaceImpl(); for (auto&& impl : impls) { - GetInterfaceData(impl.Interface(), m_propertyData, m_isStringable); + GetInterfaceData(process, ExpandInterfaceImplForType(impl.Interface(), typeSig), m_propertyData, m_isStringable); } - GetInterfaceData(type.coded_index(), m_propertyData, m_isStringable); + GetInterfaceData(process, typeSig, m_propertyData, m_isStringable); } } -HRESULT object_visualizer::CreateEvaluationResult(_In_ DkmVisualizedExpression* pVisualizedExpression, _In_ bool isAbiObject, _Deref_out_ DkmEvaluationResult** ppResultObject) +HRESULT object_visualizer::CreateEvaluationResult(_In_ DkmVisualizedExpression* pVisualizedExpression, _In_ ObjectType objectType, _Deref_out_ DkmEvaluationResult** ppResultObject) { - com_ptr pObjectVisualizer = make_self(pVisualizedExpression, isAbiObject); + com_ptr pObjectVisualizer = make_self(pVisualizedExpression, objectType); IF_FAIL_RET(pVisualizedExpression->SetDataItem(DkmDataCreationDisposition::CreateNew, pObjectVisualizer.get())); @@ -525,18 +788,50 @@ HRESULT object_visualizer::CreateEvaluationResult(_In_ DkmVisualizedExpression* return S_OK; } +#ifdef COMPONENT_DEPLOYMENT +static std::set g_refresh_cache; +bool requires_refresh(UINT64 address, DkmEvaluationFlags_t evalFlags) +{ + auto refreshed = g_refresh_cache.find(address) != g_refresh_cache.end(); + return !refreshed && ((evalFlags & DkmEvaluationFlags::EnableExtendedSideEffects) != DkmEvaluationFlags::EnableExtendedSideEffects); +} +void cache_refresh(UINT64 address) +{ + g_refresh_cache.insert(address); +} +#else +bool requires_refresh(UINT64, DkmEvaluationFlags_t) +{ + return false; +} +void cache_refresh(UINT64) +{ +} +#endif + HRESULT object_visualizer::CreateEvaluationResult(_Deref_out_ DkmEvaluationResult** ppResultObject) { com_ptr pRootVisualizedExpression = m_pVisualizedExpression.as(); auto valueHome = make_com_ptr(m_pVisualizedExpression->ValueHome()); com_ptr pPointerValueHome = valueHome.as(); - + auto address = pPointerValueHome->Address(); + com_ptr pValue; - IF_FAIL_RET(ObjectToString(m_pVisualizedExpression.get(), pPointerValueHome.get(), m_isAbiObject, pValue)); + DkmEvaluationResultFlags_t evalResultFlags = DkmEvaluationResultFlags::ReadOnly | DkmEvaluationResultFlags::Expandable; + if (requires_refresh(address, m_pVisualizedExpression->InspectionContext()->EvaluationFlags())) + { + IF_FAIL_RET(DkmString::Create(L"", pValue.put())); + evalResultFlags |= DkmEvaluationResultFlags::EnableExtendedSideEffectsUponRefresh | DkmEvaluationResultFlags::CanEvaluateNow; + } + else + { + cache_refresh(address); + IF_FAIL_RET(ObjectToString(m_pVisualizedExpression.get(), pPointerValueHome.get(), m_objectType, pValue)); + } com_ptr pAddress; - IF_FAIL_RET(DkmDataAddress::Create(m_pVisualizedExpression->StackFrame()->RuntimeInstance(), pPointerValueHome->Address(), nullptr, pAddress.put())); + IF_FAIL_RET(DkmDataAddress::Create(m_pVisualizedExpression->StackFrame()->RuntimeInstance(), address, nullptr, pAddress.put())); com_ptr pSuccessEvaluationResult; IF_FAIL_RET(DkmSuccessEvaluationResult::Create( @@ -544,7 +839,7 @@ HRESULT object_visualizer::CreateEvaluationResult(_Deref_out_ DkmEvaluationResul m_pVisualizedExpression->StackFrame(), pRootVisualizedExpression->Name(), pRootVisualizedExpression->FullName(), - DkmEvaluationResultFlags::Expandable | DkmEvaluationResultFlags::ReadOnly, + evalResultFlags, pValue.get(), pValue.get(), pRootVisualizedExpression->Type(), @@ -567,29 +862,31 @@ HRESULT object_visualizer::CreateEvaluationResult(_Deref_out_ DkmEvaluationResul } HRESULT object_visualizer::GetChildren( - _In_ UINT32 /*InitialRequestSize*/, + _In_ UINT32 InitialRequestSize, _In_ DkmInspectionContext* pInspectionContext, _Out_ DkmArray* pInitialChildren, - _Deref_out_ DkmEvaluationResultEnumContext** ppEnumContext -) + _Deref_out_ DkmEvaluationResultEnumContext** ppEnumContext) { // Ignore metadata errors to ensure that Raw Data is always available - try + if (m_propertyData.empty()) { - GetPropertyData(); - } - catch (std::invalid_argument const& e) - { - std::string_view message(e.what()); - NatvisDiagnostic(m_pVisualizedExpression.get(), - std::wstring(L"Exception in object_visualizer::GetPropertyData: ") + + try + { + GetPropertyData(); + } + catch (std::invalid_argument const& e) + { + std::string_view message(e.what()); + NatvisDiagnostic(m_pVisualizedExpression.get(), + std::wstring(L"Exception in object_visualizer::GetPropertyData: ") + std::wstring(message.begin(), message.end()), - NatvisDiagnosticLevel::Error, to_hresult()); - } - catch (...) - { - NatvisDiagnostic(m_pVisualizedExpression.get(), - L"Exception in object_visualizer::GetPropertyData", NatvisDiagnosticLevel::Error, to_hresult()); + NatvisDiagnosticLevel::Error, to_hresult()); + } + catch (...) + { + NatvisDiagnostic(m_pVisualizedExpression.get(), + L"Exception in object_visualizer::GetPropertyData", NatvisDiagnosticLevel::Error, to_hresult()); + } } com_ptr pEnumContext; @@ -600,32 +897,34 @@ HRESULT object_visualizer::GetChildren( this, pEnumContext.put())); - DkmAllocArray(0, pInitialChildren); + IF_FAIL_RET(GetItems(m_pVisualizedExpression.get(), pEnumContext.get(), 0, InitialRequestSize, pInitialChildren)); + *ppEnumContext = pEnumContext.detach(); return S_OK; } HRESULT object_visualizer::GetItems( - _In_ DkmVisualizedExpression* /*pVisualizedExpression*/, + _In_ DkmVisualizedExpression* pVisualizedExpression, _In_ DkmEvaluationResultEnumContext* /*pEnumContext*/, _In_ UINT32 StartIndex, _In_ UINT32 Count, - _Out_ DkmArray* pItems -) + _Out_ DkmArray* pItems) { - std::list> childItems; + CAutoDkmArray resultValues; + IF_FAIL_RET(DkmAllocArray(std::min(m_propertyData.size(), size_t(Count)), &resultValues)); - auto pParent = m_pVisualizedExpression.get(); - for( auto childIndex = StartIndex; childIndex < StartIndex + Count; ++childIndex) + auto pParent = pVisualizedExpression; + auto childCount = std::min(m_propertyData.size() - StartIndex, (size_t)Count); + for(size_t i = 0; i < childCount; ++i) { - auto& prop = m_propertyData[childIndex]; + auto& prop = m_propertyData[i + (size_t)StartIndex]; com_ptr pPropertyVisualized; - if(FAILED(CreateChildVisualizedExpression(prop, pParent, m_isAbiObject, pPropertyVisualized.put()))) + if(FAILED(CreateChildVisualizedExpression(prop, pParent, m_objectType, pPropertyVisualized.put()))) { com_ptr pErrorMessage; IF_FAIL_RET(DkmString::Create(L"", pErrorMessage.put())); - + com_ptr pDisplayName; IF_FAIL_RET(DkmString::Create(prop.displayName.c_str(), pDisplayName.put())); @@ -634,9 +933,9 @@ HRESULT object_visualizer::GetItems( pParent->InspectionContext(), pParent->StackFrame(), pDisplayName.get(), - nullptr, + nullptr, pErrorMessage.get(), - DkmEvaluationResultFlags::ExceptionThrown, + DkmEvaluationResultFlags::ExceptionThrown, DkmDataItem::Null(), pVisualizedResult.put() )); @@ -654,22 +953,9 @@ HRESULT object_visualizer::GetItems( pPropertyVisualized.put() )); } - childItems.push_back(pPropertyVisualized); - } - - CAutoDkmArray resultValues; - IF_FAIL_RET(DkmAllocArray(childItems.size(), &resultValues)); - - UINT32 j = 0; - auto pos = childItems.begin(); - while (pos != childItems.end()) - { - com_ptr pCurr = *pos; - resultValues.Members[j++] = pCurr.detach(); - pos++; + resultValues.Members[i] = pPropertyVisualized.detach(); } *pItems = resultValues.Detach(); - return S_OK; } diff --git a/natvis/object_visualizer.h b/natvis/object_visualizer.h index 2c84a4a94..6d1a0f00c 100644 --- a/natvis/object_visualizer.h +++ b/natvis/object_visualizer.h @@ -20,7 +20,18 @@ enum class PropertyCategory Class, }; -// Metatdata for resolving a runtime class property value +inline constexpr bool IsBuiltIn(PropertyCategory value) noexcept +{ + return PropertyCategory::Bool <= value && value < PropertyCategory::Value; +} + +enum class ObjectType +{ + Abi, + Projection, +}; + +// Metadata for resolving a runtime class property value struct PropertyData { std::wstring iid; @@ -36,17 +47,17 @@ struct PropertyData struct __declspec(uuid("c7da92da-3bc9-4312-8a93-46f480663980")) object_visualizer : winrt::implements { - object_visualizer(Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pVisualizedExpression, bool isAbiObject) + object_visualizer(Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pVisualizedExpression, ObjectType objectType) { m_pVisualizedExpression = make_com_ptr(pVisualizedExpression); - m_isAbiObject = isAbiObject; + m_objectType = objectType; } ~object_visualizer() { } - static HRESULT CreateEvaluationResult(_In_ Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pVisualizedExpression, _In_ bool isAbiObject, _Deref_out_ Microsoft::VisualStudio::Debugger::Evaluation::DkmEvaluationResult** ppResultObject); + static HRESULT CreateEvaluationResult(_In_ Microsoft::VisualStudio::Debugger::Evaluation::DkmVisualizedExpression* pVisualizedExpression, _In_ ObjectType objectType, _Deref_out_ Microsoft::VisualStudio::Debugger::Evaluation::DkmEvaluationResult** ppResultObject); HRESULT CreateEvaluationResult(_Deref_out_ Microsoft::VisualStudio::Debugger::Evaluation::DkmEvaluationResult** ppResultObject); @@ -69,7 +80,7 @@ object_visualizer : winrt::implements void GetPropertyData(); void GetTypeProperties(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& type_name); winrt::com_ptr m_pVisualizedExpression; - bool m_isAbiObject; + ObjectType m_objectType; std::vector m_propertyData; bool m_isStringable{ false }; }; diff --git a/natvis/packages.config b/natvis/packages.config index a42f7bbdf..7907cba44 100644 --- a/natvis/packages.config +++ b/natvis/packages.config @@ -2,5 +2,5 @@ - + \ No newline at end of file diff --git a/natvis/pch.h b/natvis/pch.h index 1a33d1b2b..3de6807b3 100644 --- a/natvis/pch.h +++ b/natvis/pch.h @@ -4,11 +4,15 @@ #define NOMINMAX #include +#pragma warning(push) +#pragma warning(disable : 4471) #include +#pragma warning(pop) #include #include #include "base_includes.h" #include "base_macros.h" +#include "base_source_location.h" #include "base_types.h" #include "base_extern.h" #include "base_meta.h" @@ -20,11 +24,15 @@ #include "base_com_ptr.h" #include "base_string.h" #include "base_string_input.h" +#include "base_string_operators.h" #include "base_array.h" #include "base_weak_ref.h" #include "base_agile_ref.h" #include "base_error.h" #include "base_marshaler.h" +#include "base_delegate.h" +#include "base_events.h" +#include "base_activation.h" #include "base_implements.h" #include #include @@ -36,6 +44,7 @@ #include #include #include +#include #ifndef IF_FAIL_RET #define IF_FAIL_RET(expr) { HRESULT _hr = (expr); if(FAILED(_hr)) { return(_hr); } } @@ -84,22 +93,24 @@ inline bool starts_with(std::string_view const& value, std::string_view const& m return 0 == value.compare(0, match.size(), match); } -winmd::reader::TypeDef FindType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeName); +winmd::reader::TypeDef FindSimpleType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeName); +winmd::reader::TypeDef FindSimpleType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeNamespace, std::string_view const& typeName); +winmd::reader::TypeSig FindType(Microsoft::VisualStudio::Debugger::DkmProcess* process, std::string_view const& typeName); -inline winmd::reader::TypeDef ResolveType(winmd::reader::coded_index index) noexcept +inline winmd::reader::TypeDef ResolveType(Microsoft::VisualStudio::Debugger::DkmProcess* process, winmd::reader::coded_index index) noexcept { switch (index.type()) { case winmd::reader::TypeDefOrRef::TypeDef: return index.TypeDef(); case winmd::reader::TypeDefOrRef::TypeRef: - return winmd::reader::find_required(index.TypeRef()); + return FindSimpleType(process, index.TypeRef().TypeNamespace(), index.TypeRef().TypeName()); default: //case TypeDefOrRef::TypeSpec: return winmd::reader::find_required(index.TypeSpec().Signature(). GenericTypeInst().GenericType().TypeRef()); } } -std::pair ResolveTypeInterface(winmd::reader::coded_index index); +std::pair ResolveTypeInterface(Microsoft::VisualStudio::Debugger::DkmProcess* process, winmd::reader::TypeSig const& typeSig); void ClearTypeResolver(); diff --git a/natvis/type_resolver.cpp b/natvis/type_resolver.cpp index 4d0ac5897..de8af71f4 100644 --- a/natvis/type_resolver.cpp +++ b/natvis/type_resolver.cpp @@ -3,6 +3,7 @@ using namespace winrt; using namespace winmd::reader; using namespace std::literals; +using namespace Microsoft::VisualStudio::Debugger; static std::map, std::pair> _cache; @@ -97,6 +98,18 @@ struct signature_generator } } + static std::string get_signature(GenericTypeInstSig const& type) + { + std::string sig = "pinterface(" + get_guid_signature(type.GenericType()); + for (auto&& arg : type.GenericArgs()) + { + sig += ";"; + sig += get_signature(arg); + } + sig += ")"; + return sig; + } + private: static std::string get_class_signature(TypeDef const& type) { @@ -151,20 +164,8 @@ struct signature_generator case TypeDefOrRef::TypeRef: return get_guid_signature(find_required(type.TypeRef())); default: //case TypeDefOrRef::TypeSpec: - return get_guid_signature(type.TypeSpec().Signature().GenericTypeInst().GenericType()); - } - } - - static std::string get_signature(GenericTypeInstSig const& type) - { - std::string sig = "pinterface(" + get_guid_signature(type.GenericType()); - for (auto&& arg : type.GenericArgs()) - { - sig += ";"; - sig += get_signature(arg); + return get_signature(type.TypeSpec().Signature().GenericTypeInst()); } - sig += ")"; - return sig; } static std::string get_signature(TypeSig::value_type const& type) @@ -265,7 +266,7 @@ static auto calculate_sha1(std::vector const& input) return get_result(intermediate_hash); } -static guid generate_guid(coded_index const& type) +static guid generate_guid(GenericTypeInstSig const& type) { constexpr guid namespace_guid = { 0xd57af411, 0x737b, 0xc042,{ 0xab, 0xae, 0x87, 0x8b, 0x1e, 0x16, 0xad, 0xee } }; constexpr auto namespace_bytes = winrt::impl::to_array(namespace_guid); @@ -277,20 +278,38 @@ static guid generate_guid(coded_index const& type) return set_named_guid_fields(endian_swap(to_guid(calculate_sha1(buffer)))); } -std::pair ResolveTypeInterface(coded_index index) +std::pair ResolveTypeInterface(DkmProcess* process, winmd::reader::TypeSig const& typeSig) { - if (auto found = _cache.find(index); found != _cache.end()) + coded_index index; + if (auto ptrIndex = std::get_if>(&typeSig.Type())) { - return found->second; - } + index = *ptrIndex; + // TODO: Cache on the whole TypeSig, not just the generic index + if (auto found = _cache.find(index); found != _cache.end()) + { + return found->second; + } + + TypeDef type = ResolveType(process, index); + if (!type) + { + return {}; + } - TypeDef type = ResolveType(index); - auto guid = index.type() == TypeDefOrRef::TypeSpec ? - format_guid(generate_guid(index)) : format_guid(get_guid(type)); + auto guid = index.type() == TypeDefOrRef::TypeSpec ? + format_guid(generate_guid(index.TypeSpec().Signature().GenericTypeInst())) : format_guid(get_guid(type)); - auto type_guid = std::pair{ type, guid }; - _cache[index] = type_guid; - return type_guid; + auto type_guid = std::pair{ type, guid }; + _cache[index] = type_guid; + return type_guid; + } + else if (auto* ptrGeneric = std::get_if(&typeSig.Type())) + { + index = ptrGeneric->GenericType(); + auto guid = format_guid(generate_guid(*ptrGeneric)); + return { ResolveType(process, index), guid }; + } + return {}; }; void ClearTypeResolver() diff --git a/nuget.config b/nuget.config new file mode 100644 index 000000000..a7584f8d9 --- /dev/null +++ b/nuget.config @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/nuget/CppWinrtRules.Project.xml b/nuget/CppWinrtRules.Project.xml index 572efc31d..43a712d21 100644 --- a/nuget/CppWinrtRules.Project.xml +++ b/nuget/CppWinrtRules.Project.xml @@ -3,6 +3,7 @@ + @@ -76,4 +77,34 @@ Description="Enables or disables the generation of Windows Metadata" Category="General" /> + + + + + + + + + + + + diff --git a/nuget/Microsoft.Windows.CppWinRT.nuspec b/nuget/Microsoft.Windows.CppWinRT.nuspec index a49f1eb8f..52151fc08 100644 --- a/nuget/Microsoft.Windows.CppWinRT.nuspec +++ b/nuget/Microsoft.Windows.CppWinRT.nuspec @@ -2,7 +2,7 @@ Microsoft.Windows.CppWinRT - 1.0.0.0 + $target_version$ C++/WinRT Build Support Microsoft Microsoft @@ -12,6 +12,7 @@ native C++ WinRT nativepackage © Microsoft Corporation. All rights reserved. LICENSE + readme.md https://github.com/Microsoft/cppwinrt https://aka.ms/cppwinrt.ico @@ -20,11 +21,12 @@ - + + diff --git a/nuget/Microsoft.Windows.CppWinRT.props b/nuget/Microsoft.Windows.CppWinRT.props index 6e1344f40..60736e177 100644 --- a/nuget/Microsoft.Windows.CppWinRT.props +++ b/nuget/Microsoft.Windows.CppWinRT.props @@ -13,16 +13,16 @@ Copyright (C) Microsoft Corporation. All rights reserved. - x64 true true false - CppWinRT + CppWinRT true true PreventSdkUapPropsAssignment + true @@ -42,11 +42,25 @@ Copyright (C) Microsoft Corporation. All rights reserved. nul nul + true + + + false + + + + false + + diff --git a/nuget/Microsoft.Windows.CppWinRT.targets b/nuget/Microsoft.Windows.CppWinRT.targets index a04e96a87..a297258f3 100644 --- a/nuget/Microsoft.Windows.CppWinRT.targets +++ b/nuget/Microsoft.Windows.CppWinRT.targets @@ -21,16 +21,31 @@ Copyright (C) Microsoft Corporation. All rights reserved. false true false + true + false $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory)))..\..\ $([System.IO.Path]::GetFullPath($(MSBuildThisFileDirectory))) $(CppWinRTParameters) -fastabi + + -modules + -module_include $(CppWinRTModuleInclude.Replace(';', ' ')) + $(CppWinRTCommandModuleFilter) -module_exclude $(CppWinRTModuleExclude.Replace(';', ' ')) "$(CppWinRTPackageDir)bin\" "$(CppWinRTPackageDir)" + + + true C++ Windows.UI.Xaml $(GeneratedFilesDir)XamlMetaDataProvider.idl $(GeneratedFilesDir)XamlMetaDataProvider.cpp + $(IntDir)$(MSBuildProjectFile).mdmerge.rsp + $(IntDir)$(MSBuildProjectFile).midlrt.rsp + $(IntDir)$(MSBuildProjectFile).cppwinrt_plat.rsp + $(IntDir)$(MSBuildProjectFile).cppwinrt_ref.rsp + $(IntDir)$(MSBuildProjectFile).cppwinrt_comp.rsp + @@ -64,9 +79,13 @@ Copyright (C) Microsoft Corporation. All rights reserved. $(AfterMidlTargets); GetCppWinRTMdMergeInputs; CppWinRTMergeProjectWinMDInputs; - GetResolvedWinMD; + CppWinRTGetResolvedWinMD; CppWinRTCopyWinMDToOutputDirectory; + + $(ResolveReferencesDependsOn); + CppWinRTImplicitlyExpandTargetPlatform + $(ResolveAssemblyReferencesDependsOn);GetCppWinRTProjectWinMDReferences;CppWinRTMarkStaticLibrariesPrivate; @@ -74,9 +93,12 @@ Copyright (C) Microsoft Corporation. All rights reserved. $(BeforeClCompileTargets);CppWinRTAddXamlMetaDataProviderCpp;CppWinRTMakeProjections; + + - $(ComputeCompileInputsTargets);CppWinRTComputeXamlGeneratedCompileInputs;CppWinRTHeapEnforcementOptOut; + CppWinRTComputeXamlGeneratedCompileInputs;$(ComputeCompileInputsTargets);CppWinRTHeapEnforcementOptOut; + $(MarkupCompilePass1DependsOn);CppWinRTAddXamlReferences @@ -86,6 +108,12 @@ Copyright (C) Microsoft Corporation. All rights reserved. $(CleanDependsOn);CppWinRTClean + + $(GetTargetPathDependsOn);CppWinRTGetResolvedWinMD + + + $(GetPackagingOutputsDependsOn);CppWinRTGetResolvedWinMD + @@ -106,12 +134,9 @@ Copyright (C) Microsoft Corporation. All rights reserved. <_FilesToDelete Remove="@(_FilesToDelete)"/> <_FilesToDelete Include="$(GeneratedFilesDir)**"/> - <_FilesToDelete Include="$(OutDir)*.winmd"/> - <_FilesToDelete Include="$(IntDir)*.winmd"/> - <_FilesToDelete Include="$(IntDir)*.idl"/> - <_FilesToDelete Include="$(IntDir)*.rsp"/> <_FilesToDelete Include="$(CppWinRTMergedDir)**"/> <_FilesToDelete Include="$(CppWinRTUnmergedDir)**"/> + <_FilesToDelete Include="$(CppWinRTProjectWinMD)" Condition="'$(CppWinRTGenerateWindowsMetadata)' == 'true'"/> @@ -124,16 +149,66 @@ Copyright (C) Microsoft Corporation. All rights reserved. + + + + + <_TargetPlatformWinMDs Condition="'$(TargetPlatformSdkRootOverride)' != ''" Include="$(TargetPlatformSdkRootOverride)\References\$(XeWin10TargetVersion)\**\*.winmd"> + true + false + $(TargetPlatformMoniker) + $(TargetPlatformDisplayName) + CppWinRTImplicitlyExpandTargetPlatform + True + + <_TargetPlatformWinMDs Condition="'$(TargetPlatformSdkRootOverride)' == ''" Include="$(WindowsSDK_MetadataPathVersioned)\**\*.winmd"> + true + false + $(TargetPlatformMoniker) + $(TargetPlatformDisplayName) + CppWinRTImplicitlyExpandTargetPlatform + True + + + + + + + + + + <_ResolveAssemblyReferenceResolvedFiles Include="@(_TargetPlatformWinMDs)" /> + + + <_TargetPlatformWinMDs Remove="@(_TargetPlatformWinMDs)" /> + + + + DependsOnTargets="GetCppWinRTProjectWinMDReferences;CppWinRTComputeXamlGeneratedMidlInputs;$(CppWinRTComputeGenerateWindowsMetadataDependsOn)"> + + + <_IncludedIdlForComputeGenerateWindowsMetadata Remove="@(_IncludedIdlForComputeGenerateWindowsMetadata)" /> + <_IncludedIdlForComputeGenerateWindowsMetadata Include="@(Midl)" Condition="'%(Midl.ExcludedFromBuild)' != 'true'" /> + - true - true + true + true @@ -142,7 +217,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. - - + - - - - - true - - - - - $([System.IO.Path]::GetFileName('%(Link.WindowsMetadataFile)')) - true - - - - $(WinMDImplementationPath)$(TargetName)$(TargetExt) - winmd - true - $(ConfigurationType) - - - - - - + + <_CppWinRTProjectWinMDItems Include="$(CppWinRTProjectWinMD)" /> + + $([System.IO.Path]::GetFileName('$(CppWinRTProjectWinMD)')) true - $(WinMDImplementationPath)$(TargetName)$(TargetExt) + $(WinMDImplementationPath)$(TargetName)$(TargetExt) winmd true $(MSBuildProjectName) $(ConfigurationType) + + <_CppWinRTProjectWinMDItems Remove="$(CppWinRTProjectWinMD)" /> @@ -215,11 +271,11 @@ Copyright (C) Microsoft Corporation. All rights reserved. <_CppWinRTPlatformWinMDInputs Remove="@(_CppWinRTPlatformWinMDInputs)" /> - <_CppWinRTPlatformWinMDInputs Include="$(WindowsSDK_MetadataPathVersioned)\**\*.winmd" /> + <_CppWinRTPlatformWinMDInputs Include="@(CppWinRTPlatformWinMDReferences)" /> %(FullPath) @@ -234,9 +290,10 @@ Copyright (C) Microsoft Corporation. All rights reserved. <_CppWinRTPlatformWinMDReferences Remove="@(_CppWinRTPlatformWinMDReferences)" /> <_CppWinRTPlatformWinMDReferences Include="@(ReferencePath)" Condition="'%(ReferencePath.IsSystemReference)' == 'true' and '%(ReferencePath.WinMDFile)' == 'true' and '%(ReferencePath.ReferenceSourceTarget)' == 'ResolveAssemblyReference'" /> - <_CppWinRTPlatformWinMDReferences Condition="'$(CppWinRTOverrideSDKReferences)' != 'true'" Include="$(WindowsSDK_MetadataPathVersioned)\**\Windows.Foundation.FoundationContract.winmd" /> - <_CppWinRTPlatformWinMDReferences Condition="'$(CppWinRTOverrideSDKReferences)' != 'true'" Include="$(WindowsSDK_MetadataPathVersioned)\**\Windows.Foundation.UniversalApiContract.winmd" /> - <_CppWinRTPlatformWinMDReferences Condition="'$(CppWinRTOverrideSDKReferences)' != 'true'" Include="$(WindowsSDK_MetadataPathVersioned)\**\Windows.Networking.Connectivity.WwanContract.winmd" /> + + <_CppWinRTPlatformWinMDReferences Include="@(ReferencePath)" Condition="'%(ReferencePath.IsSystemReference)' == 'true' and '%(ReferencePath.WinMDFile)' == 'true' and '%(ReferencePath.ResolvedFrom)' == 'ImplicitlyExpandTargetPlatform'" /> + + <_CppWinRTPlatformWinMDReferences Include="@(ReferencePath)" Condition="'%(ReferencePath.IsSystemReference)' == 'true' and '%(ReferencePath.WinMDFile)' == 'true' and '%(ReferencePath.ResolvedFrom)' == 'CppWinRTImplicitlyExpandTargetPlatform'" /> <_CppWinRTPlatformWinMDReferences Include="$(CppWinRTSDKReferences)" /> @@ -248,11 +305,12 @@ Copyright (C) Microsoft Corporation. All rights reserved. <_CppWinRTDirectWinMDReferences Remove="@(_CppWinRTDirectWinMDReferences)" /> <_CppWinRTDirectWinMDReferences Include="@(ReferencePath)" Condition="'%(ReferencePath.IsSystemReference)' != 'true' and '%(ReferencePath.WinMDFile)' == 'true' and '%(ReferencePath.ReferenceSourceTarget)' == 'ResolveAssemblyReference'" /> + <_CppWinRTDirectWinMDReferences Include="@(ReferencePath)" Condition="'%(ReferencePath.WinMDFile)' == 'true' and '%(ReferencePath.ReferenceSourceTarget)' == 'ExpandSDKReference'" /> %(FullPath) @@ -296,11 +354,11 @@ Copyright (C) Microsoft Corporation. All rights reserved. + DependsOnTargets="CppWinRTResolveReferences" + Returns="@(CppWinRTMdMergeMetadataDirectories);@(CppWinRTMdMergeInputs)"> <_MdMergeInputs Remove="@(_MdMergeInputs)"/> - <_MdMergeInputs Include="@(Midl)"> + <_MdMergeInputs Include="@(Midl)" Condition="'%(Midl.ExcludedFromBuild)' != 'true'"> %(Midl.OutputDirectory)%(Midl.MetadataFileName) $(CppWinRTProjectWinMD) @@ -338,7 +396,7 @@ Copyright (C) Microsoft Corporation. All rights reserved. - >true + true @@ -378,10 +436,10 @@ namespace $(RootNamespace) + Overwrite="true" /> @@ -400,10 +458,10 @@ $(XamlMetaDataProviderPch) + Overwrite="true" /> @@ -412,7 +470,7 @@ $(XamlMetaDataProviderPch) Condition="'$(CppWinRTModernIDL)' != 'false'" DependsOnTargets="GetCppWinRTPlatformWinMDReferences;GetCppWinRTDirectWinMDReferences;GetCppWinRTProjectWinMDReferences;$(CppWinRTSetMidlReferencesDependsOn)" Inputs="$(MSBuildAllProjects);@(CppWinRTDirectWinMDReferences);@(CppWinRTStaticProjectWinMDReferences);@(CppWinRTDynamicProjectWinMDReferences);@(CppWinRTPlatformWinMDReferences)" - Outputs="$(IntDir)midlrt.rsp"> + Outputs="$(CppWinRTMidlResponseFile)"> <_MidlReferences Remove="@(_MidlReferences)"/> <_MidlReferences Include="@(CppWinRTDirectWinMDReferences)"/> @@ -421,8 +479,11 @@ $(XamlMetaDataProviderPch) <_MidlReferences Include="@(CppWinRTPlatformWinMDReferences)"/> <_MidlReferencesDistinct Remove="@(_MidlReferencesDistinct)" /> <_MidlReferencesDistinct Include="@(_MidlReferences->'%(WinMDPath)'->Distinct())" /> - - %(Midl.AdditionalOptions) %40"$(IntDir)midlrt.rsp" + + %(Midl.AdditionalOptions) /nomidl + + + %(Midl.AdditionalOptions) %40"$(CppWinRTMidlResponseFile)" @@ -430,37 +491,81 @@ $(XamlMetaDataProviderPch) + File="$(CppWinRTMidlResponseFile)" Lines="$(_MidlrtParameters)" + Overwrite="true" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + DependsOnTargets="Midl;GetCppWinRTMdMergeInputs;_CppWinRTGenerateMergeProjectWinMDDependencyCache;$(CppWinRTMergeProjectWinMDInputsDependsOn)" + Inputs="$(MSBuildAllProjects);@(CppWinRTMdMergeInputs);@(CustomAdditionalMdMergeInputs)" + Outputs="@(_MdMergedOutput);$(CppWinRTMdMergeResponseFile)"> <_MdMergeDepth Condition="'$(CppWinRTNamespaceMergeDepth)' != ''">-n:$(CppWinRTNamespaceMergeDepth) <_MdMergeDepth Condition="'$(_MdMergeDepth)' == ''">$(CppWinRTMergeDepth) <_MdMergeDepth Condition="'$(_MdMergeDepth)' == '' And '$(CppWinRTRootNamespaceAutoMerge)' == 'true'">-n:$(RootNamespace.Split('.').length) <_MdMergeDepth Condition="'$(_MdMergeDepth)' == '' And ('@(Page)' != '' Or '@(ApplicationDefinition)' != '')">-n:1 - <_MdMergeCommand>$(MdMergePath)mdmerge %40"$(IntDir)mdmerge.rsp" + <_MdMergeCommand>$(MdMergePath)mdmerge %40"$(CppWinRTMdMergeResponseFile)" - <_MdMergeParameters>-v @(CppWinRTMdMergeMetadataDirectories->'-metadata_dir "%(RelativeDir)."', ' ') + <_MdMergeParameters Condition="'$(CppWinRTMergeNoValidate)'!='true'">-v + <_MdMergeParameters>$(_MdMergeParameters) @(CppWinRTMdMergeMetadataDirectories->'-metadata_dir "%(RelativeDir)."', ' ') <_MdMergeParameters>$(_MdMergeParameters) @(CppWinRTMdMergeInputs->'-i "%(Identity)"', ' ') <_MdMergeParameters>$(_MdMergeParameters) -o "$(CppWinRTMergedDir.TrimEnd('\'))" -partial $(_MdMergeDepth) + + File="$(CppWinRTMdMergeResponseFile)" Lines="$(_MdMergeParameters)" + Overwrite="true" /> + @@ -470,6 +575,10 @@ $(XamlMetaDataProviderPch) <_MdMergedOutput Include="$(CppWinRTMergedDir)*.winmd"/> + + + + @@ -483,17 +592,62 @@ $(XamlMetaDataProviderPch) SkipUnchangedFiles="$(CppWinRTSkipUnchangedFiles)" SourceFiles="@(_MdMergedOutput)" DestinationFiles="@(_MdMergedOutput->'$(OutDir)%(Filename)%(Extension)')" /> + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + DependsOnTargets="CppWinRTResolveReferences;GetCppWinRTPlatformWinMDInputs;_CppWinRTMakePlatformProjectionDependencyCache;$(CppWinRTMakePlatformProjectionDependsOn)" + Inputs="$(MSBuildAllProjects);@(CppWinRTPlatformWinMDInputs);@(CustomAdditionalPlatformWinMDInputs)" + Outputs="$(CppWinRTPlatformProjectionResponseFile)"> - $(CppWinRTPath)cppwinrt %40"$(IntDir)cppwinrt_plat.rsp" + $(CppWinRTPath)cppwinrt %40"$(CppWinRTPlatformProjectionResponseFile)" <_CppwinrtInputs Remove="@(_CppwinrtInputs)"/> @@ -501,26 +655,78 @@ $(XamlMetaDataProviderPch) <_CppwinrtParameters>$(CppWinRTCommandVerbosity) $(CppWinRTParameters) + + <_CppwinrtParameters Condition="'$(_CppWinRTConsumesPlatformModules)'!='true'">$(_CppwinrtParameters) $(CppWinRTCommandModules) $(CppWinRTCommandModuleFilter) <_CppwinrtParameters>$(_CppwinrtParameters) @(_CppwinrtInputs->'-in "%(WinMDPath)"', ' ') <_CppwinrtParameters>$(_CppwinrtParameters) -out "$(GeneratedFilesDir)." + + File="$(CppWinRTPlatformProjectionResponseFile)" Lines="$(_CppwinrtParameters)" + Overwrite="true" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DependsOnTargets="CppWinRTResolveReferences;_CppWinRTMakeReferenceProjectionDependencyCache;$(CppWinRTMakeReferenceProjectionDependsOn)" + Inputs="$(MSBuildAllProjects);@(CppWinRTDirectWinMDReferences);@(CppWinRTDynamicProjectWinMDReferences);@(CppWinRTPlatformWinMDReferences);@(CustomAdditionalReferenceWinMDInputs)" + Outputs="$(CppWinRTReferenceProjectionResponseFile)"> - $(CppWinRTPath)cppwinrt %40"$(IntDir)cppwinrt_ref.rsp" + $(CppWinRTPath)cppwinrt %40"$(CppWinRTReferenceProjectionResponseFile)" <_CppwinrtRefInputs Remove="@(_CppwinrtRefInputs)"/> @@ -530,26 +736,77 @@ $(XamlMetaDataProviderPch) <_CppwinrtRefRefs Include="@(CppWinRTPlatformWinMDReferences)"/> - <_CppwinrtParameters>$(CppWinRTCommandVerbosity) $(CppWinRTParameters) + <_CppwinrtParameters>$(CppWinRTCommandVerbosity) $(CppWinRTParameters) $(CppWinRTCommandModules) $(CppWinRTCommandModuleFilter) <_CppwinrtParameters>$(_CppwinrtParameters) @(_CppwinrtRefInputs->'-in "%(WinMDPath)"', ' ') <_CppwinrtParameters>$(_CppwinrtParameters) @(_CppwinrtRefRefs->'-ref "%(WinMDPath)"', ' ') <_CppwinrtParameters>$(_CppwinrtParameters) -out "$(GeneratedFilesDir)." + + File="$(CppWinRTReferenceProjectionResponseFile)" Lines="$(_CppwinrtParameters)" + Overwrite="true" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DependsOnTargets="CppWinRTResolveReferences;GetCppWinRTMdMergeInputs;_CppWinRTMakeComponentProjectionDependencyCache;$(CppWinRTMakeComponentProjectionDependsOn)" + Inputs="$(MSBuildAllProjects);@(CppWinRTMdMergeInputs);@(CppWinRTStaticProjectWinMDReferences);@(CustomAdditionalComponentWinMDInputs)" + Outputs="$(CppWinRTComponentProjectionResponseFile)"> <_PCH>@(ClCompile->Metadata('PrecompiledHeaderFile')->Distinct()) @@ -557,12 +814,13 @@ $(XamlMetaDataProviderPch) Text="Please retarget to 10.0.17709.0 or later, or rename your PCH to 'pch.h'."/> true - $(_PCH) + $(_PCH) + . -prefix -pch $(CppWinRTPrecompiledHeader) - $(CppWinRTPath)cppwinrt %40"$(IntDir)cppwinrt_comp.rsp" + $(CppWinRTPath)cppwinrt %40"$(CppWinRTComponentProjectionResponseFile)" @@ -584,18 +842,23 @@ $(XamlMetaDataProviderPch) <_CppwinrtCompRefs Include="@(CppWinRTPlatformWinMDReferences)"/> - <_CppwinrtParameters>$(CppWinRTCommandVerbosity) $(CppWinRTParameters) -overwrite -name $(RootNamespace) $(CppWinRTCommandPrecompiledHeader) $(CppWinRTCommandUsePrefixes) -comp "$(GeneratedFilesDir)sources" + <_CppwinrtParameters>$(CppWinRTCommandVerbosity) $(CppWinRTParameters) $(CppWinRTCommandModules) $(CppWinRTCommandModuleFilter) -overwrite -name $(RootNamespace) $(CppWinRTCommandPrecompiledHeader) $(CppWinRTCommandUsePrefixes) -comp "$(GeneratedFilesDir)sources" <_CppwinrtParameters Condition="'$(CppWinRTOptimized)'=='true'">$(_CppwinrtParameters) -opt <_CppwinrtParameters>$(_CppwinrtParameters) @(_CppwinrtCompInputs->'-in "%(WinMDPath)"', ' ') <_CppwinrtParameters>$(_CppwinrtParameters) @(_CppwinrtCompRefs->'-ref "%(WinMDPath)"', ' ') <_CppwinrtParameters>$(_CppwinrtParameters) -out "$(GeneratedFilesDir)." + + File="$(CppWinRTComponentProjectionResponseFile)" Lines="$(_CppwinrtParameters)" + Overwrite="true" /> + + + + @@ -603,9 +866,11 @@ $(XamlMetaDataProviderPch) + DependsOnTargets="$(CppWinRTAddXamlReferencesDependsOn);CppWinRTGetResolvedWinMD;GetCppWinRTProjectWinMDReferences"> + + @@ -622,19 +887,103 @@ $(XamlMetaDataProviderPch) - - %(AdditionalOptions) /bigobj /await - %(AdditionalIncludeDirectories);$(GeneratedFilesDir) - - - $(WindowsSDK_MetadataFoundationPath);%(AdditionalMetadataDirectories) - $(WindowsSDK_MetadataPath);%(AdditionalMetadataDirectories) - %(AdditionalOptions) /nomidl - - - %(AdditionalDependencies);WindowsApp.lib - %(AdditionalDependencies);$(CppWinRTPackageDir)build\native\lib\$(Platform)\cppwinrt_fast_forwarder.lib - + + %(AdditionalOptions) /bigobj + %(AdditionalOptions) /await:strict + %(AdditionalIncludeDirectories);$(GeneratedFilesDir) + + + $(WindowsSDK_MetadataFoundationPath);%(AdditionalMetadataDirectories) + $(WindowsSDK_MetadataPath);%(AdditionalMetadataDirectories) + + + %(AdditionalDependencies);WindowsApp.lib + %(AdditionalDependencies);$(CppWinRTPackageDir)build\native\lib\$(Platform)\cppwinrt_fast_forwarder.lib + - + + + + + + CompileAsCppModule + true + NotUsing + + + + + + + + + $(GeneratedFilesDir) + $(IntDir) + $(OutDir) + + + + + + + + + <_CppWinRTModuleProviders Remove="@(_CppWinRTModuleProviders)" /> + <_CppWinRTModuleProviders Include="@(ProjectReference)" + Condition="'%(ProjectReference.CppWinRTConsumeModule)' == 'true'" /> + + + + + <_CppWinRTConsumesPlatformModules>true + + + + + + + + + + + + + <_CppWinRTModuleIfcSearchDirs>@(_CppWinRTResolvedModuleRefs->'%(CppWinRTModuleIfcDir)') + + + + $(_CppWinRTModuleIfcSearchDirs);%(ClCompile.AdditionalBMIDirectories) + + + + diff --git a/nuget/modules.md b/nuget/modules.md new file mode 100644 index 000000000..4f0eebb95 --- /dev/null +++ b/nuget/modules.md @@ -0,0 +1,524 @@ +# C++/WinRT C++20 Modules Guide + +## Overview + +C++/WinRT can generate per-namespace C++20 named modules (`.ixx` files) alongside the traditional projection headers. This allows you to write: + +```cpp +import winrt.Windows.Foundation; +``` + +instead of: + +```cpp +#include +``` + +Benefits include: + +- **Smaller intermediate artifacts.** The IFC representation of a C++/WinRT projection is significantly smaller than the equivalent precompiled header (PCH). +- **Shared compilation across projects.** PCHs eliminate redundant parsing *within* a project, but each project rebuilds its own PCH. Module IFCs can be built once in a shared module builder project and consumed by all dependent projects, eliminating redundant work across the entire solution. +- **Better isolation.** Module imports don't leak macros into the importing translation unit. + +> **Real-world experience:** The guidance in this document was hardened against a prototype conversion of the Windows Terminal codebase (~250 files across 15+ projects) from textual `#include`s to module imports. + +## Prerequisites + +- **MSVC v145 toolset** (Visual Studio 2026) or later with C++20 module support +- **C++/WinRT 3.x** NuGet package with `CppWinRTBuildModule` support +- `/std:c++20` or later (`/std:c++latest` recommended for `import std;`) +- `BuildStlModules=true` for `import std;` support + +## Three Fundamental Constraints + +These three rules drive almost every pattern in this guide. Understanding them up front makes the rest of the document much easier to follow: + +1. **`import` in a PCH breaks the compiler.** MSVC cannot handle module `import` declarations inside a precompiled header — it produces internal compiler errors (ICE). All module imports must live outside the PCH. + +2. **Include-then-import is safe, but has a cost.** MSVC handles the case where you `#include` a header and then `import` the same content. For STL headers this is a reasonable workaround (the compilation cost is modest), but C++/WinRT projection headers can be extremely expensive to parse textually. Include-then-import with winrt headers defeats the build-cost improvements of adopting modules, so it should be avoided where possible. + +3. **Import-then-include is not supported.** When a module has already been imported, `#include`-ing a header that declares the same types causes errors — the compiler sees conflicting declarations. C++/WinRT provides a workaround: defining `WINRT_IMPORT_MODULE` before including a winrt header causes the header to be (mostly) a no-op, defining only its include-guard macro. This makes it safe to include winrt headers *after* importing modules, which is essential for lighting up header-guard-based features in libraries like WIL, and for interoperating with external code that includes winrt headers out of your control (e.g. the XAML compiler). STL headers do not have an equivalent workaround, but can fall back on the "generally safe" include-then-import pattern. + +The patterns throughout this guide are direct consequences of these constraints: + +- The PCH is stripped of all winrt content. +- A separate "module preamble" header centralizes imports. +- Wrapper headers define `WINRT_IMPORT_MODULE` before including winrt headers. +- STL headers are sometimes pre-included in the PCH, but only as a workaround. + +## Quick Start — Single Project + +For a small project that builds and consumes its own modules: + +1. Set `CppWinRTBuildModule` to `true` in your project: + ```xml + + true + + ``` + +2. Optionally limit which namespaces get modules: + ```xml + + Windows.Foundation;Windows.Storage + + ``` + + By default, C++/WinRT generates and builds `.ixx` files for **every** namespace reachable from your WinMD inputs — for a typical Windows SDK projection, that's hundreds of namespaces and can add upwards of a minute to your build. If build speed matters, prune the set with `CppWinRTModuleInclude` (or `CppWinRTModuleExclude`) so that only the namespaces you actually `import` are produced. See [Understanding the Include and Exclude Filters](#understanding-the-include-and-exclude-filters) for the full discussion. + +3. Enable `BuildStlModules` for `import std;` support: + ```xml + + true + + ``` + +4. In your `.cpp` files: + ```cpp + import winrt.Windows.Foundation; + + int main() { + winrt::init_apartment(); + winrt::Windows::Foundation::Uri uri(L"https://example.com"); + } + ``` + +For a single vcxproj — or a handful of projects that don't share many namespaces — this is all you need. A dedicated module builder is only worth the overhead when multiple projects need the same platform and third-party WinRT modules. + +## Architecture — Module Builder (Recommended for Multi-Project Solutions) + +For solutions with multiple projects, the recommended approach is a dedicated **module builder** project that builds the shared IFC files once, consumed by all other projects. This avoids each project redundantly compiling the same module interfaces. + +### The Module Builder Project + +Create a dedicated static library project whose sole purpose is to build the shared platform module IFC files. This project has no source files of its own — the C++/WinRT NuGet targets generate `.ixx` files automatically. + +```xml + + StaticLibrary + true + true + +``` + +Key configuration: + +- **`CppWinRTBuildModule=true`** generates per-namespace `.ixx` module interface files. +- **`BuildStlModules=true`** is required because the generated `.ixx` files use `import std;`. +- **`WINRT_ENABLE_LEGACY_COM`** — define this preprocessor macro if your project uses classic COM interfaces (`IUnknown`, `IInspectable`). It causes `` and `` to be included within the `winrt_base` module. +- **PCH**: don't bother. The module builder can use a PCH, but there is little benefit — its content is almost entirely C++/WinRT headers, and most non-C++/WinRT headers are only used once in `winrt_base`. + +### Consumer Project Configuration + +Each project that uses modules needs: + +```xml + + true + MyCompany.MyComponent + Microsoft.UI;Microsoft.Web + + + + true + + +``` + +- **`CppWinRTBuildModule=true`** is required on consumers too. It enables the C++/WinRT targets to generate `.ixx` module interface files for the project's own component namespaces (filtered by `CppWinRTModuleInclude` / `CppWinRTModuleExclude`) and wires up module consumption from referenced projects. +- **`CppWinRTConsumeModule=true`** on the `ProjectReference` tells the build system to use the builder's pre-built platform IFCs instead of compiling platform `.ixx` files again, and skip generating platform `.ixx` files in the consumer's own projection. + +### Adding Third-Party WinMDs to the Module Builder + +If your project uses third-party WinRT components (e.g., WinUI/MUX, WebView2), build their modules in the shared builder by adding their WinMDs as platform inputs: + +```xml + + + + + + +``` + +**Important:** Add these WinMDs as `CppWinRTPlatformWinMDReferences`, **not** as NuGet `` items. In practice, adding third-party WinMDs via NuGet `` items on the module builder has been observed to cause empty path errors in the reference projection step. Adding them directly as `CppWinRTPlatformWinMDReferences` feeds them into the platform projection, which is the correct pipeline for a module builder project. + +### Component DLLs + +WinRT component projects can also use modules. Set `CppWinRTBuildModule=true` and all three projections (platform, reference, component) will generate `.ixx` files. + +```xml + + true + MyComponent + + + + true + + +``` + +If project A references a component DLL from project B, project A builds its own reference projection modules from B's `.winmd`: + +```cpp +// These modules are built locally from the component's .winmd +import winrt.MyComponent; + +auto obj = winrt::MyComponent::MyClass(); +``` + +## MSBuild Properties + +| Property | Default | Description | +|-|-|-| +| `CppWinRTBuildModule` | `false` | Generate `.ixx` module interface units from projections | +| `CppWinRTModuleInclude` | (all) | Semicolon-delimited namespace prefixes to include in module generation | +| `CppWinRTModuleExclude` | (none) | Semicolon-delimited namespace prefixes to exclude from module generation | + +| `ProjectReference` Metadata | Default | Description | +|-|-|-| +| `CppWinRTConsumeModule` | `false` | Consume pre-built platform module IFCs from this project reference | + +### Understanding the Include and Exclude Filters + +These filters control which C++/WinRT module `.ixx` files are generated from WinMD inputs. They only affect modules produced by the C++/WinRT NuGet targets from `.winmd` files — hand-authored modules or modules unrelated to WinRT are not affected. + +- **`CppWinRTModuleInclude`** — semicolon-separated namespace prefixes. Only matching namespaces will have `.ixx` files generated. Without this, *all* namespaces from the project's WinMD inputs are candidates. +- **`CppWinRTModuleExclude`** — semicolon-separated namespace prefixes to suppress `.ixx` generation for. **Import statements are still generated** in the modules that remain; only the `.ixx` file creation is suppressed. + +#### Why Filter at All? + +There are three distinct reasons to set these filters. Most non-trivial projects will hit more than one: + +1. **Build performance.** By default, enabling `CppWinRTBuildModule=true` generates and compiles `.ixx` files for *every* namespace reachable from your WinMD inputs. For a typical Windows SDK projection that's hundreds of namespaces, and the IFC compilation step can easily add upwards of a minute to a clean build. Use `CppWinRTModuleInclude` to narrow generation to namespaces you (or your consumers) actually `import`, or `CppWinRTModuleExclude` to drop large subtrees you don't use (e.g., `Windows.Devices`, `Windows.Media`). For a dedicated module builder project that other projects consume, this is typically the dominant reason to filter. + +2. **Avoiding ambiguous IFC errors (C7684).** An IFC is ambiguous when the same module name resolves to two different `.ifc` files — typically one built locally and one from a referenced project. See [When to Set an Exclude for Ambiguity](#when-to-set-an-exclude-for-ambiguity) below. + +3. **Avoiding modules with unsatisfied dependencies.** Filtering to a subset of namespaces does *not* prune the `import` statements those modules emit for their dependencies — so a generated module whose dependencies fall outside your filter will fail to compile. See [Module Filtering and Transitive Dependencies](#module-filtering-and-transitive-dependencies) below. + +#### When to Set an Exclude for Ambiguity + +Exclude a namespace when its IFC is already provided by a project you reference: + +| Scenario | Action | +|----------|--------| +| You reference a **static library** with `CppWinRTBuildModule=true` | Exclude namespaces that static lib produces (its IFCs propagate via `AdditionalBMIDirectories`) | +| You reference a **DLL** with `CppWinRTBuildModule=true` | **No exclude needed** — DLL IFCs don't propagate by default | +| The **module builder** produces a namespace (e.g., `Microsoft.UI`) | Exclude it — the module builder's IFCs propagate via `CppWinRTConsumeModule` | + +Example — a project that references both the module builder (which provides `Microsoft.UI.*`) and a `TerminalCore` static library (which provides `Microsoft.Terminal.Core`): + +```xml +Microsoft.Terminal +Microsoft.Terminal.Core;Microsoft.UI;Microsoft.Web +``` + +### Module Filtering and Transitive Dependencies + +`CppWinRTModuleInclude` and `CppWinRTModuleExclude` control which namespace `.ixx` files are **generated**, but they do not suppress `import` statements for dependencies. If namespace A is included in the filter and depends on namespace B, the generated `winrt.A.ixx` will contain `import winrt.B;` even if B is excluded from the filter. This is by design — the module for B must exist *somewhere* (either from the same project or from a referenced project). + +Implications: + +- **Transitive closure must be satisfied.** If you filter to a subset of namespaces, any dependencies that fall outside the filter must be available from another source (e.g., a module builder project referenced via `CppWinRTConsumeModule`, or MSBuild's automatic `ReferencedModuleBMIs` from a static library reference). Otherwise, compilation will fail with "could not find module" errors. + +- **Use `CppWinRTModuleExclude` to avoid generating modules that have unsatisfied dependencies.** For example, `Windows.Foundation.Diagnostics` depends on `Windows.Storage`. If you filter to `CppWinRTModuleInclude=Windows.Foundation`, the Diagnostics `.ixx` will be generated (it matches the prefix) but will fail to compile because `winrt.Windows.Storage` doesn't exist. Add `CppWinRTModuleExclude=Windows.Foundation.Diagnostics` to prevent this. + +- **In multi-project scenarios with static libraries, use `CppWinRTModuleExclude` to avoid duplicate modules.** MSBuild automatically propagates all module IFCs from static library references to consuming projects (via the `AllProjectBMIsArePublic` property, which defaults to `true` for static libraries). If project A is a static library that builds modules for namespace X, and project B references A, then B already has A's IFCs available. If B also has `CppWinRTBuildModule=true`, its reference projection will generate a second `winrt.X.ixx`, causing an ambiguous module error. Set `CppWinRTModuleExclude=X` on project B to prevent this. B's own `.ixx` files will still emit `import winrt.X;`, which resolves to A's IFC via MSBuild's automatic propagation. Note: this issue does not affect DLL references — MSBuild does not propagate module IFCs from DLLs by default. + +## Module Names + +| Module | Contents | +|-|-| +| `winrt_base` | Core C++/WinRT types (`hstring`, `com_ptr`, `IUnknown`, etc.) — re-exported by all namespace modules | +| `winrt_numerics` | `Windows::Foundation::Numerics` types — re-exported by `winrt_base` | +| `winrt.` | Per-namespace projection (e.g., `winrt.Windows.Foundation`) | + +## Converting an Existing Project: Step by Step + +### 1. Strip winrt from the PCH + +Module `import` declarations inside a precompiled header cause compiler ICEs. All C++/WinRT content must be moved out of the PCH and into the module preamble header (see next step). + +Remove the following from your PCH: + +- `#include ` — all winrt projection headers +- `#include ` and `` +- Any header that depends on C++/WinRT types, **including headers that conditionally enable behavior based on C++/WinRT header guards.** For example, `wil/cppwinrt_helpers.h` checks for `WINRT_Windows_UI_Core_H` and uses types from `Windows.UI.Core` when defined — this header must move out of the PCH. + +**Keep** in the PCH: + +- Platform SDK headers (``, ``, etc.) +- Non-winrt third-party headers + +**STL headers and `import std;`** — STL headers are safe to include *before* `import std;` (include-then-import works). Problems arise when STL headers are included *after* `import std;`, which can cause redefinition warnings (C4348, C5028). In most cases it is preferable to use `import std;` instead of putting STL headers in the PCH. However, if you depend on libraries that internally `#include` STL headers *after* modules have been imported, you may need to pre-include the offending STL headers in the PCH to make the later inclusion inert: + +```cpp +// Pre-include STL headers that other libraries include after import std; +#include +#include +``` + +Ideally, the offending library code would also adopt `import std;`, but that's not always immediately practical. + +### 2. Create a Module Preamble Header + +A module preamble header centralizes the module imports and library setup that are shared across a project's source files. It is not strictly required — you could add imports directly to each `.cpp` file — but it speeds up migration significantly and becomes necessary if you need to deal with generated files outside your control (see [XAML Projects](#xaml-projects)). + +Create a preamble header in each project that contains the module imports commonly used across that project: + +```cpp +// ModulePreamble.h (or whatever name you prefer) +#pragma once + +#define WINRT_IMPORT_MODULE + +// Import the C++/WinRT namespaces used across this project +import winrt.Windows.Foundation; +import winrt.Windows.Foundation.Collections; +import winrt.Windows.System; +// ... other namespaces your project needs + +// Component modules +import winrt.MyCompany.MyComponent; +``` + +Importing modules is cheap, so don't hesitate to list every namespace the project uses. You can add library wrapper headers and other setup to this file as needed (see next section). + +Include the preamble in each `.cpp` file after the PCH: + +```cpp +#include "pch.h" +#include "ModulePreamble.h" +// ... rest of the file +``` + +### 3. Wrap Library Headers That Depend on C++/WinRT Types + +Libraries like WIL use `#ifdef WINRT_Windows_Foundation_H` guards to conditionally enable winrt-dependent features. With modules, those header guards are never defined because the winrt headers are never textually included. To light up this behavior, define the appropriate header guards before including the library header — or, equivalently, include the now-inert winrt headers under `WINRT_IMPORT_MODULE`, which defines the guards as a side effect. + +You can do this directly in the module preamble: + +```cpp +// In your module preamble header +import winrt.Windows.Foundation; +#define WINRT_IMPORT_MODULE +#define WINRT_Windows_Foundation_H // Lights up WIL's Foundation support +#include +``` + +If you include the library from many places, you can author a **wrapper header** that bundles the imports, guard definitions, and library include together: + +```cpp +// wil_cppwinrt_module.h +#pragma once + +import winrt_base; +import winrt.Windows.Foundation; +import winrt.Windows.Foundation.Collections; + +#define WINRT_IMPORT_MODULE +// Define header guards to light up WIL's conditional winrt features. +// You can either define the guards directly, or include the now-inert +// winrt headers (which define the guards as a side effect): +#include +#include + +#include +#include +``` + +Include this wrapper in your module preamble as needed. Re-importing modules and re-defining header guards is harmless, so there's no issue with including the wrapper from multiple places. + +This pattern applies to any library that conditionally uses winrt types based on header include guards. + +### 4. Update the vcxproj + +```xml + +true +MyCompany.MyComponent +Microsoft.UI;Microsoft.Web + + + + true + +``` + +### 5. Add Solution Build Dependency + +If using `.slnx` or `.sln`, add a build dependency to ensure the module builder, if using, compiles first: + +```xml + +``` + +## Special Cases + +### XAML Projects + +XAML projects have a two-pass compilation model that complicates module adoption. The XAML compiler generates source files (`XamlTypeInfo.g.cpp`, `XamlTypeInfo.Impl.g.cpp`, `XamlMetaDataProvider.cpp`) that use winrt types but don't know about modules. + +The solution is to inject `ModulePreamble.h` via the `/FI` (forced include) compiler flag on these generated files, using MSBuild targets: + +```xml + + + + + NotUsing + $(MSBuildProjectDirectory)\ModulePreamble.h + + + +``` + +For static library projects, the XAML build system has a second compilation pass (`CompileXamlGeneratedFiles`) that runs after `ClCompile` but before `Lib`. For DLLs and EXEs, the same targets run after the build compile phase. In either case, you need a second target to ensure the `/FI` metadata is applied before `CompileXamlGeneratedFiles`: + +```xml + + + + NotUsing + $(MSBuildProjectDirectory)\ModulePreamble.h + + + +``` + +**Critical MSBuild detail:** Use ``, not just ``. Without `Update`, MSBuild adds new items instead of modifying metadata on existing ones. + +**Why the preamble header needs `#include "pch.h"` for XAML:** When used as `/FI`, the preamble header is processed *before* the generated file's own `#include "pch.h"`. Adding `#include "pch.h"` at the top of the preamble ensures platform headers are available. For regular `.cpp` files that already include pch.h before the preamble, it's a `#pragma once` no-op. If your project doesn't use the `/FI` approach for XAML, you don't need pch.h in the preamble. + +#### Understanding the XAML Build Order + +The XAML build targets schedule `MarkupCompilePass2` and `CompileXamlGeneratedFiles` differently depending on project type: + +- **Static libraries** (`AfterClCompileTargets`): Pass2 runs after `ClCompile`, before `Lib` +- **DLLs/EXEs** (`AfterBuildCompileTargets`): Pass2 runs after the build compile phase, before `Link` + +In both cases, the order is: + +1. **MarkupCompilePass1** (before `ClCompile`): Generates `.xaml.g.h` declarations +2. **ClCompile**: Compiles your source files +3. **MarkupCompilePass2**: Generates `.xaml.g.hpp` implementations +4. **CompileXamlGeneratedFiles**: Compiles `XamlTypeInfo.g.cpp` (which `#include`s the `.xaml.g.hpp` files) + +The XAML-generated items (`XamlTypeInfo.g.cpp`, etc.) are added to `ClCompile` by the `ComputeXamlGeneratedCompileInputs` target, which is why the `/FI` target uses `AfterTargets="ComputeXamlGeneratedCompileInputs"`. + +### Headers with Conditional winrt Conversions + +If you have utility types (like a `color` struct) with conversion operators gated behind winrt header guards: + +```cpp +#ifdef WINRT_Windows_UI_H + operator winrt::Windows::UI::Color() const { ... } +#endif +``` + +These guards must be defined **before** the header is first included (`#pragma once` means it won't be processed again). Create a dedicated wrapper that imports the module, sets the guard, then re-includes the header: + +```cpp +// color_module.h +#pragma once +import winrt.Windows.UI; +#define WINRT_IMPORT_MODULE +#include // Sets WINRT_Windows_UI_H +#include // Now sees the guard, enables converters +``` + +Include this wrapper in your `ModulePreamble.h` **before** any other header that might include the guarded file. + +### Win32 Macro Conflicts + +Some Win32 macros (e.g., `GetObject` from `wingdi.h`) conflict with WinRT method names. With headers, the macro was applied to both the projection types and the call site. With modules, the WinRT types don't have the macro applied (they were compiled in the module without the macro), but your source file still has the macro defined. Use: + +```cpp +#pragma push_macro("GetObject") +#undef GetObject +// ... code using winrt types with GetObject method +#pragma pop_macro("GetObject") +``` + +## Caution: Module Reuse Across Projects + +Pre-built IFCs (via `CppWinRTConsumeModule`) should only be shared when the builder and consumer use the same compilation context. In particular: + +- **Component modules are project-private.** A component projection built with `CppWinRTOptimized=true` generates modules that bypass activation factories for in-component type instantiation (`-opt`). If a consuming project accidentally imports these modules instead of building its own reference projection, the consumer will attempt direct instantiation across DLL boundaries, resulting in linker errors or incorrect behavior. Each project should build its own modules from the component's `.winmd` — do not tag component `ProjectReference`s with `CppWinRTConsumeModule`. + +- **`CppWinRTConsumeModule` is intended for platform module builders only.** The builder project is a dedicated static library whose sole purpose is compiling platform SDK modules. Its compilation flags (no `-opt`, no `-comp`) produce modules safe for any consumer. Only tag this builder's `ProjectReference` with `CppWinRTConsumeModule=true`. + +- **Module filter scope matters.** `CppWinRTModuleInclude` / `CppWinRTModuleExclude` applies to all three projections (platform, reference, component). If you set `CppWinRTModuleInclude=MyComponent`, only `MyComponent` namespaces will get `.ixx` files — platform and reference namespace modules will not be generated. Make sure your filter includes all namespaces you intend to import as modules, or use `CppWinRTConsumeModule` to get platform modules from a builder that was configured with the appropriate filter. + +- **Compilation settings must be compatible between builder and consumer.** Module IFCs encode assumptions about the compilation environment. While the compiler may not always diagnose mismatches, the following differences between the module builder and consumer may cause subtle or hard-to-diagnose issues: + - **Debug vs Release** — mixing Debug and Release configurations can produce mismatched code generation, iterator debugging levels, and runtime library selections. + - **Preprocessor definitions** — definitions that affect type layout, conditional compilation, or feature flags should match between builder and consumer. + - **Struct alignment / packing** — different `/Zp` settings between projects can change struct layout, causing silent ABI mismatches. + - **Language standard** — while C++20 and later are generally compatible, mixing `/std:c++20` and `/std:c++23` and/or `/std:c++latest` can affect type definitions if language features differ. + + As a general rule, the module builder project should try to use the same configuration, preprocessor definitions, and compiler options as its consumers. + +## Limitations + +- Module IFCs are not compatible across toolset versions. All projects must use the same toolset. +- Cyclic namespace dependencies (e.g., `Windows.Foundation` ↔ `Windows.Foundation.Collections`) are handled automatically via SCC consolidation, but the resulting module name is chosen alphabetically. Adding new APIs could change SCC groupings. + +## Common Errors and Solutions + +| Error | Cause | Solution | +|-------|-------|----------| +| **C2230**: could not find module `winrt.X` | Missing import or the module IFC wasn't built | Add the import; verify the module builder or consumer produces it; check `CppWinRTModuleInclude` | +| **C7684**: ambiguous resolution to IFC | Same module built by multiple projects visible to the consumer | Add the namespace to `CppWinRTModuleExclude` | +| **C4348**: redefinition of default parameter | STL header included after `import std;` | Pre-include the STL header in the PCH (e.g., `#include `) | +| **C5028**: alignment specified in prior declaration | Same root cause as C4348 | Pre-include the STL header (e.g., `#include `) | +| **C4430 / C2039**: missing type / not a member | Type not visible because module not imported | Add the missing `import winrt.Namespace;` to ModulePreamble | +| **LNK2019**: unresolved external for `InitializeComponent` | XAML-generated code compiled without module imports | Ensure the `/FI` targets are present and use `Update="@(ClCompile)"` | +| **LNK2005**: symbol already defined | XAML-generated file compiled both directly and via a wrapper | Use the `/FI` injection approach instead of wrapper `.cpp` files | +| Redefinition errors when mixing `#include` and `import` | Same namespace included textually after being imported | Define `WINRT_IMPORT_MODULE` before the winrt header, or remove the `#include` | +| **"could not find module 'winrt.X'"** in cross-project consumer | Builder's `IntDir` not visible to consumer | Verify `ProjectReference` to the builder has `CppWinRTConsumeModule=true` | +| Linker errors for component constructors | Importing a component's internal module instead of building your own reference projection | Remove explicit `/reference` flags for component IFCs and ensure your project has `CppWinRTBuildModule=true` so it builds reference projection modules from the component's `.winmd` | + +## Tips + +- **Clean build after configuration changes.** Stale IFC files from previous builds cause confusing ambiguity errors. Clean the intermediate directory when changing module include/exclude filters. +- **Start from leaf projects.** Convert projects with no WinRT component dependencies first (e.g., utility libraries), then work up the dependency graph. +- **One project at a time.** Each conversion may surface new missing imports or exclude requirements. Building incrementally makes errors easier to diagnose. +- **Watch for transitive IFC propagation.** Static library IFCs become visible to all consumers in the reference chain. This is correct behavior but requires `CppWinRTModuleExclude` entries in consumers. +- **`CppWinRTModuleInclude` is usually needed.** Without it, projects with `CppWinRTModuleExclude` may not generate any `.ixx` files at all. Specify the namespace prefix for your component (e.g., `Microsoft.Terminal`). +- **DLL wrapper projects typically need no changes.** If you have a pattern of "static lib + thin DLL wrapper", the DLL wrapper usually just links the lib and doesn't need module conversion. + +## Approaches Tried and Abandoned + +For posterity, these patterns were attempted during the Terminal prototype but proved problematic. Avoid them. + +### Wrapper `.module.cpp` files for XAML-generated code + +The idea was to create wrapper `.cpp` files that would `#include` the XAML-generated files after setting up module imports, then remove the original generated items from `ClCompile` and replace them with the wrappers. + +**Why it failed:** The XAML build system generates files across two compilation passes. `XamlTypeInfo.g.cpp` `#include`s `.xaml.g.hpp` files that don't exist until Pass2, but the wrapper needed to compile during the first `ClCompile` pass. `__has_include` guards to make wrappers compile empty initially and recompile later proved unreliable — `CompileXamlGeneratedFiles` is a separate compilation step, and the wrapper items weren't correctly routed through it. + +**The `/FI` approach works** because it modifies the compiler flags on the *existing* generated items rather than replacing them, working with the XAML build system's two-pass compilation. + +### `AllProjectBMIsArePublic=false` on static libraries + +The idea was to prevent IFC propagation from static libraries by setting `AllProjectBMIsArePublic=false`, hiding the static lib's IFCs from consumers. + +**Why we moved away:** This fights against the intended VC++ build model. If a project consumes a static library, it should also consume that library's IFC files. Using different IFCs for the same types risks ODR violations. The `CppWinRTModuleExclude` approach is better — the consumer avoids building duplicate IFCs while still consuming the producer's IFCs. + +### Moving wrapper items into MSBuild targets + +The idea was to add wrapper `.cpp` `ClCompile` items inside a `` (dynamically) instead of a static ``, so they'd only enter `ClCompile` after generated files existed. + +**Why it failed:** Items added inside targets don't get the same metadata processing (module dependency scanning, IFC reference resolution) as items in static `ItemGroup`s. The compiler couldn't find any modules. diff --git a/nuget/readme.md b/nuget/readme.md index 696bf1b76..16eb5d2bb 100644 --- a/nuget/readme.md +++ b/nuget/readme.md @@ -36,12 +36,12 @@ It sets the following project properties and item metadata: | XamlLanguage | CppWinRT | Directs the Xaml compiler to generate C++/WinRT code | | ClCompile.CompileAsWinRT | *false | Enables ISO C++ compilation (disables C++/CX) | | ClCompile.LanguageStandard | *stdcpp17 | Enables C++17 language features | -| ClCompile.AdditionalOptions | /bigobj /await | Enables support for large object files and coroutines | +| ClCompile.AdditionalOptions | /bigobj | Enables support for large object files | | ClCompile.AdditionalIncludeDirectories | GeneratedFilesDir | Adds $(GeneratedFilesDir) to the C++ include dirs | | Link.AdditionalDependencies | WindowsApp.lib | Umbrella library for Windows Runtime imports | | Midl.AdditionalOptions | /reference ... | Enables faster compilation with winmd references (versus idl imports) | | Midl.EnableWindowsRuntime | true | Enables Windows Runtime semantics | -| Midl.MetadataFileName | Unmerged\%(Filename).winmd | Generates unmerged metadata in a tempoary location | +| Midl.MetadataFileName | Unmerged\%(Filename).winmd | Generates unmerged metadata in a temporary location | | Midl.GenerateClientFiles, GenerateServerFiles, GenerateStublessProxies, GenerateTypeLibrary, HeaderFileName, DllDataFileName, InterfaceIdentifierFileName, ProxyFileName, TypeLibraryName | *nul, *None, *false | Disable unnecessary output | \*If not already set @@ -61,6 +61,7 @@ C++/WinRT behavior can be customized with these project properties: | CppWinRTVerbosity | low \| *normal \| high | Sets the [importance](https://docs.microsoft.com/en-us/visualstudio/msbuild/message-task?view=vs-2017) of C++/WinRT build messages (see below) | | CppWinRTNamespaceMergeDepth | *1 | Sets the depth of namespace merging (Xaml apps require 1) | | CppWinRTRootNamespaceAutoMerge | true \| *false | Sets the namespace merge depth to be the length of the root namespace | +| CppWinRTMergeNoValidate | true \| *false | Disables mdmerge validation | | CppWinRTUsePrefixes | *true \| false | Uses a dotted prefix namespace convention (versus a nested folder convention) | | CppWinRTPath | ...\cppwinrt.exe | NuGet package-relative path to cppwinrt.exe, for custom build rule invocation | | CppWinRTParameters | "" | Custom cppwinrt.exe command-line parameters (be sure to append to existing) | @@ -68,6 +69,10 @@ C++/WinRT behavior can be customized with these project properties: | CppWinRTProjectLanguage | C++/CX \| *C++/WinRT | Selects the C++ dialect for the project. C++/WinRT provides full projection support, C++/CX permits consuming projection headers. | | CppWinRTOptimized | true \| *false | Enables component projection [optimization features](https://kennykerr.ca/2019/06/07/cppwinrt-optimizing-components/) | | CppWinRTGenerateWindowsMetadata | true \| *false | Indicates whether this project produces Windows Metadata | +| CppWinRTEnableDefaultPrivateFalse | true \| *false | Indicates whether this project uses C++/WinRT optimized default for copying binaries to the output directory | +| CppWinRTBuildModule | true \| *false | Generates per-namespace C++20 module interface units (.ixx) alongside projection headers | +| CppWinRTModuleInclude | namespace list | Semicolon-delimited namespaces to include in module generation (default: all) | +| CppWinRTModuleExclude | namespace list | Semicolon-delimited namespaces to exclude from module generation | \*Default value To customize common C++/WinRT project properties: @@ -75,6 +80,72 @@ To customize common C++/WinRT project properties: * expand the Common Properties item * select the C++/WinRT property page +## InitializeComponent + +In older versions of C++/WinRT, Xaml objects called InitializeComponent from constructors. This can lead to memory corruption if InitializeComponent throws an exception. + +```cpp +void MainPage::MainPage() +{ + // This pattern should no longer be used + InitializeComponent(); +} +``` + +C++/WinRT now calls InitializeComponent automatically and safely, after object construction. Explicit calls to InitializeComponent from constructors in existing code should now be removed. Multiple calls to InitializeComponent are idempotent. + +If a Xaml object needs to access a Xaml property during initialization, it should override InitializeComponent: + +```cpp +void MainPage::InitializeComponent() +{ + // Call base InitializeComponent() to register with the Xaml runtime + MainPageT::InitializeComponent(); + // Can now access Xaml properties + MyButton().Content(box_value(L"Click")); +} +``` + +A non-Xaml object can also participate in two-phase construction by defining an InitializeComponent method. + +```cpp +void MyComponent::InitializeComponent() +{ + // Execute initialization logic that may throw +} +``` + +***[Windows|Microsoft]::UI::Xaml::Markup::ComponentConnectorT*** + +A consequence of calling InitializeComponent outside construction is that Xaml runtime callbacks to IComponentConnector::Connect and IComponentConnector2::GetBindingConnector are now dispatched to the most derived implementations. Previously, these calls were dispatched directly to the class under construction, as the vtable had yet to be initialized. For objects with markup that derive from composable base classes with markup, this is a breaking change. Derived classes must now implement IComponentConnector::Connect and IComponentConnector2::GetBindingConnector by explicitly calling into the base class. The ComponentConnectorT template provides a correct implementation for these interfaces: + +```cpp +struct DerivedPage : winrt::Windows::UI::Xaml::Markup::ComponentConnectorT> +``` + +If overriding DerivedPage::InitializeComponent, ComponentConnectorT::InitializeComponent should be called instead of DerivedPageT::InitializeComponent: + +```cpp +void DerivedPage::InitializeComponent() +{ + // Call base InitializeComponent() to register with the Xaml runtime + ComponentConnectorT::InitializeComponent(); + // Can now access Xaml properties from base or derived class + MyBaseButton().Content(box_value(L"Click")); +} +``` + +## C++20 Modules + +C++/WinRT supports C++20 named modules as an alternative to `#include`-based consumption. Instead of `#include `, you can write `import winrt.Windows.Foundation;`. + +See the [C++/WinRT C++20 Modules Guide](https://github.com/microsoft/cppwinrt/blob/master/nuget/modules.md) for the full guide (also shipped alongside this file as `modules.md`). + +| ProjectReference metadata | Description | +|-|-| +| CppWinRTConsumeModule | true \| *false | When set on a ProjectReference, consumes pre-built platform module IFCs from the referenced project | +\*Default value + ## Troubleshooting The msbuild verbosity level maps to msbuild message importance as follows: @@ -90,7 +161,18 @@ For example, if the verbosity is set to minimal, then only messages with high im The default importance of C++/WinRT build messages is 'normal', but this can be overridden with the CppWinRTVerbosity property to enable throttling of C++/WinRT messages independent of the overall verbosity level. Example: -> msbuild project.vcxproj /vebosity:minimal /property:CppWinRTVerbosity=high ... +> msbuild project.vcxproj /verbosity:minimal /property:CppWinRTVerbosity=high ... For more complex analysis of build errors, the [MSBuild Binary and Structured Log Viewer](http://msbuildlog.com/) is highly recommended. +## Building, Testing + +Be sure to get the latest nuget.exe from [nuget.org](https://www.nuget.org/downloads) and place it in your path. + +Build the package by running [build_nuget.cmd](../build_nuget.cmd) from a developer environment command line. For testing pass a version number that is much higher than your currently installed, like: + +``` +c:\repos\cppwinrt> .\build_nuget.cmd 5.0.0.0 +``` + +Add the cppwinrt repo directory as a nuget source location and update your projects' references to point at it, update project references, then rebuild a test/sample project. diff --git a/nuget/readme.txt b/nuget/readme.txt index 049ab13b8..8d2681b6b 100644 --- a/nuget/readme.txt +++ b/nuget/readme.txt @@ -19,4 +19,7 @@ In addition, C++/WinRT generates templates and skeleton implementations for each ======================================================================== For more information, visit: https://github.com/Microsoft/cppwinrt/tree/master/nuget + +The full documentation is also included in this package as readme.md, and +the C++20 modules guide as modules.md. ======================================================================== diff --git a/prebuild/CMakeLists.txt b/prebuild/CMakeLists.txt new file mode 100644 index 000000000..da2162d44 --- /dev/null +++ b/prebuild/CMakeLists.txt @@ -0,0 +1,23 @@ +cmake_minimum_required(VERSION 3.16) + +project(cppwinrt-prebuild LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED True) + +if(NOT DEFINED CPPWINRT_BUILD_VERSION) + message(FATAL_ERROR "CPPWINRT_BUILD_VERSION has not been defined. You should build the top-level project instead.") +endif() + + +set(PREBUILD_SRCS + main.cpp + pch.h +) +add_executable(cppwinrt-prebuild ${PREBUILD_SRCS}) +target_compile_definitions(cppwinrt-prebuild PRIVATE CPPWINRT_VERSION_STRING="${CPPWINRT_BUILD_VERSION}") +target_include_directories(cppwinrt-prebuild PRIVATE ../cppwinrt/) + +if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + install(TARGETS cppwinrt-prebuild) +endif() diff --git a/prebuild/main.cpp b/prebuild/main.cpp index d446c02d4..1295f8f69 100644 --- a/prebuild/main.cpp +++ b/prebuild/main.cpp @@ -19,9 +19,11 @@ int main(int const argc, char** argv) strings_h.write(R"( #pragma once namespace cppwinrt::strings { +extern "C++" { )"); strings_cpp.write(R"( +#include "strings.h" namespace cppwinrt::strings { )"); @@ -41,7 +43,7 @@ namespace cppwinrt::strings { strings_h.write(R"(extern char const %[%]; )", name.string(), - static_cast(view.size())); + static_cast(view.size() + 1)); strings_cpp.write(R"(extern char const %[] = R"xyz()xyz" )", @@ -49,7 +51,7 @@ namespace cppwinrt::strings { std::string_view remainder = view; - while (remainder.size()) + while (!remainder.empty()) { auto const size = std::min(size_t{ 16'000 }, remainder.size()); auto const chunk = remainder.substr(0, size); @@ -66,6 +68,7 @@ namespace cppwinrt::strings { strings_h.write(R"( } +} )"); strings_cpp.write(R"( @@ -74,12 +77,19 @@ namespace cppwinrt::strings { writer version_rc; + // Extract major.minor substrings from CPPWINRT_VERSION_STRING (e.g. "3.0.250316.1") + std::string_view const full_version{ CPPWINRT_VERSION_STRING }; + auto const first_dot = full_version.find('.'); + auto const second_dot = full_version.find('.', first_dot + 1); + auto const ver_major = full_version.substr(0, first_dot); + auto const ver_minor = full_version.substr(first_dot + 1, second_dot - first_dot - 1); + version_rc.write(R"( #include "winres.h" VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,0,0,0 - PRODUCTVERSION 2,0,0,0 + FILEVERSION %,%,0,0 + PRODUCTVERSION %,%,0,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -96,7 +106,7 @@ BEGIN BEGIN VALUE "CompanyName", "Microsoft Corporation" VALUE "FileDescription", "C++/WinRT" - VALUE "FileVersion", "2.0.0.0" + VALUE "FileVersion", "%.%.0.0" VALUE "LegalCopyright", "Microsoft Corporation. All rights reserved." VALUE "OriginalFilename", "cppwinrt.exe" VALUE "ProductName", "C++/WinRT" @@ -109,7 +119,10 @@ BEGIN END END )", - CPPWINRT_VERSION_STRING); + ver_major, ver_minor, // FILEVERSION + ver_major, ver_minor, // PRODUCTVERSION + ver_major, ver_minor, // FileVersion string + CPPWINRT_VERSION_STRING); // ProductVersion string std::filesystem::create_directories(argv[2]); auto const output = std::filesystem::canonical(argv[2]); diff --git a/prebuild/prebuild.vcxproj b/prebuild/prebuild.vcxproj index 92df71b65..9fad91fd8 100644 --- a/prebuild/prebuild.vcxproj +++ b/prebuild/prebuild.vcxproj @@ -1,10 +1,6 @@ - - Debug - ARM - Debug ARM64 @@ -13,10 +9,6 @@ Debug Win32 - - Release - ARM - Release ARM64 @@ -38,17 +30,12 @@ 15.0 {FB239623-7D19-4025-BCEA-B43298D4A315} cppwinrt - 10.0 - + Application true - - Application - true - Application true @@ -58,11 +45,6 @@ false true - - Application - false - true - Application false @@ -85,18 +67,12 @@ - - - - - - @@ -107,35 +83,13 @@ - - $(OutDir)temp\$(ProjectName)\ - - - - - $(OutDir)temp\$(ProjectName)\ - - - $(OutDir)temp\$(ProjectName)\ - - - $(OutDir)temp\$(ProjectName)\ - Disabled ..\cppwinrt MultiThreadedDebug - - - Console - - - - - Disabled - ..\cppwinrt - MultiThreadedDebug + Level4 + true Console @@ -146,6 +100,8 @@ Disabled ..\cppwinrt MultiThreadedDebug + Level4 + true Console @@ -156,6 +112,8 @@ Disabled ..\cppwinrt MultiThreadedDebug + Level4 + true Console @@ -168,20 +126,9 @@ true ..\cppwinrt MultiThreaded - - - Console - true - true - - - - - MaxSpeed - true - true - ..\cppwinrt - MultiThreaded + Guard + Level4 + true Console @@ -196,6 +143,9 @@ true ..\cppwinrt MultiThreaded + Guard + Level4 + true Console @@ -210,6 +160,9 @@ true ..\cppwinrt MultiThreaded + Guard + Level4 + true Console diff --git a/prepare_versionless_diffs.cmd b/prepare_versionless_diffs.cmd new file mode 100644 index 000000000..05cb6dd0b --- /dev/null +++ b/prepare_versionless_diffs.cmd @@ -0,0 +1,40 @@ +@echo off + +setlocal ENABLEDELAYEDEXPANSION + +set target_platform=%1 +set target_configuration=%2 +if "%target_platform%"=="" set target_platform=x64 + +if /I "%target_platform%" equ "all" ( + if "%target_configuration%"=="" ( + set target_configuration=all + ) + call %0 x86 !target_configuration! + call %0 x64 !target_configuration! + call %0 arm64 !target_configuration! + goto :eof +) + +if /I "%target_configuration%" equ "all" ( + call %0 %target_platform% Debug + call %0 %target_platform% Release + goto :eof +) + +if "%target_configuration%"=="" ( + set target_configuration=Debug +) + +set reference_output=%~p0\_reference\%target_platform%\%target_configuration% +set build_output=%~p0\_build\%target_platform%\%target_configuration% + +echo Removing version stamps from %reference_output%\winrt +pushd %reference_output%\winrt +powershell -Command "gci -r -include *.h,*.ixx | %%{ (get-content $_) -replace 'was generated by.*|CPPWINRT_VERSION.*','' | set-content $_ }" +popd + +echo Removing version stamps from %build_output%\winrt +pushd %build_output%\winrt +powershell -Command "gci -r -include *.h,*.ixx | %%{ (get-content $_) -replace 'was generated by.*|CPPWINRT_VERSION.*','' | set-content $_ }" +popd diff --git a/run_tests.cmd b/run_tests.cmd index d63741994..58e3c5524 100644 --- a/run_tests.cmd +++ b/run_tests.cmd @@ -9,7 +9,9 @@ if "%target_platform%"=="" set target_platform=x64 if "%target_configuration%"=="" set target_configuration=Debug call :run_test test -call :run_test test_win7 +call :run_test test_nocoro +call :run_test test_cpp20 +call :run_test test_cpp20_no_sourcelocation call :run_test test_fast call :run_test test_slow call :run_test test_old @@ -19,5 +21,12 @@ goto :eof :run_test if not "%target_version%"=="" set args=-o %1-%target_version%.xml -r junit -_build\%target_platform%\%target_configuration%\%1.exe %args% +rem Buffer output and redirect to stdout/stderr depending whether the test executable exits successfully. Pipeline will fail if there's any output to stderr. +_build\%target_platform%\%target_configuration%\%1.exe %args% > %1_results.txt +if %ERRORLEVEL% EQU 0 ( + type %1_results.txt +) else ( + type %1_results.txt >&2 + echo %1 >> test_failures.txt +) goto :eof diff --git a/scratch/scratch.vcxproj b/scratch/scratch.vcxproj index df71cbdac..84f6ce3d8 100644 --- a/scratch/scratch.vcxproj +++ b/scratch/scratch.vcxproj @@ -1,10 +1,7 @@ + - - Debug - ARM - Debug ARM64 @@ -13,10 +10,6 @@ Debug Win32 - - Release - ARM - Release ARM64 @@ -35,292 +28,69 @@ + true + true + true + true 16.0 {E893622C-47DE-4F83-B422-0A26711590A4} scratch scratch - 10.0 - - - - Application - true - - - Application - true - - - Application - true - - Application - false - true - - - Application - false - true - - - Application - false - true + + ..\_build\$(Platform)\$(Configuration) + false - - Application + + true - - Application + false true - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - $(OutDir)temp\$(ProjectName)\ - - - - - $(OutDir)temp\$(ProjectName)\ - - - $(OutDir)temp\$(ProjectName)\ - - - $(OutDir)temp\$(ProjectName)\ - - - - MaxSpeed - true - true - $(OutputPath);Generated Files;..\..\..\library - NOMINMAX;_MBCS;%(PreprocessorDefinitions) - /await %(AdditionalOptions) - MultiThreaded - - - Console - true - true - - - - - - - - - - - - - Disabled - $(OutputPath);Generated Files;..\..\..\library - _MBCS;%(PreprocessorDefinitions) - /await %(AdditionalOptions) - MultiThreadedDebug - - - Console - - - - - - - - - - - - - Disabled - $(OutputPath);Generated Files;..\..\..\library - _MBCS;%(PreprocessorDefinitions) - /await %(AdditionalOptions) - MultiThreadedDebug - - - Console - - - - - - - - - - - - - Disabled - $(OutputPath);Generated Files;..\..\..\library - _MBCS;%(PreprocessorDefinitions) - /await %(AdditionalOptions) - MultiThreadedDebug - - - Console - - - - - - - - - - - - - Disabled - $(OutputPath);Generated Files;..\..\..\library - _MBCS;%(PreprocessorDefinitions) - /await %(AdditionalOptions) - MultiThreadedDebug - - - Console - - - - - - - - - - - + - MaxSpeed - true - true - $(OutputPath);Generated Files;..\..\..\library - NOMINMAX;_MBCS;%(PreprocessorDefinitions) - /await %(AdditionalOptions) - MultiThreaded + $(OutputPath);Generated Files; + Level4 + true Console - true - true - - - - - - - - - + MaxSpeed true true - $(OutputPath);Generated Files;..\..\..\library NOMINMAX;_MBCS;%(PreprocessorDefinitions) - /await %(AdditionalOptions) MultiThreaded - Console true true - - - - - - - - - + - MaxSpeed - true - true - $(OutputPath);Generated Files;..\..\..\library - NOMINMAX;_MBCS;%(PreprocessorDefinitions) - /await %(AdditionalOptions) - MultiThreaded + Disabled + _MBCS;%(PreprocessorDefinitions) + MultiThreadedDebug - - Console - true - true - - - - - - - - - - NotUsing - Use - Use - Use - Use - Use - Use - Use - Use + Use - Create - Create - Create - Create - Create - Create - Create - Create + Create + + + diff --git a/strings/base_abi.h b/strings/base_abi.h index 1ad5e827e..4b7b8f77f 100644 --- a/strings/base_abi.h +++ b/strings/base_abi.h @@ -1,13 +1,13 @@ -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { template <> struct abi { - struct __declspec(novtable) type + struct WINRT_IMPL_ABI_DECL type { - virtual int32_t __stdcall QueryInterface(guid const& id, void** object) noexcept = 0; - virtual uint32_t __stdcall AddRef() noexcept = 0; - virtual uint32_t __stdcall Release() noexcept = 0; + virtual std::int32_t __stdcall QueryInterface(guid const& id, void** object) noexcept = 0; + virtual std::uint32_t __stdcall AddRef() noexcept = 0; + virtual std::uint32_t __stdcall Release() noexcept = 0; }; }; @@ -15,11 +15,11 @@ namespace winrt::impl template <> struct abi { - struct __declspec(novtable) type : unknown_abi + struct WINRT_IMPL_ABI_DECL type : unknown_abi { - virtual int32_t __stdcall GetIids(uint32_t* count, guid** ids) noexcept = 0; - virtual int32_t __stdcall GetRuntimeClassName(void** name) noexcept = 0; - virtual int32_t __stdcall GetTrustLevel(Windows::Foundation::TrustLevel* level) noexcept = 0; + virtual std::int32_t __stdcall GetIids(std::uint32_t* count, guid** ids) noexcept = 0; + virtual std::int32_t __stdcall GetRuntimeClassName(void** name) noexcept = 0; + virtual std::int32_t __stdcall GetTrustLevel(Windows::Foundation::TrustLevel* level) noexcept = 0; }; }; @@ -27,114 +27,119 @@ namespace winrt::impl template <> struct abi { - struct __declspec(novtable) type : inspectable_abi + struct WINRT_IMPL_ABI_DECL type : inspectable_abi { - virtual int32_t __stdcall ActivateInstance(void** instance) noexcept = 0; + virtual std::int32_t __stdcall ActivateInstance(void** instance) noexcept = 0; }; }; - struct __declspec(novtable) IAgileObject : unknown_abi {}; + struct WINRT_IMPL_ABI_DECL IAgileObject : unknown_abi {}; - struct __declspec(novtable) IAgileReference : unknown_abi + struct WINRT_IMPL_ABI_DECL IAgileReference : unknown_abi { - virtual int32_t __stdcall Resolve(guid const& id, void** object) noexcept = 0; + virtual std::int32_t __stdcall Resolve(guid const& id, void** object) noexcept = 0; }; - struct __declspec(novtable) IMarshal : unknown_abi + struct WINRT_IMPL_ABI_DECL IMarshal : unknown_abi { - virtual int32_t __stdcall GetUnmarshalClass(guid const& riid, void* pv, uint32_t dwDestContext, void* pvDestContext, uint32_t mshlflags, guid* pCid) noexcept = 0; - virtual int32_t __stdcall GetMarshalSizeMax(guid const& riid, void* pv, uint32_t dwDestContext, void* pvDestContext, uint32_t mshlflags, uint32_t* pSize) noexcept = 0; - virtual int32_t __stdcall MarshalInterface(void* pStm, guid const& riid, void* pv, uint32_t dwDestContext, void* pvDestContext, uint32_t mshlflags) noexcept = 0; - virtual int32_t __stdcall UnmarshalInterface(void* pStm, guid const& riid, void** ppv) noexcept = 0; - virtual int32_t __stdcall ReleaseMarshalData(void* pStm) noexcept = 0; - virtual int32_t __stdcall DisconnectObject(uint32_t dwReserved) noexcept = 0; + virtual std::int32_t __stdcall GetUnmarshalClass(guid const& riid, void* pv, std::uint32_t dwDestContext, void* pvDestContext, std::uint32_t mshlflags, guid* pCid) noexcept = 0; + virtual std::int32_t __stdcall GetMarshalSizeMax(guid const& riid, void* pv, std::uint32_t dwDestContext, void* pvDestContext, std::uint32_t mshlflags, std::uint32_t* pSize) noexcept = 0; + virtual std::int32_t __stdcall MarshalInterface(void* pStm, guid const& riid, void* pv, std::uint32_t dwDestContext, void* pvDestContext, std::uint32_t mshlflags) noexcept = 0; + virtual std::int32_t __stdcall UnmarshalInterface(void* pStm, guid const& riid, void** ppv) noexcept = 0; + virtual std::int32_t __stdcall ReleaseMarshalData(void* pStm) noexcept = 0; + virtual std::int32_t __stdcall DisconnectObject(std::uint32_t dwReserved) noexcept = 0; }; - struct __declspec(novtable) IGlobalInterfaceTable : unknown_abi + struct WINRT_IMPL_ABI_DECL IGlobalInterfaceTable : unknown_abi { - virtual int32_t __stdcall RegisterInterfaceInGlobal(void* object, guid const& iid, uint32_t* cookie) noexcept = 0; - virtual int32_t __stdcall RevokeInterfaceFromGlobal(uint32_t cookie) noexcept = 0; - virtual int32_t __stdcall GetInterfaceFromGlobal(uint32_t cookie, guid const& iid, void** object) noexcept = 0; + virtual std::int32_t __stdcall RegisterInterfaceInGlobal(void* object, guid const& iid, std::uint32_t* cookie) noexcept = 0; + virtual std::int32_t __stdcall RevokeInterfaceFromGlobal(std::uint32_t cookie) noexcept = 0; + virtual std::int32_t __stdcall GetInterfaceFromGlobal(std::uint32_t cookie, guid const& iid, void** object) noexcept = 0; }; - struct __declspec(novtable) IStaticLifetime : inspectable_abi + struct WINRT_IMPL_ABI_DECL IStaticLifetime : inspectable_abi { - virtual int32_t __stdcall unused() noexcept = 0; - virtual int32_t __stdcall GetCollection(void** value) noexcept = 0; + virtual std::int32_t __stdcall unused() noexcept = 0; + virtual std::int32_t __stdcall GetCollection(void** value) noexcept = 0; }; - struct __declspec(novtable) IStaticLifetimeCollection : inspectable_abi + struct WINRT_IMPL_ABI_DECL IStaticLifetimeCollection : inspectable_abi { - virtual int32_t __stdcall Lookup(void*, void**) noexcept = 0; - virtual int32_t __stdcall unused() noexcept = 0; - virtual int32_t __stdcall unused2() noexcept = 0; - virtual int32_t __stdcall unused3() noexcept = 0; - virtual int32_t __stdcall Insert(void*, void*, bool*) noexcept = 0; - virtual int32_t __stdcall unused4() noexcept = 0; - virtual int32_t __stdcall unused5() noexcept = 0; + virtual std::int32_t __stdcall Lookup(void*, void**) noexcept = 0; + virtual std::int32_t __stdcall unused() noexcept = 0; + virtual std::int32_t __stdcall unused2() noexcept = 0; + virtual std::int32_t __stdcall unused3() noexcept = 0; + virtual std::int32_t __stdcall Insert(void*, void*, bool*) noexcept = 0; + virtual std::int32_t __stdcall Remove(void*) noexcept = 0; + virtual std::int32_t __stdcall unused4() noexcept = 0; }; - struct __declspec(novtable) IWeakReference : unknown_abi + struct WINRT_IMPL_ABI_DECL IWeakReference : unknown_abi { - virtual int32_t __stdcall Resolve(guid const& iid, void** objectReference) noexcept = 0; + virtual std::int32_t __stdcall Resolve(guid const& iid, void** objectReference) noexcept = 0; }; - struct __declspec(novtable) IWeakReferenceSource : unknown_abi + struct WINRT_IMPL_ABI_DECL IWeakReferenceSource : unknown_abi { - virtual int32_t __stdcall GetWeakReference(IWeakReference** weakReference) noexcept = 0; + virtual std::int32_t __stdcall GetWeakReference(IWeakReference** weakReference) noexcept = 0; }; - struct __declspec(novtable) IRestrictedErrorInfo : unknown_abi + struct WINRT_IMPL_ABI_DECL IRestrictedErrorInfo : unknown_abi { - virtual int32_t __stdcall GetErrorDetails(bstr* description, int32_t* error, bstr* restrictedDescription, bstr* capabilitySid) noexcept = 0; - virtual int32_t __stdcall GetReference(bstr* reference) noexcept = 0; + virtual std::int32_t __stdcall GetErrorDetails(bstr* description, std::int32_t* error, bstr* restrictedDescription, bstr* capabilitySid) noexcept = 0; + virtual std::int32_t __stdcall GetReference(bstr* reference) noexcept = 0; }; - struct __declspec(novtable) IErrorInfo : unknown_abi + struct WINRT_IMPL_ABI_DECL IErrorInfo : unknown_abi { - virtual int32_t __stdcall GetGUID(guid* value) noexcept = 0; - virtual int32_t __stdcall GetSource(bstr* value) noexcept = 0; - virtual int32_t __stdcall GetDescription(bstr* value) noexcept = 0; - virtual int32_t __stdcall GetHelpFile(bstr* value) noexcept = 0; - virtual int32_t __stdcall GetHelpContext(uint32_t* value) noexcept = 0; + virtual std::int32_t __stdcall GetGUID(guid* value) noexcept = 0; + virtual std::int32_t __stdcall GetSource(bstr* value) noexcept = 0; + virtual std::int32_t __stdcall GetDescription(bstr* value) noexcept = 0; + virtual std::int32_t __stdcall GetHelpFile(bstr* value) noexcept = 0; + virtual std::int32_t __stdcall GetHelpContext(std::uint32_t* value) noexcept = 0; }; - struct __declspec(novtable) ILanguageExceptionErrorInfo2 : unknown_abi + struct WINRT_IMPL_ABI_DECL ILanguageExceptionErrorInfo2 : unknown_abi { - virtual int32_t __stdcall GetLanguageException(void** exception) noexcept = 0; - virtual int32_t __stdcall GetPreviousLanguageExceptionErrorInfo(ILanguageExceptionErrorInfo2** previous) noexcept = 0; - virtual int32_t __stdcall CapturePropagationContext(void* exception) noexcept = 0; - virtual int32_t __stdcall GetPropagationContextHead(ILanguageExceptionErrorInfo2** head) noexcept = 0; + virtual std::int32_t __stdcall GetLanguageException(void** exception) noexcept = 0; + virtual std::int32_t __stdcall GetPreviousLanguageExceptionErrorInfo(ILanguageExceptionErrorInfo2** previous) noexcept = 0; + virtual std::int32_t __stdcall CapturePropagationContext(void* exception) noexcept = 0; + virtual std::int32_t __stdcall GetPropagationContextHead(ILanguageExceptionErrorInfo2** head) noexcept = 0; }; struct ICallbackWithNoReentrancyToApplicationSTA; - struct __declspec(novtable) IContextCallback : unknown_abi + struct WINRT_IMPL_ABI_DECL IContextCallback : unknown_abi { - virtual int32_t __stdcall ContextCallback(int32_t(__stdcall* callback)(com_callback_args*), com_callback_args* args, guid const& iid, int method, void* reserved) noexcept = 0; + virtual std::int32_t __stdcall ContextCallback(std::int32_t(__stdcall* callback)(com_callback_args*), com_callback_args* args, guid const& iid, int method, void* reserved) noexcept = 0; }; - struct __declspec(novtable) IServerSecurity : unknown_abi + struct WINRT_IMPL_ABI_DECL IServerSecurity : unknown_abi { - virtual int32_t __stdcall QueryBlanket(uint32_t*, uint32_t*, wchar_t**, uint32_t*, uint32_t*, void**, uint32_t*) noexcept = 0; - virtual int32_t __stdcall ImpersonateClient() noexcept = 0; - virtual int32_t __stdcall RevertToSelf() noexcept = 0; - virtual int32_t __stdcall IsImpersonating() noexcept = 0; + virtual std::int32_t __stdcall QueryBlanket(std::uint32_t*, std::uint32_t*, wchar_t**, std::uint32_t*, std::uint32_t*, void**, std::uint32_t*) noexcept = 0; + virtual std::int32_t __stdcall ImpersonateClient() noexcept = 0; + virtual std::int32_t __stdcall RevertToSelf() noexcept = 0; + virtual std::int32_t __stdcall IsImpersonating() noexcept = 0; }; - struct __declspec(novtable) IBufferByteAccess : unknown_abi + struct WINRT_IMPL_ABI_DECL IBufferByteAccess : unknown_abi { - virtual int32_t __stdcall Buffer(uint8_t** value) noexcept = 0; + virtual std::int32_t __stdcall Buffer(std::uint8_t** value) noexcept = 0; + }; + + struct WINRT_IMPL_ABI_DECL IMemoryBufferByteAccess : unknown_abi + { + virtual std::int32_t __stdcall GetBuffer(std::uint8_t** value, std::uint32_t* capacity) noexcept = 0; }; template <> struct abi { - using type = int64_t; + using type = std::int64_t; }; template <> struct abi { - using type = int64_t; + using type = std::int64_t; }; template <> inline constexpr guid guid_v{ 0x00000000, 0x0000, 0x0000, { 0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46 } }; @@ -155,4 +160,5 @@ namespace winrt::impl template <> inline constexpr guid guid_v{ 0x000001da, 0x0000, 0x0000, { 0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46 } }; template <> inline constexpr guid guid_v{ 0x0000013E, 0x0000, 0x0000, { 0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46 } }; template <> inline constexpr guid guid_v{ 0x905a0fef, 0xbc53, 0x11df, { 0x8c,0x49,0x00,0x1e,0x4f,0xc6,0x86,0xda } }; + template <> inline constexpr guid guid_v{ 0x5b0d3235, 0x4dba, 0x4d44, { 0x86,0x5e,0x8f,0x1d,0x0e,0x4f,0xd0,0x4d } }; } diff --git a/strings/base_activation.h b/strings/base_activation.h index 7f35e1a79..c657c692d 100644 --- a/strings/base_activation.h +++ b/strings/base_activation.h @@ -1,5 +1,5 @@ -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { struct library_traits { @@ -18,27 +18,19 @@ namespace winrt::impl using library_handle = handle_type; - inline int32_t __stdcall fallback_RoGetActivationFactory(void*, guid const&, void** factory) noexcept - { - *factory = nullptr; - return error_class_not_available; - } - - template - hresult get_runtime_activation_factory(param::hstring const& name, void** result) noexcept + template + WINRT_IMPL_NOINLINE hresult get_runtime_activation_factory_impl(param::hstring const& name, winrt::guid const& guid, void** result) noexcept { if (winrt_activation_handler) { - return winrt_activation_handler(*(void**)(&name), guid_of(), result); + return winrt_activation_handler(*(void**)(&name), guid, result); } - static int32_t(__stdcall * handler)(void* classId, guid const& iid, void** factory) noexcept; - impl::load_runtime_function("RoGetActivationFactory", handler, fallback_RoGetActivationFactory); - hresult hr = handler(*(void**)(&name), guid_of(), result); + hresult hr = WINRT_IMPL_RoGetActivationFactory(*(void**)(&name), guid, result); if (hr == impl::error_not_initialized) { - auto usage = reinterpret_cast(WINRT_IMPL_GetProcAddress(WINRT_IMPL_LoadLibraryW(L"combase.dll"), "CoIncrementMTAUsage")); + auto usage = reinterpret_cast(WINRT_IMPL_GetProcAddress(load_library(L"combase.dll"), "CoIncrementMTAUsage")); if (!usage) { @@ -47,7 +39,7 @@ namespace winrt::impl void* cookie; usage(&cookie); - hr = handler(*(void**)(&name), guid_of(), result); + hr = WINRT_IMPL_RoGetActivationFactory(*(void**)(&name), guid, result); } if (hr == 0) @@ -65,7 +57,7 @@ namespace winrt::impl { path.resize(count); path += L".dll"; - library_handle library(WINRT_IMPL_LoadLibraryW(path.c_str())); + library_handle library(load_library(path.c_str())); path.resize(path.size() - 4); if (!library) @@ -73,7 +65,7 @@ namespace winrt::impl continue; } - auto library_call = reinterpret_cast(WINRT_IMPL_GetProcAddress(library.get(), "DllGetActivationFactory")); + auto library_call = reinterpret_cast(WINRT_IMPL_GetProcAddress(library.get(), "DllGetActivationFactory")); if (!library_call) { @@ -87,13 +79,13 @@ namespace winrt::impl continue; } - if constexpr (std::is_same_v< Interface, Windows::Foundation::IActivationFactory>) + if constexpr (isSameInterfaceAsIActivationFactory) { *result = library_factory.detach(); library.detach(); return 0; } - else if (0 == library_factory.as(guid_of(), result)) + else if (0 == library_factory.as(guid, result)) { library.detach(); return 0; @@ -103,6 +95,12 @@ namespace winrt::impl WINRT_IMPL_SetErrorInfo(0, error_info.get()); return hr; } + + template + hresult get_runtime_activation_factory(param::hstring const& name, void** result) noexcept + { + return get_runtime_activation_factory_impl>(name, guid_of(), result); + } } WINRT_EXPORT namespace winrt @@ -121,22 +119,26 @@ WINRT_EXPORT namespace winrt #pragma clang diagnostic ignored "-Wdeprecated-declarations" #endif -#if defined _M_ARM -#define WINRT_IMPL_INTERLOCKED_READ_MEMORY_BARRIER (__dmb(_ARM_BARRIER_ISH)); +#if defined(__GNUC__) && defined(__aarch64__) +#define WINRT_IMPL_INTERLOCKED_READ_MEMORY_BARRIER __asm__ __volatile__ ("dmb ish"); #elif defined _M_ARM64 #define WINRT_IMPL_INTERLOCKED_READ_MEMORY_BARRIER (__dmb(_ARM64_BARRIER_ISH)); #endif -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { - inline int32_t interlocked_read_32(int32_t const volatile* target) noexcept + inline std::int32_t interlocked_read_32(std::int32_t const volatile* target) noexcept { #if defined _M_IX86 || defined _M_X64 - int32_t const result = *target; + std::int32_t const result = *target; _ReadWriteBarrier(); return result; -#elif defined _M_ARM || defined _M_ARM64 - int32_t const result = __iso_volatile_load32(reinterpret_cast(target)); +#elif defined _M_ARM64 +#if defined(__GNUC__) + std::int32_t const result = *target; +#else + std::int32_t const result = __iso_volatile_load32(reinterpret_cast(target)); +#endif WINRT_IMPL_INTERLOCKED_READ_MEMORY_BARRIER return result; #else @@ -145,14 +147,18 @@ namespace winrt::impl } #if defined _WIN64 - inline int64_t interlocked_read_64(int64_t const volatile* target) noexcept + inline std::int64_t interlocked_read_64(std::int64_t const volatile* target) noexcept { #if defined _M_X64 - int64_t const result = *target; + std::int64_t const result = *target; _ReadWriteBarrier(); return result; #elif defined _M_ARM64 - int64_t const result = __iso_volatile_load64(target); +#if defined(__GNUC__) + std::int64_t const result = *target; +#else + std::int64_t const result = __iso_volatile_load64(target); +#endif WINRT_IMPL_INTERLOCKED_READ_MEMORY_BARRIER return result; #else @@ -171,16 +177,18 @@ namespace winrt::impl T* interlocked_read_pointer(T* const volatile* target) noexcept { #ifdef _WIN64 - return (T*)interlocked_read_64((int64_t*)target); + return (T*)interlocked_read_64((std::int64_t*)target); #else - return (T*)interlocked_read_32((int32_t*)target); + return (T*)interlocked_read_32((std::int32_t*)target); #endif } #ifdef _WIN64 - inline constexpr uint32_t memory_allocation_alignment{ 16 }; + inline constexpr std::uint32_t memory_allocation_alignment{ 16 }; +#ifdef _MSC_VER #pragma warning(push) #pragma warning(disable:4324) // structure was padded due to alignment specifier +#endif struct alignas(16) slist_entry { slist_entry* next; @@ -189,32 +197,34 @@ namespace winrt::impl { struct { - uint64_t reserved1; - uint64_t reserved2; + std::uint64_t reserved1; + std::uint64_t reserved2; } reserved1; struct { - uint64_t reserved1 : 16; - uint64_t reserved2 : 48; - uint64_t reserved3 : 4; - uint64_t reserved4 : 60; + std::uint64_t reserved1 : 16; + std::uint64_t reserved2 : 48; + std::uint64_t reserved3 : 4; + std::uint64_t reserved4 : 60; } reserved2; }; +#ifdef _MSC_VER #pragma warning(pop) +#endif #else - inline constexpr uint32_t memory_allocation_alignment{ 8 }; + inline constexpr std::uint32_t memory_allocation_alignment{ 8 }; struct slist_entry { slist_entry* next; }; union slist_header { - uint64_t reserved1; + std::uint64_t reserved1; struct { slist_entry reserved1; - uint16_t reserved2; - uint16_t reserved3; + std::uint16_t reserved2; + std::uint16_t reserved3; } reserved2; }; #endif @@ -224,27 +234,30 @@ namespace winrt::impl factory_count_guard(factory_count_guard const&) = delete; factory_count_guard& operator=(factory_count_guard const&) = delete; - explicit factory_count_guard(size_t& count) noexcept : m_count(count) + explicit factory_count_guard(std::size_t& count) noexcept : m_count(count) { +#ifndef WINRT_NO_MODULE_LOCK #ifdef _WIN64 - _InterlockedIncrement64((int64_t*)&m_count); + _InterlockedIncrement64((std::int64_t*)&m_count); #else _InterlockedIncrement((long*)&m_count); +#endif #endif } ~factory_count_guard() noexcept { +#ifndef WINRT_NO_MODULE_LOCK #ifdef _WIN64 - _InterlockedDecrement64((int64_t*)&m_count); + _InterlockedDecrement64((std::int64_t*)&m_count); #else _InterlockedDecrement((long*)&m_count); +#endif #endif } private: - - size_t& m_count; + [[maybe_unused]] std::size_t& m_count; // Field is unused when WINRT_NO_MODULE_LOCK is defined. }; struct factory_cache_entry_base @@ -252,7 +265,7 @@ namespace winrt::impl struct alignas(sizeof(void*) * 2) object_and_count { unknown_abi* object; - size_t count; + std::size_t count; }; object_and_count m_value; @@ -270,14 +283,19 @@ namespace winrt::impl object_and_count current_value{ pointer_value, 0 }; #if defined _WIN64 - if (1 == _InterlockedCompareExchange128((int64_t*)this, 0, 0, (int64_t*)¤t_value)) +#if defined(__GNUC__) + bool exchanged = __sync_bool_compare_and_swap((__int128*)this, *(__int128*)¤t_value, (__int128)0); +#else + bool exchanged = 1 == _InterlockedCompareExchange128((std::int64_t*)this, 0, 0, (std::int64_t*)¤t_value); +#endif + if (exchanged) { pointer_value->Release(); } #else - int64_t const result = _InterlockedCompareExchange64((int64_t*)this, 0, *(int64_t*)¤t_value); + std::int64_t const result = _InterlockedCompareExchange64((std::int64_t*)this, 0, *(std::int64_t*)¤t_value); - if (result == *(int64_t*)¤t_value) + if (result == *(std::int64_t*)¤t_value) { pointer_value->Release(); } @@ -287,7 +305,7 @@ namespace winrt::impl static_assert(std::is_standard_layout_v); -#if !defined _M_IX86 && !defined _M_X64 && !defined _M_ARM && !defined _M_ARM64 +#if !defined _M_IX86 && !defined _M_X64 && !defined _M_ARM64 #error Unsupported architecture: verify that zero-initialization of SLIST_HEADER is still safe #endif @@ -312,7 +330,7 @@ namespace winrt::impl // entry->next must be read before entry->clear() is called since the InterlockedCompareExchange // inside clear() will allow another thread to add the entry back to the cache. slist_entry* next = entry->next; - reinterpret_cast(reinterpret_cast(entry) - offsetof(factory_cache_entry_base, m_next))->clear(); + reinterpret_cast(reinterpret_cast(entry) - offsetof(factory_cache_entry_base, m_next))->clear(); entry = next; } } @@ -332,7 +350,7 @@ namespace winrt::impl struct factory_cache_entry : factory_cache_entry_base { template - __declspec(noinline) auto call(F&& callback) + WINRT_IMPL_NOINLINE auto call(F&& callback) { #ifdef WINRT_DIAGNOSTICS get_diagnostics_info().add_factory(); @@ -355,7 +373,9 @@ namespace winrt::impl if (nullptr == _InterlockedCompareExchangePointer(reinterpret_cast(&m_value.object), *reinterpret_cast(&object), nullptr)) { *reinterpret_cast(&object) = nullptr; +#ifndef WINRT_NO_MODULE_LOCK get_factory_cache().add(this); +#endif } return callback(*reinterpret_cast const*>(&m_value.object)); @@ -400,10 +420,9 @@ namespace winrt::impl return factory.call(static_cast(callback)); } - template - com_ref try_get_activation_factory(hresult_error* exception = nullptr) noexcept + template + com_ref try_get_activation_factory(param::hstring const& name, hresult_error* exception = nullptr) noexcept { - param::hstring const name{ name_of() }; void* result{}; hresult const hr = get_runtime_activation_factory(name, &result); @@ -424,7 +443,7 @@ namespace winrt::impl template struct produce : produce_base { - int32_t __stdcall ActivateInstance(void** instance) noexcept final try + std::int32_t __stdcall ActivateInstance(void** instance) noexcept final try { *instance = nullptr; typename D::abi_guard guard(this->shim()); @@ -437,7 +456,7 @@ namespace winrt::impl WINRT_EXPORT namespace winrt { - enum class apartment_type : int32_t + enum class apartment_type : std::int32_t { multi_threaded = 0, single_threaded = 2, @@ -445,7 +464,7 @@ WINRT_EXPORT namespace winrt inline void init_apartment(apartment_type const type = apartment_type::multi_threaded) { - hresult const result = WINRT_IMPL_CoInitializeEx(nullptr, static_cast(type)); + hresult const result = WINRT_IMPL_CoInitializeEx(nullptr, static_cast(type)); if (result < 0) { @@ -463,7 +482,7 @@ WINRT_EXPORT namespace winrt { // Normally, the callback avoids having to return a ref-counted object and the resulting AddRef/Release bump. // In this case we do want a unique reference, so we use the lambda to return one and thus produce an - // AddRef'd object that is returned to the caller. + // AddRef'd object that is returned to the caller. return impl::call_factory([](auto&& factory) { return factory; @@ -473,13 +492,25 @@ WINRT_EXPORT namespace winrt template auto try_get_activation_factory() noexcept { - return impl::try_get_activation_factory(); + return impl::try_get_activation_factory(name_of()); } template auto try_get_activation_factory(hresult_error& exception) noexcept { - return impl::try_get_activation_factory(&exception); + return impl::try_get_activation_factory(name_of(), &exception); + } + + template + auto try_get_activation_factory(param::hstring const& name) noexcept + { + return impl::try_get_activation_factory(name); + } + + template + auto try_get_activation_factory(param::hstring const& name, hresult_error& exception) noexcept + { + return impl::try_get_activation_factory(name, &exception); } inline void clear_factory_cache() noexcept @@ -488,7 +519,13 @@ WINRT_EXPORT namespace winrt } template - auto create_instance(guid const& clsid, uint32_t context = 0x1 /*CLSCTX_INPROC_SERVER*/, void* outer = nullptr) + auto try_create_instance(guid const& clsid, std::uint32_t context = 0x1 /*CLSCTX_INPROC_SERVER*/, void* outer = nullptr) + { + return try_capture(WINRT_IMPL_CoCreateInstance, clsid, outer, context); + } + + template + auto create_instance(guid const& clsid, std::uint32_t context = 0x1 /*CLSCTX_INPROC_SERVER*/, void* outer = nullptr) { return capture(WINRT_IMPL_CoCreateInstance, clsid, outer, context); } @@ -511,7 +548,7 @@ WINRT_EXPORT namespace winrt } } -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { template T fast_activate(Windows::Foundation::IActivationFactory const& factory) diff --git a/strings/base_agile_ref.h b/strings/base_agile_ref.h index 2e7c8dcbe..14447706a 100644 --- a/strings/base_agile_ref.h +++ b/strings/base_agile_ref.h @@ -3,22 +3,27 @@ WINRT_EXPORT namespace winrt { #if defined (WINRT_NO_MODULE_LOCK) - // Defining WINRT_NO_MODULE_LOCK is appropriate for apps (executables) that don't implement something like DllCanUnloadNow + // Defining WINRT_NO_MODULE_LOCK is appropriate for apps (executables) or pinned DLLs (that don't support unloading) // and can thus avoid the synchronization overhead imposed by the default module lock. constexpr auto get_module_lock() noexcept { struct lock { - constexpr uint32_t operator++() noexcept + constexpr std::uint32_t operator++() noexcept { return 1; } - constexpr uint32_t operator--() noexcept + constexpr std::uint32_t operator--() noexcept { return 0; } + + constexpr explicit operator bool() noexcept + { + return true; + } }; return lock{}; @@ -42,7 +47,7 @@ WINRT_EXPORT namespace winrt #endif } -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { template struct module_lock_updater; @@ -66,109 +71,14 @@ namespace winrt::impl using update_module_lock = module_lock_updater; - struct agile_ref_fallback final : IAgileReference, update_module_lock - { - agile_ref_fallback(com_ptr&& git, uint32_t cookie) noexcept : - m_git(std::move(git)), - m_cookie(cookie) - { - } - - ~agile_ref_fallback() noexcept - { - m_git->RevokeInterfaceFromGlobal(m_cookie); - } - - int32_t __stdcall QueryInterface(guid const& id, void** object) noexcept final - { - if (is_guid_of(id) || is_guid_of(id) || is_guid_of(id)) - { - *object = static_cast(this); - AddRef(); - return 0; - } - - *object = nullptr; - return error_no_interface; - } - - uint32_t __stdcall AddRef() noexcept final - { - return ++m_references; - } - - uint32_t __stdcall Release() noexcept final - { - auto const remaining = --m_references; - - if (remaining == 0) - { - delete this; - } - - return remaining; - } - - int32_t __stdcall Resolve(guid const& id, void** object) noexcept final - { - return m_git->GetInterfaceFromGlobal(m_cookie, id, object); - } - - private: - - com_ptr m_git; - uint32_t m_cookie{}; - atomic_ref_count m_references{ 1 }; - }; - - template - void load_runtime_function(char const* name, F& result, L fallback) noexcept + inline void* load_library(wchar_t const* library) noexcept { - if (result) - { - return; - } - - result = reinterpret_cast(WINRT_IMPL_GetProcAddress(WINRT_IMPL_LoadLibraryW(L"combase.dll"), name)); - - if (result) - { - return; - } - - result = fallback; - } - - inline int32_t __stdcall fallback_RoGetAgileReference(uint32_t, winrt::guid const& iid, void* object, void** reference) noexcept - { - *reference = nullptr; - static constexpr guid git_clsid{ 0x00000323, 0x0000, 0x0000, { 0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46 } }; - - com_ptr git; - hresult hr = WINRT_IMPL_CoCreateInstance(git_clsid, nullptr, 1 /*CLSCTX_INPROC_SERVER*/, guid_of(), git.put_void()); - - if (hr < 0) - { - return hr; - } - - uint32_t cookie{}; - hr = git->RegisterInterfaceInGlobal(object, iid, &cookie); - - if (hr < 0) - { - return hr; - } - - *reference = new agile_ref_fallback(std::move(git), cookie); - return 0; + return WINRT_IMPL_LoadLibraryExW(library, nullptr, 0x00001000 /* LOAD_LIBRARY_SEARCH_DEFAULT_DIRS */); } inline hresult get_agile_reference(winrt::guid const& iid, void* object, void** reference) noexcept { - static int32_t(__stdcall * handler)(uint32_t options, winrt::guid const& iid, void* object, void** reference) noexcept; - load_runtime_function("RoGetAgileReference", handler, fallback_RoGetAgileReference); - return handler(0, iid, object, reference); + return WINRT_IMPL_RoGetAgileReference(0, iid, object, reference); } } @@ -187,7 +97,7 @@ WINRT_EXPORT namespace winrt } } - impl::com_ref get() const noexcept + [[nodiscard]] impl::com_ref get() const noexcept { if (!m_ref) { @@ -209,8 +119,10 @@ WINRT_EXPORT namespace winrt com_ptr m_ref; }; + template agile_ref(T const&)->agile_ref>; + template - agile_ref make_agile(T const& object) + agile_ref> make_agile(T const& object) { return object; } diff --git a/strings/base_array.h b/strings/base_array.h index eb3fa540b..d29bee883 100644 --- a/strings/base_array.h +++ b/strings/base_array.h @@ -5,7 +5,7 @@ WINRT_EXPORT namespace winrt struct array_view { using value_type = T; - using size_type = uint32_t; + using size_type = std::uint32_t; using reference = value_type&; using const_reference = value_type const&; using pointer = value_type*; @@ -17,6 +17,11 @@ WINRT_EXPORT namespace winrt array_view() noexcept = default; + array_view(pointer data, size_type size) noexcept : + m_data(data), + m_size(size) + {} + array_view(pointer first, pointer last) noexcept : m_data(first), m_size(static_cast(last - first)) @@ -26,6 +31,20 @@ WINRT_EXPORT namespace winrt array_view(value.begin(), static_cast(value.size())) {} +#ifdef __cpp_lib_span + template + array_view(std::span span) noexcept : + array_view(span.data(), static_cast(span.size())) + { + WINRT_ASSERT(span.size() <= (std::numeric_limits::max)()); + } + + operator std::span() const noexcept + { + return { m_data, m_size }; + } +#endif + template array_view(C(&value)[N]) noexcept : array_view(value, N) @@ -43,12 +62,12 @@ WINRT_EXPORT namespace winrt { } - template + template array_view(std::array& value) noexcept : array_view(value.data(), static_cast(value.size())) {} - template + template array_view(std::array const& value) noexcept : array_view(value.data(), static_cast(value.size())) {} @@ -192,11 +211,6 @@ WINRT_EXPORT namespace winrt protected: - array_view(pointer data, size_type size) noexcept : - m_data(data), - m_size(size) - {} - pointer m_data{ nullptr }; size_type m_size{ 0 }; @@ -217,11 +231,16 @@ WINRT_EXPORT namespace winrt } }; - template array_view(C(&value)[N]) -> array_view; + template array_view(C(&value)[N]) -> array_view; template array_view(std::vector& value) -> array_view; template array_view(std::vector const& value) -> array_view; - template array_view(std::array& value) -> array_view; - template array_view(std::array const& value) -> array_view; + template array_view(std::array& value) -> array_view; + template array_view(std::array const& value) -> array_view; + +#ifdef __cpp_lib_span + template array_view(std::span& value) -> array_view; + template array_view(std::span const& value) -> array_view; +#endif template struct com_array : array_view @@ -246,7 +265,7 @@ WINRT_EXPORT namespace winrt com_array(count, value_type()) {} - com_array(void* ptr, uint32_t const count, take_ownership_from_abi_t) noexcept : + com_array(void* ptr, std::uint32_t const count, take_ownership_from_abi_t) noexcept : array_view(static_cast(ptr), static_cast(ptr) + count) { } @@ -269,12 +288,21 @@ WINRT_EXPORT namespace winrt com_array(value.begin(), value.end()) {} - template + template explicit com_array(std::array const& value) : com_array(value.begin(), value.end()) {} - template +#ifdef __cpp_lib_span + template + explicit com_array(std::span span) noexcept : + com_array(span.data(), span.data() + span.size()) + { + WINRT_ASSERT(span.size() <= (std::numeric_limits::max)()); + } +#endif + + template explicit com_array(U const(&value)[N]) : com_array(value, value + N) {} @@ -345,16 +373,41 @@ WINRT_EXPORT namespace winrt this->m_size = size; } } + + std::pair> detach_abi() noexcept + { +#if defined(_MSC_VER) && !defined(__clang__) + // https://github.com/microsoft/cppwinrt/pull/1165 + std::pair> result; + std::memset(&result, 0, sizeof(result)); + result.first = this->size(); + result.second = *reinterpret_cast*>(this); + std::memset(this, 0, sizeof(com_array)); +#else + std::pair> result(this->size(), *reinterpret_cast*>(this)); + this->m_data = nullptr; + this->m_size = 0; +#endif + return result; + } + + template + friend std::pair> detach_abi(com_array& object) noexcept; }; - template com_array(uint32_t, C const&) -> com_array>; + template com_array(std::uint32_t, C const&) -> com_array>; template ::difference_type>> com_array(InIt, InIt) -> com_array::value_type>>; template com_array(std::vector const&) -> com_array>; - template com_array(std::array const&) -> com_array>; - template com_array(C const(&)[N]) -> com_array>; + template com_array(std::array const&) -> com_array>; + template com_array(C const(&)[N]) -> com_array>; template com_array(std::initializer_list) -> com_array>; +#ifdef __cpp_lib_span + template com_array(std::span const& value) -> com_array>; +#endif + + namespace impl { template @@ -418,11 +471,9 @@ WINRT_EXPORT namespace winrt } template - auto detach_abi(com_array& object) noexcept + std::pair> detach_abi(com_array& object) noexcept { - std::pair> result(object.size(), *reinterpret_cast*>(&object)); - memset(&object, 0, sizeof(com_array)); - return result; + return object.detach_abi(); } template @@ -432,7 +483,7 @@ WINRT_EXPORT namespace winrt } } -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { template struct array_size_proxy @@ -445,10 +496,10 @@ namespace winrt::impl ~array_size_proxy() noexcept { WINRT_ASSERT(m_value.data() || (!m_value.data() && m_size == 0)); - *reinterpret_cast(reinterpret_cast(&m_value) + 1) = m_size; + *reinterpret_cast(reinterpret_cast(&m_value) + 1) = m_size; } - operator uint32_t*() noexcept + operator std::uint32_t*() noexcept { return &m_size; } @@ -461,7 +512,7 @@ namespace winrt::impl private: com_array& m_value; - uint32_t m_size{ 0 }; + std::uint32_t m_size{ 0 }; }; template @@ -473,7 +524,7 @@ namespace winrt::impl template struct com_array_proxy { - com_array_proxy(uint32_t* size, winrt::impl::arg_out* value) noexcept : m_size(size), m_value(value) + com_array_proxy(std::uint32_t* size, winrt::impl::arg_out* value) noexcept : m_size(size), m_value(value) {} ~com_array_proxy() noexcept @@ -494,7 +545,7 @@ namespace winrt::impl private: - uint32_t* m_size; + std::uint32_t* m_size; arg_out* m_value; com_array m_temp; }; @@ -503,7 +554,7 @@ namespace winrt::impl WINRT_EXPORT namespace winrt { template - auto detach_abi(uint32_t* __valueSize, impl::arg_out* value) noexcept + auto detach_abi(std::uint32_t* __valueSize, impl::arg_out* value) noexcept { return impl::com_array_proxy(__valueSize, value); } diff --git a/strings/base_chrono.h b/strings/base_chrono.h index 1a6236b5b..7934ac5b2 100644 --- a/strings/base_chrono.h +++ b/strings/base_chrono.h @@ -3,17 +3,17 @@ WINRT_EXPORT namespace winrt { struct file_time { - uint64_t value{}; + std::uint64_t value{}; file_time() noexcept = default; - constexpr explicit file_time(uint64_t const value) noexcept : value(value) + constexpr explicit file_time(std::uint64_t const value) noexcept : value(value) { } #ifdef _FILETIME_ constexpr file_time(FILETIME const& value) noexcept - : value(value.dwLowDateTime | (static_cast(value.dwHighDateTime) << 32)) + : value(value.dwLowDateTime | (static_cast(value.dwHighDateTime) << 32)) { } @@ -26,7 +26,7 @@ WINRT_EXPORT namespace winrt struct clock { - using rep = int64_t; + using rep = std::int64_t; using period = impl::filetime_period; using duration = Windows::Foundation::TimeSpan; using time_point = Windows::Foundation::DateTime; @@ -42,17 +42,17 @@ WINRT_EXPORT namespace winrt static time_t to_time_t(time_point const& time) noexcept { - return std::chrono::duration_cast(time - time_t_epoch).count(); + return static_cast(std::chrono::system_clock::to_time_t(std::chrono::time_point_cast(to_sys(time)))); } static time_point from_time_t(time_t time) noexcept { - return time_t_epoch + time_t_duration{ time }; + return from_sys(std::chrono::time_point_cast(std::chrono::system_clock::from_time_t(time))); } static file_time to_file_time(time_point const& time) noexcept { - return file_time{ static_cast(time.time_since_epoch().count()) }; + return file_time{ static_cast(time.time_since_epoch().count()) }; } static time_point from_file_time(file_time const& time) noexcept @@ -70,10 +70,25 @@ WINRT_EXPORT namespace winrt return from_file_time(time); } + template + static std::chrono::time_point> + to_sys(std::chrono::time_point const& tp) + { + return epoch + tp.time_since_epoch(); + } + + template + static std::chrono::time_point> + from_sys(std::chrono::time_point const& tp) + { + using result_t = std::chrono::time_point>; + return result_t{ tp - epoch }; + } + private: - // Define 00:00:00, Jan 1 1970 UTC in FILETIME units. - static constexpr time_point time_t_epoch{ duration{ 0x019DB1DED53E8000 } }; - using time_t_duration = std::chrono::duration; + // system_clock epoch is 00:00:00, Jan 1 1970. + // This is 11644473600 seconds after Windows FILETIME epoch of 00:00:00, Jan 1 1601. + static constexpr std::chrono::time_point epoch{ std::chrono::seconds{ -11644473600 } }; }; } diff --git a/strings/base_collections.h b/strings/base_collections.h index afb70494b..7d8dc2e77 100644 --- a/strings/base_collections.h +++ b/strings/base_collections.h @@ -1,176 +1,17 @@ -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { namespace wfc = Windows::Foundation::Collections; - template - struct fast_iterator + template + auto consume_Windows_Foundation_Collections_IIterable::begin() const { - using iterator_category = std::input_iterator_tag; - using value_type = decltype(std::declval().GetAt(0)); - using difference_type = ptrdiff_t; - using pointer = value_type*; - using reference = value_type; - - fast_iterator() noexcept : m_collection(nullptr), m_index(0) {} - - fast_iterator(T const& collection, uint32_t const index) noexcept : - m_collection(&collection), - m_index(index) - {} - - fast_iterator& operator++() noexcept - { - ++m_index; - return*this; - } - - fast_iterator operator++(int) noexcept - { - auto previous = *this; - ++m_index; - return previous; - } - - fast_iterator& operator--() noexcept - { - --m_index; - return*this; - } - - fast_iterator operator--(int) noexcept - { - auto previous = *this; - --m_index; - return previous; - } - - fast_iterator& operator+=(difference_type n) noexcept - { - m_index += static_cast(n); - return*this; - } - - fast_iterator operator+(difference_type n) const noexcept - { - return fast_iterator(*this) += n; - } - - fast_iterator& operator-=(difference_type n) noexcept - { - return *this += -n; - } - - fast_iterator operator-(difference_type n) const noexcept - { - return *this + -n; - } - - difference_type operator-(fast_iterator const& other) const noexcept - { - return static_cast(m_index) - static_cast(other.m_index); - } - - reference operator*() const - { - return m_collection->GetAt(m_index); - } - - reference operator[](difference_type n) const - { - return m_collection->GetAt(m_index + static_cast(n)); - } - - bool operator==(fast_iterator const& other) const noexcept - { - WINRT_ASSERT(m_collection == other.m_collection); - return m_index == other.m_index; - } - - bool operator<(fast_iterator const& other) const noexcept - { - WINRT_ASSERT(m_collection == other.m_collection); - return m_index < other.m_index; - } - - bool operator!=(fast_iterator const& other) const noexcept - { - return !(*this == other); - } - - bool operator>(fast_iterator const& other) const noexcept - { - return !(*this < other); - } - - bool operator<=(fast_iterator const& other) const noexcept - { - return !(*this > other); - } - - bool operator>=(fast_iterator const& other) const noexcept - { - return !(*this < other); - } - - private: - - T const* m_collection{}; - uint32_t m_index{}; - }; - - template - class has_GetAt - { - template ().GetAt(0))> static constexpr bool get_value(int) { return true; } - template static constexpr bool get_value(...) { return false; } - - public: - - static constexpr bool value = get_value(0); - }; - - template ::value, int> = 0> - auto begin(T const& collection) -> decltype(collection.First()) - { - auto result = collection.First(); - - if (!result.HasCurrent()) - { - return {}; - } - - return result; - } - - template ::value, int> = 0> - auto end([[maybe_unused]] T const& collection) noexcept -> decltype(collection.First()) - { - return {}; + return get_begin_iterator(static_cast(*this)); } - - template ::value, int> = 0> - fast_iterator begin(T const& collection) noexcept - { - return { collection, 0 }; - } - - template ::value, int> = 0> - fast_iterator end(T const& collection) - { - return { collection, collection.Size() }; - } - - template ::value, int> = 0> - auto rbegin(T const& collection) - { - return std::make_reverse_iterator(end(collection)); - } - - template ::value, int> = 0> - auto rend(T const& collection) + template + auto consume_Windows_Foundation_Collections_IIterable::end() const { - return std::make_reverse_iterator(begin(collection)); + return get_end_iterator(static_cast(*this)); } template @@ -260,10 +101,10 @@ namespace winrt::impl private: - uint32_t const m_snapshot; + std::uint32_t const m_snapshot; }; - uint32_t get_version() const noexcept + std::uint32_t get_version() const noexcept { return m_version; } @@ -275,7 +116,7 @@ namespace winrt::impl private: - std::atomic m_version{}; + std::atomic m_version{}; }; template diff --git a/strings/base_collections_base.h b/strings/base_collections_base.h index 641d4aadb..d299cc0c8 100644 --- a/strings/base_collections_base.h +++ b/strings/base_collections_base.h @@ -1,3 +1,81 @@ +WINRT_EXPORT namespace winrt::impl +{ + struct nop_lock_guard {}; + + struct single_threaded_collection_base + { + [[nodiscard]] auto acquire_exclusive() const + { + return nop_lock_guard{}; + } + + [[nodiscard]] auto acquire_shared() const + { + return nop_lock_guard(); + } + }; + + struct multi_threaded_collection_base + { + [[nodiscard]] auto acquire_exclusive() const + { + return slim_lock_guard{m_mutex}; + } + + [[nodiscard]] auto acquire_shared() const + { + return slim_shared_lock_guard{m_mutex}; + } + + private: + + mutable slim_mutex m_mutex; + }; + + template + using container_type_t = std::decay_t().get_container())>; + + template + struct removed_values + { + void assign(container_type_t& value) + { + // Trivially destructible; okay to run destructors under lock and clearing allows potential re-use of buffers + value.clear(); + } + }; + + template + struct removed_values::value_type>>> + { + container_type_t m_value; + + void assign(container_type_t& value) + { + m_value.swap(value); + } + }; + + template + struct removed_value + { + // Trivially destructible; okay to run destructor under lock + template + void assign(U&&) {} + }; + + template + struct removed_value && !std::is_trivially_destructible_v>> + { + std::optional m_value; + + template + void assign(U&& value) + { + m_value.emplace(std::move(value)); + } + }; +} WINRT_EXPORT namespace winrt { @@ -16,8 +94,21 @@ WINRT_EXPORT namespace winrt return value; } + auto acquire_exclusive() const + { + return impl::nop_lock_guard{}; + } + + auto acquire_shared() const + { + // Support for concurrent "shared" operations is optional + return static_cast(*this).acquire_exclusive(); + } + auto First() { + // NOTE: iterator's constructor requires shared access + [[maybe_unused]] auto guard = static_cast(*this).acquire_shared(); return make(static_cast(this)); } @@ -53,7 +144,6 @@ WINRT_EXPORT namespace winrt void abi_enter() { m_owner->abi_enter(); - this->check_version(*m_owner); } void abi_exit() @@ -71,63 +161,78 @@ WINRT_EXPORT namespace winrt T Current() const { + [[maybe_unused]] auto guard = m_owner->acquire_shared(); + this->check_version(*m_owner); + if (m_current == m_end) { throw hresult_out_of_bounds(); } - if constexpr (!impl::is_key_value_pair::value) - { - return m_owner->unwrap_value(*m_current); - } - else - { - return make>(m_owner->unwrap_value(m_current->first), m_owner->unwrap_value(m_current->second)); - } + return current_value_withlock(); } - bool HasCurrent() const noexcept + bool HasCurrent() const { + [[maybe_unused]] auto guard = m_owner->acquire_shared(); + this->check_version(*m_owner); return m_current != m_end; } - bool MoveNext() noexcept + bool MoveNext() { + [[maybe_unused]] auto guard = m_owner->acquire_exclusive(); + this->check_version(*m_owner); if (m_current != m_end) { ++m_current; } - return HasCurrent(); + return m_current != m_end; } - uint32_t GetMany(array_view values) + std::uint32_t GetMany(array_view values) { + [[maybe_unused]] auto guard = m_owner->acquire_exclusive(); + this->check_version(*m_owner); return GetMany(values, typename std::iterator_traits::iterator_category()); } private: - uint32_t GetMany(array_view values, std::random_access_iterator_tag) + T current_value_withlock() const { - uint32_t const actual = (std::min)(static_cast(m_end - m_current), values.size()); + WINRT_ASSERT(m_current != m_end); + if constexpr (!impl::is_key_value_pair::value) + { + return m_owner->unwrap_value(*m_current); + } + else + { + return make>(m_owner->unwrap_value(m_current->first), m_owner->unwrap_value(m_current->second)); + } + } + + std::uint32_t GetMany(array_view values, std::random_access_iterator_tag) + { + std::uint32_t const actual = (std::min)(static_cast(m_end - m_current), values.size()); m_owner->copy_n(m_current, actual, values.begin()); m_current += actual; return actual; } - uint32_t GetMany(array_view values, std::input_iterator_tag) + std::uint32_t GetMany(array_view values, std::input_iterator_tag) { auto output = values.begin(); while (output < values.end() && m_current != m_end) { - *output = Current(); + *output = current_value_withlock(); ++output; ++m_current; } - return static_cast(output - values.begin()); + return static_cast(output - values.begin()); } using iterator_type = decltype(std::declval().get_container().begin()); @@ -141,9 +246,10 @@ WINRT_EXPORT namespace winrt template struct vector_view_base : iterable_base { - T GetAt(uint32_t const index) const + T GetAt(std::uint32_t const index) const { - if (index >= Size()) + [[maybe_unused]] auto guard = static_cast(*this).acquire_shared(); + if (index >= container_size()) { throw hresult_out_of_bounds(); } @@ -151,33 +257,43 @@ WINRT_EXPORT namespace winrt return static_cast(*this).unwrap_value(*std::next(static_cast(*this).get_container().begin(), index)); } - uint32_t Size() const noexcept + std::uint32_t Size() const noexcept { - return static_cast(std::distance(static_cast(*this).get_container().begin(), static_cast(*this).get_container().end())); + [[maybe_unused]] auto guard = static_cast(*this).acquire_shared(); + return container_size(); } - bool IndexOf(T const& value, uint32_t& index) const noexcept + bool IndexOf(T const& value, std::uint32_t& index) const noexcept { + [[maybe_unused]] auto guard = static_cast(*this).acquire_shared(); auto first = std::find_if(static_cast(*this).get_container().begin(), static_cast(*this).get_container().end(), [&](auto&& match) { return value == static_cast(*this).unwrap_value(match); }); - index = static_cast(first - static_cast(*this).get_container().begin()); - return index < Size(); + index = static_cast(first - static_cast(*this).get_container().begin()); + return index < container_size(); } - uint32_t GetMany(uint32_t const startIndex, array_view values) const + std::uint32_t GetMany(std::uint32_t const startIndex, array_view values) const { - if (startIndex >= Size()) + [[maybe_unused]] auto guard = static_cast(*this).acquire_shared(); + if (startIndex >= container_size()) { return 0; } - uint32_t const actual = (std::min)(Size() - startIndex, values.size()); + std::uint32_t const actual = (std::min)(container_size() - startIndex, values.size()); this->copy_n(static_cast(*this).get_container().begin() + startIndex, actual, values.begin()); return actual; } + + private: + + std::uint32_t container_size() const noexcept + { + return static_cast(std::distance(static_cast(*this).get_container().begin(), static_cast(*this).get_container().end())); + } }; template @@ -188,19 +304,25 @@ WINRT_EXPORT namespace winrt return static_cast(*this); } - void SetAt(uint32_t const index, T const& value) + void SetAt(std::uint32_t const index, T const& value) { + impl::removed_value::value_type> oldValue; + + [[maybe_unused]] auto guard = static_cast(*this).acquire_exclusive(); if (index >= static_cast(*this).get_container().size()) { throw hresult_out_of_bounds(); } this->increment_version(); - static_cast(*this).get_container()[index] = static_cast(*this).wrap_value(value); + auto&& pos = static_cast(*this).get_container()[index]; + oldValue.assign(pos); + pos = static_cast(*this).wrap_value(value); } - void InsertAt(uint32_t const index, T const& value) + void InsertAt(std::uint32_t const index, T const& value) { + [[maybe_unused]] auto guard = static_cast(*this).acquire_exclusive(); if (index > static_cast(*this).get_container().size()) { throw hresult_out_of_bounds(); @@ -210,43 +332,60 @@ WINRT_EXPORT namespace winrt static_cast(*this).get_container().insert(static_cast(*this).get_container().begin() + index, static_cast(*this).wrap_value(value)); } - void RemoveAt(uint32_t const index) + void RemoveAt(std::uint32_t const index) { + impl::removed_value::value_type> removedValue; + + [[maybe_unused]] auto guard = static_cast(*this).acquire_exclusive(); if (index >= static_cast(*this).get_container().size()) { throw hresult_out_of_bounds(); } this->increment_version(); - static_cast(*this).get_container().erase(static_cast(*this).get_container().begin() + index); + auto itr = static_cast(*this).get_container().begin() + index; + removedValue.assign(*itr); + static_cast(*this).get_container().erase(itr); } void Append(T const& value) { + [[maybe_unused]] auto guard = static_cast(*this).acquire_exclusive(); this->increment_version(); static_cast(*this).get_container().push_back(static_cast(*this).wrap_value(value)); } void RemoveAtEnd() { + impl::removed_value::value_type> removedValue; + + [[maybe_unused]] auto guard = static_cast(*this).acquire_exclusive(); if (static_cast(*this).get_container().empty()) { throw hresult_out_of_bounds(); } this->increment_version(); + removedValue.assign(static_cast(*this).get_container().back()); static_cast(*this).get_container().pop_back(); } void Clear() noexcept { + impl::removed_values oldContainer; + + [[maybe_unused]] auto guard = static_cast(*this).acquire_exclusive(); this->increment_version(); - static_cast(*this).get_container().clear(); + oldContainer.assign(static_cast(*this).get_container()); } void ReplaceAll(array_view value) { + impl::removed_values oldContainer; + + [[maybe_unused]] auto guard = static_cast(*this).acquire_exclusive(); this->increment_version(); + oldContainer.assign(static_cast(*this).get_container()); assign(value.begin(), value.end()); } @@ -255,16 +394,14 @@ WINRT_EXPORT namespace winrt template void assign(InputIt first, InputIt last) { - using container_type = std::remove_reference_t(*this).get_container())>; - - if constexpr (std::is_same_v) + if constexpr (std::is_same_v::value_type>) { static_cast(*this).get_container().assign(first, last); } else { auto& container = static_cast(*this).get_container(); - container.clear(); + WINRT_ASSERT(container.empty()); container.reserve(std::distance(first, last)); std::transform(first, last, std::back_inserter(container), [&](auto&& value) @@ -288,19 +425,19 @@ WINRT_EXPORT namespace winrt m_changed.remove(cookie); } - void SetAt(uint32_t const index, T const& value) + void SetAt(std::uint32_t const index, T const& value) { vector_base::SetAt(index, value); call_changed(Windows::Foundation::Collections::CollectionChange::ItemChanged, index); } - void InsertAt(uint32_t const index, T const& value) + void InsertAt(std::uint32_t const index, T const& value) { vector_base::InsertAt(index, value); call_changed(Windows::Foundation::Collections::CollectionChange::ItemInserted, index); } - void RemoveAt(uint32_t const index) + void RemoveAt(std::uint32_t const index) { vector_base::RemoveAt(index); call_changed(Windows::Foundation::Collections::CollectionChange::ItemRemoved, index); @@ -332,7 +469,7 @@ WINRT_EXPORT namespace winrt protected: - void call_changed(Windows::Foundation::Collections::CollectionChange const change, uint32_t const index) + void call_changed(Windows::Foundation::Collections::CollectionChange const change, std::uint32_t const index) { m_changed(static_cast(*this), make(change, index)); } @@ -343,7 +480,7 @@ WINRT_EXPORT namespace winrt struct args : implements { - args(Windows::Foundation::Collections::CollectionChange const change, uint32_t const index) noexcept : + args(Windows::Foundation::Collections::CollectionChange const change, std::uint32_t const index) noexcept : m_change(change), m_index(index) { @@ -354,7 +491,7 @@ WINRT_EXPORT namespace winrt return m_change; } - uint32_t Index() const noexcept + std::uint32_t Index() const noexcept { return m_index; } @@ -362,15 +499,30 @@ WINRT_EXPORT namespace winrt private: Windows::Foundation::Collections::CollectionChange const m_change; - uint32_t const m_index; + std::uint32_t const m_index; }; }; template struct map_view_base : iterable_base, Version> { + // specialization of Lookup that avoids throwing the hresult + std::optional TryLookup(K const& key, trylookup_from_abi_t) const + { + [[maybe_unused]] auto guard = static_cast(*this).acquire_shared(); + auto pair = static_cast(*this).get_container().find(static_cast(*this).wrap_value(key)); + + if (pair == static_cast(*this).get_container().end()) + { + return std::nullopt; + } + + return static_cast(*this).unwrap_value(pair->second); + } + V Lookup(K const& key) const { + [[maybe_unused]] auto guard = static_cast(*this).acquire_shared(); auto pair = static_cast(*this).get_container().find(static_cast(*this).wrap_value(key)); if (pair == static_cast(*this).get_container().end()) @@ -381,13 +533,15 @@ WINRT_EXPORT namespace winrt return static_cast(*this).unwrap_value(pair->second); } - uint32_t Size() const noexcept + std::uint32_t Size() const noexcept { - return static_cast(static_cast(*this).get_container().size()); + [[maybe_unused]] auto guard = static_cast(*this).acquire_shared(); + return static_cast(static_cast(*this).get_container().size()); } bool HasKey(K const& key) const noexcept { + [[maybe_unused]] auto guard = static_cast(*this).acquire_shared(); return static_cast(*this).get_container().find(static_cast(*this).wrap_value(key)) != static_cast(*this).get_container().end(); } @@ -396,6 +550,7 @@ WINRT_EXPORT namespace winrt first = nullptr; second = nullptr; } + }; template @@ -408,21 +563,42 @@ WINRT_EXPORT namespace winrt bool Insert(K const& key, V const& value) { + impl::removed_value::mapped_type> oldValue; + + [[maybe_unused]] auto guard = static_cast(*this).acquire_exclusive(); this->increment_version(); - auto pair = static_cast(*this).get_container().insert_or_assign(static_cast(*this).wrap_value(key), static_cast(*this).wrap_value(value)); - return !pair.second; + auto [itr, added] = static_cast(*this).get_container().emplace(static_cast(*this).wrap_value(key), static_cast(*this).wrap_value(value)); + if (!added) + { + oldValue.assign(itr->second); + itr->second = static_cast(*this).wrap_value(value); + } + + return !added; } void Remove(K const& key) { + typename impl::container_type_t::node_type removedNode; + + [[maybe_unused]] auto guard = static_cast(*this).acquire_exclusive(); + auto& container = static_cast(*this).get_container(); + auto found = container.find(static_cast(*this).wrap_value(key)); + if (found == container.end()) + { + throw hresult_out_of_bounds(); + } this->increment_version(); - static_cast(*this).get_container().erase(static_cast(*this).wrap_value(key)); + removedNode = container.extract(found); } void Clear() noexcept { + impl::removed_values oldContainer; + + [[maybe_unused]] auto guard = static_cast(*this).acquire_exclusive(); this->increment_version(); - static_cast(*this).get_container().clear(); + oldContainer.assign(static_cast(*this).get_container()); } }; @@ -458,15 +634,17 @@ WINRT_EXPORT namespace winrt call_changed(Windows::Foundation::Collections::CollectionChange::Reset, impl::empty_value()); } - private: - - event> m_changed; + protected: void call_changed(Windows::Foundation::Collections::CollectionChange const change, K const& key) { m_changed(static_cast(*this), make(change, key)); } + private: + + event> m_changed; + struct args : implements> { args(Windows::Foundation::Collections::CollectionChange const change, K const& key) noexcept : diff --git a/strings/base_collections_input_iterable.h b/strings/base_collections_input_iterable.h index e75211c30..e9d3af251 100644 --- a/strings/base_collections_input_iterable.h +++ b/strings/base_collections_input_iterable.h @@ -1,5 +1,5 @@ -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { template struct input_iterable : diff --git a/strings/base_collections_input_map.h b/strings/base_collections_input_map.h index 16ed34ccf..3fe146bf6 100644 --- a/strings/base_collections_input_map.h +++ b/strings/base_collections_input_map.h @@ -1,14 +1,15 @@ -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { - template - struct input_map : - implements, wfc::IMap, wfc::IMapView, wfc::IIterable>>, - map_base, K, V> + template + struct map_impl : + implements, wfc::IMap, wfc::IMapView, wfc::IIterable>>, + map_base, K, V>, + ThreadingBase { static_assert(std::is_same_v>, "Must be constructed with rvalue."); - explicit input_map(Container&& values) : m_values(std::forward(values)) + explicit map_impl(Container&& values) : m_values(std::forward(values)) { } @@ -22,11 +23,17 @@ namespace winrt::impl return m_values; } + using ThreadingBase::acquire_shared; + using ThreadingBase::acquire_exclusive; + private: Container m_values; }; + template + using input_map = map_impl; + template auto make_input_map(Container&& values) { diff --git a/strings/base_collections_input_map_view.h b/strings/base_collections_input_map_view.h index bfd8d82a9..d79eed61d 100644 --- a/strings/base_collections_input_map_view.h +++ b/strings/base_collections_input_map_view.h @@ -1,5 +1,5 @@ -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { template struct input_map_view : diff --git a/strings/base_collections_input_vector.h b/strings/base_collections_input_vector.h index 0b6d57674..a06e73b33 100644 --- a/strings/base_collections_input_vector.h +++ b/strings/base_collections_input_vector.h @@ -1,14 +1,15 @@ -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { - template - struct input_vector : - implements, wfc::IVector, wfc::IVectorView, wfc::IIterable>, - vector_base, T> + template + struct vector_impl : + implements, wfc::IVector, wfc::IVectorView, wfc::IIterable>, + vector_base, T>, + ThreadingBase { static_assert(std::is_same_v>, "Must be constructed with rvalue."); - explicit input_vector(Container&& values) : m_values(std::forward(values)) + explicit vector_impl(Container&& values) : m_values(std::forward(values)) { } @@ -22,10 +23,16 @@ namespace winrt::impl return m_values; } + using ThreadingBase::acquire_shared; + using ThreadingBase::acquire_exclusive; + private: Container m_values; }; + + template + using input_vector = vector_impl; } WINRT_EXPORT namespace winrt::param @@ -81,7 +88,7 @@ WINRT_EXPORT namespace winrt::param private: interface_type m_interface; - bool m_owned{ true }; + bool m_owned = true; }; template diff --git a/strings/base_collections_input_vector_view.h b/strings/base_collections_input_vector_view.h index 3793e239a..30768c18e 100644 --- a/strings/base_collections_input_vector_view.h +++ b/strings/base_collections_input_vector_view.h @@ -1,5 +1,5 @@ -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { template struct input_vector_view : diff --git a/strings/base_collections_map.h b/strings/base_collections_map.h index 877b5bf30..6bf884236 100644 --- a/strings/base_collections_map.h +++ b/strings/base_collections_map.h @@ -1,14 +1,18 @@ -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { template - struct observable_map : - implements, wfc::IObservableMap, wfc::IMap, wfc::IMapView, wfc::IIterable>>, - observable_map_base, K, V> + using multi_threaded_map = map_impl; + + template + struct observable_map_impl : + implements, wfc::IObservableMap, wfc::IMap, wfc::IMapView, wfc::IIterable>>, + observable_map_base, K, V>, + ThreadingBase { static_assert(std::is_same_v>, "Must be constructed with rvalue."); - explicit observable_map(Container&& values) : m_values(std::forward(values)) + explicit observable_map_impl(Container&& values) : m_values(std::forward(values)) { } @@ -22,10 +26,19 @@ namespace winrt::impl return m_values; } + using ThreadingBase::acquire_shared; + using ThreadingBase::acquire_exclusive; + private: Container m_values; }; + + template + using observable_map = observable_map_impl; + + template + using multi_threaded_observable_map = observable_map_impl; } WINRT_EXPORT namespace winrt @@ -48,6 +61,24 @@ WINRT_EXPORT namespace winrt return make>>(std::move(values)); } + template , typename Allocator = std::allocator>> + Windows::Foundation::Collections::IMap multi_threaded_map() + { + return make>>(std::map{}); + } + + template , typename Allocator = std::allocator>> + Windows::Foundation::Collections::IMap multi_threaded_map(std::map&& values) + { + return make>>(std::move(values)); + } + + template , typename KeyEqual = std::equal_to, typename Allocator = std::allocator>> + Windows::Foundation::Collections::IMap multi_threaded_map(std::unordered_map&& values) + { + return make>>(std::move(values)); + } + template , typename Allocator = std::allocator>> Windows::Foundation::Collections::IObservableMap single_threaded_observable_map() { @@ -65,17 +96,35 @@ WINRT_EXPORT namespace winrt { return make>>(std::move(values)); } + + template , typename Allocator = std::allocator>> + Windows::Foundation::Collections::IObservableMap multi_threaded_observable_map() + { + return make>>(std::map{}); + } + + template , typename Allocator = std::allocator>> + Windows::Foundation::Collections::IObservableMap multi_threaded_observable_map(std::map&& values) + { + return make>>(std::move(values)); + } + + template , typename KeyEqual = std::equal_to, typename Allocator = std::allocator>> + Windows::Foundation::Collections::IObservableMap multi_threaded_observable_map(std::unordered_map&& values) + { + return make>>(std::move(values)); + } } -namespace std +WINRT_EXPORT namespace std { template struct tuple_size> - : integral_constant + : integral_constant { }; - template + template struct tuple_element> { static_assert(Idx < 2, "key-value pair index out of bounds"); @@ -83,9 +132,9 @@ namespace std }; } -namespace winrt::Windows::Foundation::Collections +WINRT_EXPORT namespace winrt::Windows::Foundation::Collections { - template + template std::tuple_element_t> get(IKeyValuePair const& kvp) { static_assert(Idx < 2, "key-value pair index out of bounds"); diff --git a/strings/base_collections_vector.h b/strings/base_collections_vector.h index 024a5495d..3388806af 100644 --- a/strings/base_collections_vector.h +++ b/strings/base_collections_vector.h @@ -1,11 +1,15 @@ -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { - template + template + using multi_threaded_vector = vector_impl; + + template struct inspectable_observable_vector : - observable_vector_base, Windows::Foundation::IInspectable>, - implements, - wfc::IObservableVector, wfc::IVector, wfc::IVectorView, wfc::IIterable> + observable_vector_base, Windows::Foundation::IInspectable>, + implements, + wfc::IObservableVector, wfc::IVector, wfc::IVectorView, wfc::IIterable>, + ThreadingBase { static_assert(std::is_same_v>, "Must be constructed with rvalue."); @@ -23,23 +27,30 @@ namespace winrt::impl return m_values; } + using ThreadingBase::acquire_shared; + using ThreadingBase::acquire_exclusive; + private: Container m_values; }; - template + template + using multi_threaded_inspectable_observable_vector = inspectable_observable_vector; + + template struct convertible_observable_vector : - observable_vector_base, T>, - implements, + observable_vector_base, T>, + implements, wfc::IObservableVector, wfc::IVector, wfc::IVectorView, wfc::IIterable, - wfc::IObservableVector, wfc::IVector, wfc::IVectorView, wfc::IIterable> + wfc::IObservableVector, wfc::IVector, wfc::IVectorView, wfc::IIterable>, + ThreadingBase { static_assert(!std::is_same_v); static_assert(std::is_same_v>, "Must be constructed with rvalue."); - using container_type = convertible_observable_vector; - using base_type = observable_vector_base, T>; + using container_type = convertible_observable_vector; + using base_type = observable_vector_base, T>; explicit convertible_observable_vector(Container&& values) : m_values(std::forward(values)) { @@ -55,6 +66,9 @@ namespace winrt::impl return m_values; } + using ThreadingBase::acquire_shared; + using ThreadingBase::acquire_exclusive; + auto First() { struct result @@ -68,6 +82,7 @@ namespace winrt::impl operator wfc::IIterator() { + [[maybe_unused]] auto guard = container->acquire_shared(); return make(container); } }; @@ -75,12 +90,12 @@ namespace winrt::impl return result{ this }; } - auto GetAt(uint32_t const index) const + auto GetAt(std::uint32_t const index) const { struct result { base_type const* container; - uint32_t const index; + std::uint32_t const index; operator T() const { @@ -98,29 +113,41 @@ namespace winrt::impl using base_type::IndexOf; - bool IndexOf(Windows::Foundation::IInspectable const& value, uint32_t& index) const + bool IndexOf(Windows::Foundation::IInspectable const& value, std::uint32_t& index) const { - try + if constexpr (is_com_interface_v) { - return IndexOf(unbox_value(value), index); + if (!value) + { + return base_type::IndexOf(nullptr, index); + } + else if (auto as = value.try_as()) + { + return base_type::IndexOf(as, index); + } } - catch (hresult_no_interface const&) + else { - index = 0; - return false; + if (auto as = value.try_as()) + { + return base_type::IndexOf(as.value(), index); + } } + + return false; } using base_type::GetMany; - uint32_t GetMany(uint32_t const startIndex, array_view values) const + std::uint32_t GetMany(std::uint32_t const startIndex, array_view values) const { + [[maybe_unused]] auto guard = this->acquire_shared(); if (startIndex >= m_values.size()) { return 0; } - uint32_t const actual = (std::min)(static_cast(m_values.size() - startIndex), values.size()); + std::uint32_t const actual = (std::min)(static_cast(m_values.size() - startIndex), values.size()); std::transform(m_values.begin() + startIndex, m_values.begin() + startIndex + actual, values.begin(), [&](auto && value) { @@ -152,14 +179,14 @@ namespace winrt::impl using base_type::SetAt; - void SetAt(uint32_t const index, Windows::Foundation::IInspectable const& value) + void SetAt(std::uint32_t const index, Windows::Foundation::IInspectable const& value) { SetAt(index, unbox_value(value)); } using base_type::InsertAt; - void InsertAt(uint32_t const index, Windows::Foundation::IInspectable const& value) + void InsertAt(std::uint32_t const index, Windows::Foundation::IInspectable const& value) { InsertAt(index, unbox_value(value)); } @@ -202,11 +229,6 @@ namespace winrt::impl impl::collection_version::iterator_type, implements> { - void abi_enter() - { - check_version(*m_owner); - } - explicit iterator(container_type* const container) noexcept : impl::collection_version::iterator_type(*container), m_current(container->get_container().begin()), @@ -217,6 +239,8 @@ namespace winrt::impl Windows::Foundation::IInspectable Current() const { + [[maybe_unused]] auto guard = m_owner->acquire_shared(); + check_version(*m_owner); if (m_current == m_end) { throw hresult_out_of_bounds(); @@ -225,24 +249,30 @@ namespace winrt::impl return box_value(*m_current); } - bool HasCurrent() const noexcept + bool HasCurrent() const { + [[maybe_unused]] auto guard = m_owner->acquire_shared(); + check_version(*m_owner); return m_current != m_end; } - bool MoveNext() noexcept + bool MoveNext() { + [[maybe_unused]] auto guard = m_owner->acquire_exclusive(); + check_version(*m_owner); if (m_current != m_end) { ++m_current; } - return HasCurrent(); + return m_current != m_end; } - uint32_t GetMany(array_view values) + std::uint32_t GetMany(array_view values) { - uint32_t const actual = (std::min)(static_cast(std::distance(m_current, m_end)), values.size()); + [[maybe_unused]] auto guard = m_owner->acquire_exclusive(); + check_version(*m_owner); + std::uint32_t const actual = (std::min)(static_cast(std::distance(m_current, m_end)), values.size()); std::transform(m_current, m_current + actual, values.begin(), [&](auto && value) { @@ -262,6 +292,9 @@ namespace winrt::impl Container m_values; }; + + template + using multi_threaded_convertible_observable_vector = convertible_observable_vector; } WINRT_EXPORT namespace winrt @@ -272,6 +305,12 @@ WINRT_EXPORT namespace winrt return make>>(std::move(values)); } + template > + Windows::Foundation::Collections::IVector multi_threaded_vector(std::vector&& values = {}) + { + return make>>(std::move(values)); + } + template > Windows::Foundation::Collections::IObservableVector single_threaded_observable_vector(std::vector&& values = {}) { @@ -284,4 +323,17 @@ WINRT_EXPORT namespace winrt return make>>(std::move(values)); } } + + template > + Windows::Foundation::Collections::IObservableVector multi_threaded_observable_vector(std::vector&& values = {}) + { + if constexpr (std::is_same_v) + { + return make>>(std::move(values)); + } + else + { + return make>>(std::move(values)); + } + } } diff --git a/strings/base_com_ptr.h b/strings/base_com_ptr.h index 46867a73a..0f02fabeb 100644 --- a/strings/base_com_ptr.h +++ b/strings/base_com_ptr.h @@ -1,4 +1,39 @@ +WINRT_EXPORT namespace winrt +{ + template + struct com_ptr; +} + +WINRT_EXPORT namespace winrt::impl +{ + struct capture_decay + { + void** result; + + template + operator T** () + { + return reinterpret_cast(result); + } + }; + + template + std::int32_t capture_to(void**result, F function, Args&& ...args) + { + return function(args..., guid_of(), capture_decay{ result }); + } + + template || std::is_union_v, int> = 0> + std::int32_t capture_to(void** result, O* object, M method, Args&& ...args) + { + return (object->*method)(args..., guid_of(), capture_decay{ result }); + } + + template + std::int32_t capture_to(void** result, com_ptr const& object, M method, Args&& ...args); +} + WINRT_EXPORT namespace winrt { template @@ -87,7 +122,7 @@ WINRT_EXPORT namespace winrt type** put() noexcept { - WINRT_ASSERT(m_ptr == nullptr); + release_ref(); return &m_ptr; } @@ -133,8 +168,17 @@ WINRT_EXPORT namespace winrt template bool try_as(To& to) const noexcept { - to = try_as>(); - return static_cast(to); + if constexpr (impl::is_com_interface_v || !std::is_same_v>) + { + to = try_as>(); + return static_cast(to); + } + else + { + auto result = try_as(); + to = result.has_value() ? result.value() : impl::empty_value(); + return result.has_value(); + } } hresult as(guid const& id, void** result) const noexcept @@ -153,16 +197,16 @@ WINRT_EXPORT namespace winrt *other = m_ptr; } - template - void capture(F function, Args&&...args) + template + bool try_capture(Args&&...args) { - check_hresult(function(args..., guid_of(), put_void())); + return impl::capture_to(put_void(), std::forward(args)...) >= 0; } - template - void capture(com_ptr const& object, M method, Args&&...args) + template + void capture(Args&&...args) { - check_hresult((object.get()->*(method))(args..., guid_of(), put_void())); + check_hresult(impl::capture_to(put_void(), std::forward(args)...)); } private: @@ -193,7 +237,7 @@ WINRT_EXPORT namespace winrt } } - __declspec(noinline) void unconditional_release_ref() noexcept + WINRT_IMPL_NOINLINE void unconditional_release_ref() noexcept { std::exchange(m_ptr, {})->Release(); } @@ -204,18 +248,19 @@ WINRT_EXPORT namespace winrt type* m_ptr{}; }; - template - impl::com_ref capture(F function, Args&& ...args) + template + impl::com_ref try_capture(Args&& ...args) { void* result{}; - check_hresult(function(args..., guid_of(), &result)); + impl::capture_to(&result, std::forward(args)...); return { result, take_ownership_from_abi }; } - template - impl::com_ref capture(com_ptr const& object, M method, Args && ...args) + + template + impl::com_ref capture(Args&& ...args) { void* result{}; - check_hresult((object.get()->*(method))(args..., guid_of(), &result)); + check_hresult(impl::capture_to(&result, std::forward(args)...)); return { result, take_ownership_from_abi }; } @@ -304,6 +349,15 @@ WINRT_EXPORT namespace winrt } } +WINRT_EXPORT namespace winrt::impl +{ + template + std::int32_t capture_to(void** result, com_ptr const& object, M method, Args&& ...args) + { + return (object.get()->*(method))(args..., guid_of(), capture_decay{ result }); + } +} + template void** IID_PPV_ARGS_Helper(winrt::com_ptr* ptr) noexcept { diff --git a/strings/base_composable.h b/strings/base_composable.h index efeb55877..5a7712ef7 100644 --- a/strings/base_composable.h +++ b/strings/base_composable.h @@ -1,9 +1,13 @@ -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { template struct composable_factory { +#ifdef _MSC_VER +#pragma warning(push) +#pragma warning(disable: 4702) // Compiler bug causing spurious "unreachable code" warnings +#endif template static I CreateInstance(const Windows::Foundation::IInspectable& outer, Windows::Foundation::IInspectable& inner, Args&&... args) { @@ -11,6 +15,9 @@ namespace winrt::impl inner = CreateInstanceImpl(outer, std::forward(args)...); return inner.as(); } +#ifdef _MSC_VER +#pragma warning(pop) +#endif private: template @@ -27,7 +34,7 @@ namespace winrt::impl }; template - class __declspec(empty_bases) produce_dispatch_to_overridable_base + class WINRT_IMPL_EMPTY_BASES produce_dispatch_to_overridable_base { protected: D& shim() noexcept diff --git a/strings/base_coroutine_foundation.h b/strings/base_coroutine_foundation.h index 8b22caa1f..82582ee64 100644 --- a/strings/base_coroutine_foundation.h +++ b/strings/base_coroutine_foundation.h @@ -1,5 +1,5 @@ -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { template struct async_completed_handler; @@ -35,18 +35,19 @@ namespace winrt::impl { // Note: A blocking wait on the UI thread for an asynchronous operation can cause a deadlock. // See https://docs.microsoft.com/windows/uwp/cpp-and-winrt-apis/concurrency#block-the-calling-thread - WINRT_ASSERT(!is_sta()); + WINRT_ASSERT(!is_sta_thread()); } template std::pair make_delegate_with_shared_state(H&& handler) { auto d = make_delegate(std::forward(handler)); - return { std::move(d), reinterpret_cast*>(get_abi(d)) }; + auto abi = reinterpret_cast*>(get_abi(d)); + return { std::move(d), abi }; } template - auto wait_for_completed(Async const& async, uint32_t const timeout) + auto wait_for_completed(Async const& async, std::uint32_t const timeout) { struct shared_type { @@ -71,8 +72,8 @@ namespace winrt::impl { check_sta_blocking_wait(); auto const milliseconds = std::chrono::duration_cast(timeout).count(); - WINRT_ASSERT((milliseconds >= 0) && (static_cast(milliseconds) < 0xFFFFFFFFull)); // Within uint32_t range and not INFINITE - return wait_for_completed(async, static_cast(milliseconds)); + WINRT_ASSERT((milliseconds >= 0) && (static_cast(milliseconds) < 0xFFFFFFFFull)); // Within std::uint32_t range and not INFINITE + return wait_for_completed(async, static_cast(milliseconds)); } inline void check_status_canceled(Windows::Foundation::AsyncStatus status) @@ -98,61 +99,133 @@ namespace winrt::impl return async.GetResults(); } - struct disconnect_aware_handler + template + auto wait_get_bypass_sta_check(Async const& async) + { + auto status = async.Status(); + if (status == Windows::Foundation::AsyncStatus::Started) + { + status = wait_for_completed(async, 0xFFFFFFFF); // INFINITE + } + check_status_canceled(status); + + return async.GetResults(); + } + +#ifdef WINRT_IMPL_COROUTINES + struct ignore_apartment_context {}; + + template + struct disconnect_aware_handler : private std::conditional_t { - disconnect_aware_handler(std::experimental::coroutine_handle<> handle) - : m_handle(handle) { } + disconnect_aware_handler(Awaiter* awaiter, std::coroutine_handle<> handle) noexcept + : m_awaiter(awaiter), m_handle(handle) { } - disconnect_aware_handler(disconnect_aware_handler&& other) - : m_context(std::move(other.m_context)) - , m_handle(std::exchange(other.m_handle, {})) { } + disconnect_aware_handler(disconnect_aware_handler&& other) = default; ~disconnect_aware_handler() { - if (m_handle) Complete(); + if (m_handle.value) Complete(); } - void operator()() + template + void operator()(Async&&, Windows::Foundation::AsyncStatus status) { + m_awaiter.value->status = status; Complete(); } private: - std::experimental::coroutine_handle<> m_handle; - com_ptr m_context = apartment_context(); + movable_primitive m_awaiter; + movable_primitive, nullptr> m_handle; void Complete() { - resume_apartment(m_context, std::exchange(m_handle, {})); + if (m_awaiter.value->suspending.exchange(false, std::memory_order_release)) + { + m_handle.value = nullptr; // resumption deferred to await_suspend + } + else + { + auto handle = m_handle.detach(); + if constexpr (preserve_context) + { + if (!resume_apartment(*this, handle, &m_awaiter.value->failure)) + { + handle.resume(); + } + } + else + { + handle.resume(); + } + } } }; - template - struct await_adapter + template + struct await_adapter : cancellable_awaiter> { - Async const& async; + template + await_adapter(T&& async) : async(std::forward(async)) { } + + std::conditional_t async; Windows::Foundation::AsyncStatus status = Windows::Foundation::AsyncStatus::Started; + std::int32_t failure = 0; + std::atomic suspending = true; + + void enable_cancellation(cancellable_promise* promise) + { + promise->set_canceller([](void* parameter) + { + cancel_asynchronously(reinterpret_cast(parameter)->async); + }, this); + } bool await_ready() const noexcept { return false; } - void await_suspend(std::experimental::coroutine_handle<> handle) + template + bool await_suspend(std::coroutine_handle handle) { - async.Completed([this, handler = disconnect_aware_handler{ handle }](auto&&, auto operation_status) mutable - { - status = operation_status; - handler(); - }); + this->set_cancellable_promise_from_handle(handle); + return register_completed_callback(handle); } auto await_resume() const { + check_hresult(failure); check_status_canceled(status); return async.GetResults(); } + + private: + bool register_completed_callback(std::coroutine_handle<> handle) + { + if constexpr (!preserve_context) + { + // Ensure that the illegal delegate assignment propagates properly. + suspending.store(true, std::memory_order_relaxed); + } + async.Completed(disconnect_aware_handler(this, handle)); + return suspending.exchange(false, std::memory_order_acquire); + } + + static fire_and_forget cancel_asynchronously(Async async) + { + co_await winrt::resume_background(); + try + { + async.Cancel(); + } + catch (hresult_error const&) + { + } + } }; +#endif template auto consume_Windows_Foundation_IAsyncAction::get() const @@ -160,6 +233,11 @@ namespace winrt::impl impl::wait_get(static_cast(static_cast(*this))); } template + auto consume_Windows_Foundation_IAsyncAction::get_unchecked() const + { + impl::wait_get_bypass_sta_check(static_cast(static_cast(*this))); + } + template auto consume_Windows_Foundation_IAsyncAction::wait_for(Windows::Foundation::TimeSpan const& timeout) const { return impl::wait_for(static_cast(static_cast(*this)), timeout); @@ -171,6 +249,11 @@ namespace winrt::impl return impl::wait_get(static_cast const&>(static_cast(*this))); } template + auto consume_Windows_Foundation_IAsyncOperation::get_unchecked() const + { + return impl::wait_get_bypass_sta_check(static_cast const&>(static_cast(*this))); + } + template auto consume_Windows_Foundation_IAsyncOperation::wait_for(Windows::Foundation::TimeSpan const& timeout) const { return impl::wait_for(static_cast const&>(static_cast(*this)), timeout); @@ -182,6 +265,11 @@ namespace winrt::impl impl::wait_get(static_cast const&>(static_cast(*this))); } template + auto consume_Windows_Foundation_IAsyncActionWithProgress::get_unchecked() const + { + impl::wait_get_bypass_sta_check(static_cast const&>(static_cast(*this))); + } + template auto consume_Windows_Foundation_IAsyncActionWithProgress::wait_for(Windows::Foundation::TimeSpan const& timeout) const { return impl::wait_for(static_cast const&>(static_cast(*this)), timeout); @@ -193,13 +281,27 @@ namespace winrt::impl return impl::wait_get(static_cast const&>(static_cast(*this))); } template + auto consume_Windows_Foundation_IAsyncOperationWithProgress::get_unchecked() const + { + return impl::wait_get_bypass_sta_check(static_cast const&>(static_cast(*this))); + } + template auto consume_Windows_Foundation_IAsyncOperationWithProgress::wait_for(Windows::Foundation::TimeSpan const& timeout) const { return impl::wait_for(static_cast const&>(static_cast(*this)), timeout); } } -#ifdef __cpp_coroutines +#ifdef WINRT_IMPL_COROUTINES +WINRT_EXPORT namespace winrt +{ + template>> + inline impl::await_adapter, false> resume_agile(Async&& async) + { + return { std::forward(async) }; + }; +} + WINRT_EXPORT namespace winrt::Windows::Foundation { inline impl::await_adapter operator co_await(IAsyncAction const& async) @@ -225,7 +327,6 @@ WINRT_EXPORT namespace winrt::Windows::Foundation return{ async }; } } -#endif WINRT_EXPORT namespace winrt { @@ -244,7 +345,7 @@ WINRT_EXPORT namespace winrt } } -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { template struct cancellation_token @@ -258,7 +359,7 @@ namespace winrt::impl return true; } - void await_suspend(std::experimental::coroutine_handle<>) const noexcept + void await_suspend(std::coroutine_handle<>) const noexcept { } @@ -272,11 +373,20 @@ namespace winrt::impl return m_promise->Status() == Windows::Foundation::AsyncStatus::Canceled; } - void callback(winrt::delegate<>&& cancel) noexcept + void callback(winrt::delegate<>&& cancel) const noexcept { m_promise->cancellation_callback(std::move(cancel)); } + bool enable_propagation(bool value = true) const noexcept + { + return m_promise->enable_cancellation_propagation(value); + } + + bool originate_on_cancel(bool value = true) const noexcept + { + return m_promise->originate_on_cancel(value); + } private: Promise* m_promise; @@ -295,7 +405,7 @@ namespace winrt::impl return true; } - void await_suspend(std::experimental::coroutine_handle<>) const noexcept + void await_suspend(std::coroutine_handle<>) const noexcept { } @@ -304,29 +414,36 @@ namespace winrt::impl return *this; } - void operator()(Progress const& result) + void operator()(Progress const& result) const { m_promise->set_progress(result); } + template + void set_result(T&& value) const + { + static_assert(!std::is_same_v, "Setting preliminary results requires IAsync...WithProgress"); + m_promise->return_value(std::forward(value)); + } + private: Promise* m_promise; }; template - struct promise_base : implements + struct promise_base : implements, cancellable_promise { using AsyncStatus = Windows::Foundation::AsyncStatus; unsigned long __stdcall Release() noexcept { - uint32_t const remaining = this->subtract_reference(); + std::uint32_t const remaining = this->subtract_reference(); if (remaining == 0) { std::atomic_thread_fence(std::memory_order_acquire); - std::experimental::coroutine_handle::from_promise(*static_cast(this)).destroy(); + std::coroutine_handle::from_promise(*static_cast(this)).destroy(); } return remaining; @@ -346,18 +463,17 @@ namespace winrt::impl m_completed_assigned = true; - if (m_status == AsyncStatus::Started) + status = m_status.load(std::memory_order_relaxed); + if (status == AsyncStatus::Started) { m_completed = make_agile_delegate(handler); return; } - - status = m_status; } if (handler) { - invoke(handler, *this, status); + winrt::impl::invoke(handler, *this, status); } } @@ -367,15 +483,17 @@ namespace winrt::impl return m_completed; } - uint32_t Id() const noexcept + std::uint32_t Id() const noexcept { return 1; } AsyncStatus Status() noexcept { - slim_lock_guard const guard(m_lock); - return m_status; + // It's okay to race against another thread that is changing the + // status. In the case where the promise was published from another + // thread, we need acquire in order to preserve causality. + return m_status.load(std::memory_order_acquire); } hresult ErrorCode() noexcept @@ -383,7 +501,7 @@ namespace winrt::impl try { slim_lock_guard const guard(m_lock); - rethrow_if_failed(); + rethrow_if_failed(m_status.load(std::memory_order_relaxed)); return 0; } catch (...) @@ -399,10 +517,17 @@ namespace winrt::impl { slim_lock_guard const guard(m_lock); - if (m_status == AsyncStatus::Started) + if (m_status.load(std::memory_order_relaxed) == AsyncStatus::Started) { - m_status = AsyncStatus::Canceled; - m_exception = std::make_exception_ptr(hresult_canceled()); + m_status.store(AsyncStatus::Canceled, std::memory_order_relaxed); + if (cancellable_promise::originate_on_cancel()) + { + m_exception = std::make_exception_ptr(hresult_canceled()); + } + else + { + m_exception = std::make_exception_ptr(hresult_canceled(hresult_error::no_originate)); + } cancel = std::move(m_cancel); } } @@ -411,6 +536,8 @@ namespace winrt::impl { cancel(); } + + cancellable_promise::cancel(); } void Close() const noexcept @@ -421,14 +548,28 @@ namespace winrt::impl { slim_lock_guard const guard(m_lock); - if (m_status == AsyncStatus::Completed) + auto status = m_status.load(std::memory_order_relaxed); + + if constexpr (std::is_same_v) + { + if (status == AsyncStatus::Completed) + { + return static_cast(this)->get_return_value(); + } + rethrow_if_failed(status); + WINRT_ASSERT(status == AsyncStatus::Started); + throw hresult_illegal_method_call(); + } + else { - return static_cast(this)->get_return_value(); + if (status == AsyncStatus::Completed || status == AsyncStatus::Started) + { + return static_cast(this)->copy_return_value(); + } + WINRT_ASSERT(status == AsyncStatus::Error || status == AsyncStatus::Canceled); + std::rethrow_exception(m_exception); } - rethrow_if_failed(); - WINRT_ASSERT(m_status == AsyncStatus::Started); - throw hresult_illegal_method_call(); } AsyncInterface get_return_object() const noexcept @@ -440,6 +581,10 @@ namespace winrt::impl { } + void copy_return_value() const noexcept + { + } + void set_completed() noexcept { async_completed_handler_t handler; @@ -448,22 +593,23 @@ namespace winrt::impl { slim_lock_guard const guard(m_lock); - if (m_status == AsyncStatus::Started) + status = m_status.load(std::memory_order_relaxed); + if (status == AsyncStatus::Started) { - m_status = AsyncStatus::Completed; + status = AsyncStatus::Completed; + m_status.store(status, std::memory_order_relaxed); } handler = std::move(this->m_completed); - status = this->m_status; } if (handler) { - invoke(handler, *this, status); + winrt::impl::invoke(handler, *this, status); } } - std::experimental::suspend_never initial_suspend() const noexcept + std::suspend_never initial_suspend() const noexcept { return{}; } @@ -481,10 +627,10 @@ namespace winrt::impl { } - bool await_suspend(std::experimental::coroutine_handle<>) const noexcept + bool await_suspend(std::coroutine_handle<>) const noexcept { promise->set_completed(); - uint32_t const remaining = promise->subtract_reference(); + std::uint32_t const remaining = promise->subtract_reference(); if (remaining == 0) { @@ -497,18 +643,13 @@ namespace winrt::impl auto final_suspend() noexcept { - if (winrt_suspend_handler) - { - winrt_suspend_handler(this); - } - return final_suspend_awaiter{ this }; } void unhandled_exception() noexcept { slim_lock_guard const guard(m_lock); - WINRT_ASSERT(m_status == AsyncStatus::Started || m_status == AsyncStatus::Canceled); + WINRT_ASSERT(m_status.load(std::memory_order_relaxed) == AsyncStatus::Started || m_status.load(std::memory_order_relaxed) == AsyncStatus::Canceled); m_exception = std::current_exception(); try @@ -517,23 +658,30 @@ namespace winrt::impl } catch (hresult_canceled const&) { - m_status = AsyncStatus::Canceled; + m_status.store(AsyncStatus::Canceled, std::memory_order_relaxed); } catch (...) { - m_status = AsyncStatus::Error; + m_status.store(AsyncStatus::Error, std::memory_order_relaxed); } } template - auto await_transform(Expression&& expression) + Expression&& await_transform(Expression&& expression) { if (Status() == AsyncStatus::Canceled) { - throw winrt::hresult_canceled(); + if (cancellable_promise::originate_on_cancel()) + { + throw winrt::hresult_canceled(); + } + else + { + throw winrt::hresult_canceled(hresult_error::no_originate); + } } - return notify_awaiter{ static_cast(expression) }; + return std::forward(expression); } cancellation_token await_transform(get_cancellation_token_t) noexcept @@ -551,14 +699,17 @@ namespace winrt::impl { slim_lock_guard const guard(m_lock); - if (m_status != AsyncStatus::Canceled) + if (m_status.load(std::memory_order_relaxed) != AsyncStatus::Canceled) { m_cancel = std::move(cancel); return; } } - cancel(); + if (cancel) + { + cancel(); + } } #if defined(_DEBUG) && !defined(WINRT_NO_MAKE_DETECTION) @@ -569,9 +720,9 @@ namespace winrt::impl protected: - void rethrow_if_failed() const + void rethrow_if_failed(AsyncStatus status) const { - if (m_status == AsyncStatus::Error || m_status == AsyncStatus::Canceled) + if (status == AsyncStatus::Error || status == AsyncStatus::Canceled) { std::rethrow_exception(m_exception); } @@ -581,12 +732,12 @@ namespace winrt::impl slim_mutex m_lock; async_completed_handler_t m_completed; winrt::delegate<> m_cancel; - AsyncStatus m_status{ AsyncStatus::Started }; + std::atomic m_status; bool m_completed_assigned{ false }; }; } -WINRT_EXPORT namespace std::experimental +WINRT_IMPL_STD_EXPORT namespace std { template struct coroutine_traits @@ -644,6 +795,11 @@ WINRT_EXPORT namespace std::experimental return std::move(m_result); } + TResult copy_return_value() noexcept + { + return m_result; + } + void return_value(TResult&& value) noexcept { m_result = std::move(value); @@ -683,13 +839,20 @@ WINRT_EXPORT namespace std::experimental return std::move(m_result); } + TResult copy_return_value() noexcept + { + return m_result; + } + void return_value(TResult&& value) noexcept { + winrt::slim_lock_guard const guard(this->m_lock); m_result = std::move(value); } void return_value(TResult const& value) noexcept { + winrt::slim_lock_guard const guard(this->m_lock); m_result = value; } @@ -743,7 +906,7 @@ WINRT_EXPORT namespace winrt auto [delegate, shared] = impl::make_delegate_with_shared_state>(shared_type{}); - auto completed = [&](T const& async) + auto completed = [delegate = std::move(delegate)](T const& async) { async.Completed(delegate); }; @@ -755,3 +918,4 @@ WINRT_EXPORT namespace winrt co_return shared->result.GetResults(); } } +#endif diff --git a/strings/base_coroutine_system.h b/strings/base_coroutine_system.h index 92a83feaa..f79fe0671 100644 --- a/strings/base_coroutine_system.h +++ b/strings/base_coroutine_system.h @@ -1,4 +1,5 @@ +#ifdef WINRT_IMPL_COROUTINES WINRT_EXPORT namespace winrt { [[nodiscard]] inline auto resume_foreground( @@ -23,7 +24,7 @@ WINRT_EXPORT namespace winrt return m_queued; } - bool await_suspend(std::experimental::coroutine_handle<> handle) + bool await_suspend(std::coroutine_handle<> handle) { return m_dispatcher.TryEnqueue(m_priority, [handle, this] { @@ -41,10 +42,9 @@ WINRT_EXPORT namespace winrt return awaitable{ dispatcher, priority }; }; -#ifdef __cpp_coroutines inline auto operator co_await(Windows::System::DispatcherQueue const& dispatcher) { return resume_foreground(dispatcher); } -#endif } +#endif diff --git a/strings/base_coroutine_system_winui.h b/strings/base_coroutine_system_winui.h deleted file mode 100644 index 770573664..000000000 --- a/strings/base_coroutine_system_winui.h +++ /dev/null @@ -1,50 +0,0 @@ - -WINRT_EXPORT namespace winrt -{ - [[nodiscard]] inline auto resume_foreground( - Microsoft::System::DispatcherQueue const& dispatcher, - Microsoft::System::DispatcherQueuePriority const priority = Microsoft::System::DispatcherQueuePriority::Normal) noexcept - { - struct awaitable - { - awaitable(Microsoft::System::DispatcherQueue const& dispatcher, Microsoft::System::DispatcherQueuePriority const priority) noexcept : - m_dispatcher(dispatcher), - m_priority(priority) - { - } - - bool await_ready() const noexcept - { - return false; - } - - bool await_resume() const noexcept - { - return m_queued; - } - - bool await_suspend(std::experimental::coroutine_handle<> handle) - { - return m_dispatcher.TryEnqueue(m_priority, [handle, this] - { - m_queued = true; - handle(); - }); - } - - private: - Microsoft::System::DispatcherQueue const& m_dispatcher; - Microsoft::System::DispatcherQueuePriority const m_priority; - bool m_queued{}; - }; - - return awaitable{ dispatcher, priority }; - }; - -#ifdef __cpp_coroutines - inline auto operator co_await(Microsoft::System::DispatcherQueue const& dispatcher) - { - return resume_foreground(dispatcher); - } -#endif -} diff --git a/strings/base_coroutine_threadpool.h b/strings/base_coroutine_threadpool.h index 254840dc2..c901b94ba 100644 --- a/strings/base_coroutine_threadpool.h +++ b/strings/base_coroutine_threadpool.h @@ -1,217 +1,258 @@ -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { - inline void __stdcall resume_background_callback(void*, void* context) noexcept - { - std::experimental::coroutine_handle<>::from_address(context)(); - }; - - inline auto resume_background(std::experimental::coroutine_handle<> handle) +#ifdef WINRT_IMPL_COROUTINES + inline auto submit_threadpool_callback(void(__stdcall* callback)(void*, void* context), void* context) { - if (!WINRT_IMPL_TrySubmitThreadpoolCallback(resume_background_callback, handle.address(), nullptr)) + if (!WINRT_IMPL_TrySubmitThreadpoolCallback(callback, context, nullptr)) { throw_last_error(); } } - inline bool is_sta() noexcept + inline void __stdcall resume_background_callback(void*, void* context) noexcept + { + std::coroutine_handle<>::from_address(context)(); + }; + + inline auto resume_background(std::coroutine_handle<> handle) { - int32_t aptType; - int32_t aptTypeQualifier; - return (0 == WINRT_IMPL_CoGetApartmentType(&aptType, &aptTypeQualifier)) && ((aptType == 0 /*APTTYPE_STA*/) || (aptType == 3 /*APTTYPE_MAINSTA*/)); + submit_threadpool_callback(resume_background_callback, handle.address()); } +#endif - inline bool requires_apartment_context() noexcept + inline std::pair get_apartment_type() noexcept { - int32_t aptType; - int32_t aptTypeQualifier; - return (0 == WINRT_IMPL_CoGetApartmentType(&aptType, &aptTypeQualifier)) && ((aptType == 0 /*APTTYPE_STA*/) || (aptType == 2 /*APTTYPE_NA*/) || (aptType == 3 /*APTTYPE_MAINSTA*/)); + std::int32_t aptType; + std::int32_t aptTypeQualifier; + if (0 == WINRT_IMPL_CoGetApartmentType(&aptType, &aptTypeQualifier)) + { + return { aptType, aptTypeQualifier }; + } + else + { + return { 1 /* APTTYPE_MTA */, 1 /* APTTYPEQUALIFIER_IMPLICIT_MTA */ }; + } } - inline auto apartment_context() + inline bool is_sta_thread() noexcept { - return requires_apartment_context() ? capture(WINRT_IMPL_CoGetObjectContext) : nullptr; + auto type = get_apartment_type(); + switch (type.first) + { + case 0: /* APTTYPE_STA */ + case 3: /* APTTYPE_MAINSTA */ + return true; + case 2: /* APTTYPE_NA */ + return type.second == 3 /* APTTYPEQUALIFIER_NA_ON_STA */ || + type.second == 5 /* APTTYPEQUALIFIER_NA_ON_MAINSTA */; + } + return false; } - inline int32_t __stdcall resume_apartment_callback(com_callback_args* args) noexcept +#ifdef WINRT_IMPL_COROUTINES + struct resume_apartment_context + { + resume_apartment_context() = default; + resume_apartment_context(std::nullptr_t) : m_context(nullptr), m_context_type(-1) {} + + bool valid() const noexcept + { + return m_context_type.value >= 0; + } + + com_ptr m_context = try_capture(WINRT_IMPL_CoGetObjectContext); + movable_primitive m_context_type = get_apartment_type().first; + }; + + inline std::int32_t __stdcall resume_apartment_callback(com_callback_args* args) noexcept { - std::experimental::coroutine_handle<>::from_address(args->data)(); + std::coroutine_handle<>::from_address(args->data)(); return 0; }; - inline auto resume_apartment(com_ptr const& context, std::experimental::coroutine_handle<> handle) + [[nodiscard]] inline bool resume_apartment_sync(com_ptr const& context, std::coroutine_handle<> handle, std::int32_t* failure) { - if (context) - { - com_callback_args args{}; - args.data = handle.address(); + com_callback_args args{}; + args.data = handle.address(); - check_hresult(context->ContextCallback(resume_apartment_callback, &args, guid_of(), 5, nullptr)); - } - else + auto result = context->ContextCallback(resume_apartment_callback, &args, guid_of(), 5, nullptr); + if (result < 0) { - if (requires_apartment_context()) - { - resume_background(handle); - } - else - { - handle(); - } + // Resume the coroutine on the wrong apartment, but tell it why. + *failure = result; + return false; } + return true; } - template - class has_awaitable_member + struct threadpool_resume { - template ().await_ready())> static constexpr bool get_value(int) { return true; } - template static constexpr bool get_value(...) { return false; } - - public: - - static constexpr bool value = get_value(0); + threadpool_resume(com_ptr const& context, std::coroutine_handle<> handle, std::int32_t* failure) : + m_context(context), m_handle(handle), m_failure(failure) { } + com_ptr m_context; + std::coroutine_handle<> m_handle; + std::int32_t* m_failure; }; - template - class has_awaitable_free + inline void __stdcall fallback_submit_threadpool_callback(void*, void* p) noexcept { - template ()))> static constexpr bool get_value(int) { return true; } - template static constexpr bool get_value(...) { return false; } - - public: - - static constexpr bool value = get_value(0); - }; + std::unique_ptr state{ static_cast(p) }; + if (!resume_apartment_sync(state->m_context, state->m_handle, state->m_failure)) + { + state->m_handle.resume(); + } + } - template - struct free_await_adapter_impl + inline void resume_apartment_on_threadpool(com_ptr const& context, std::coroutine_handle<> handle, std::int32_t* failure) { - T&& awaitable; + auto state = std::make_unique(context, handle, failure); + submit_threadpool_callback(fallback_submit_threadpool_callback, state.get()); + state.release(); + } - bool ready() + [[nodiscard]] inline auto resume_apartment(resume_apartment_context const& context, std::coroutine_handle<> handle, std::int32_t* failure) + { + WINRT_ASSERT(context.valid()); + if ((context.m_context == nullptr) || (context.m_context == try_capture(WINRT_IMPL_CoGetObjectContext))) { - return await_ready(awaitable); + return false; } - - template - auto suspend(std::experimental::coroutine_handle handle) + else if (context.m_context_type.value == 1 /* APTTYPE_MTA */) { - return await_suspend(awaitable, handle); + resume_background(handle); + return true; } - - auto resume() + else if (is_sta_thread()) { - return await_resume(awaitable); + resume_apartment_on_threadpool(context.m_context, handle, failure); + return true; } - }; + else + { + return resume_apartment_sync(context.m_context, handle, failure); + } + } +#endif +} - template - struct free_await_adapter +#ifdef WINRT_IMPL_COROUTINES +WINRT_EXPORT namespace winrt +{ + struct cancellable_promise { - T&& awaitable; + using canceller_t = void(*)(void*); - bool await_ready() + void set_canceller(canceller_t canceller, void* context) { - return free_await_adapter_impl{ static_cast(awaitable) }.ready(); + m_context = context; + canceller_t expected = nullptr; + m_canceller.compare_exchange_strong(expected, canceller, std::memory_order_release, std::memory_order_relaxed); } - template - auto await_suspend(std::experimental::coroutine_handle handle) + void revoke_canceller() { - return free_await_adapter_impl{ static_cast(awaitable) }.suspend(handle); + auto existing = m_canceller.load(std::memory_order_relaxed); + do + { + while (existing == cancelling_ptr) + { + std::this_thread::yield(); + existing = m_canceller.load(std::memory_order_relaxed); + } + } + while (!m_canceller.compare_exchange_weak(existing, nullptr, std::memory_order_acquire, std::memory_order_relaxed)); } - auto await_resume() + void cancel() { - return free_await_adapter_impl{ static_cast(awaitable) }.resume(); + auto canceller = m_canceller.exchange(cancelling_ptr, std::memory_order_acquire); + if (canceller != cancelling_ptr) + { + struct unique_cancellation_lock + { + cancellable_promise* promise; + ~unique_cancellation_lock() + { + promise->m_canceller.store(nullptr, std::memory_order_release); + } + } lock{ this }; + + if (canceller != nullptr) + { + canceller(m_context); + } + } } - }; - - template - struct member_await_adapter - { - T&& awaitable; - bool await_ready() + bool enable_cancellation_propagation(bool value) noexcept { - return awaitable.await_ready(); + return std::exchange(m_propagate_cancellation, value); } - template - auto await_suspend(std::experimental::coroutine_handle handle) + bool cancellation_propagation_enabled() const noexcept { - return awaitable.await_suspend(handle); + return m_propagate_cancellation; } - auto await_resume() + bool originate_on_cancel(bool value = true) noexcept { - return awaitable.await_resume(); + return std::exchange(m_originate_on_cancel, value); } - }; - template - auto get_awaiter(T&& value) noexcept -> decltype(static_cast(value).operator co_await()) - { - return static_cast(value).operator co_await(); - } - - template - auto get_awaiter(T&& value) noexcept -> decltype(operator co_await(static_cast(value))) - { - return operator co_await(static_cast(value)); - } + bool should_originate_on_cancel() const noexcept + { + return m_originate_on_cancel; + } - template ::value, int> = 0> - auto get_awaiter(T&& value) noexcept - { - return member_await_adapter{ static_cast(value) }; - } + private: + static inline auto const cancelling_ptr = reinterpret_cast(1); - template ::value, int> = 0> - auto get_awaiter(T&& value) noexcept - { - return free_await_adapter{ static_cast(value) }; - } + std::atomic m_canceller{ nullptr }; + void* m_context{ nullptr }; + bool m_propagate_cancellation{ false }; + bool m_originate_on_cancel{ true }; // By default, will call RoOriginateError before throwing a cancel error code. + }; - template - struct notify_awaiter + template + struct cancellable_awaiter { - decltype(get_awaiter(std::declval())) awaitable; + cancellable_awaiter() noexcept = default; + cancellable_awaiter(cancellable_awaiter const&) = default; - notify_awaiter(T&& awaitable) : awaitable(get_awaiter(static_cast(awaitable))) + ~cancellable_awaiter() { - } - - bool await_ready() - { - if (winrt_suspend_handler) + if (m_promise) { - winrt_suspend_handler(this); + m_promise->revoke_canceller(); } - - return awaitable.await_ready(); } - template - auto await_suspend(std::experimental::coroutine_handle handle) + void operator=(cancellable_awaiter const&) = delete; + + protected: + template + void set_cancellable_promise_from_handle(std::coroutine_handle const& handle) { - return awaitable.await_suspend(handle); + if constexpr (std::is_base_of_v) + { + set_cancellable_promise(&handle.promise()); + } } - auto await_resume() + private: + void set_cancellable_promise(cancellable_promise* promise) { - if (winrt_resume_handler) + if (promise->cancellation_propagation_enabled()) { - winrt_resume_handler(this); + m_promise = promise; + static_cast(this)->enable_cancellation(m_promise); } - - return awaitable.await_resume(); } + + cancellable_promise* m_promise = nullptr; }; -} -WINRT_EXPORT namespace winrt -{ [[nodiscard]] inline auto resume_background() noexcept { struct awaitable @@ -225,7 +266,7 @@ WINRT_EXPORT namespace winrt { } - void await_suspend(std::experimental::coroutine_handle<> handle) const + void await_suspend(std::coroutine_handle<> handle) const { impl::resume_background(handle); } @@ -252,7 +293,7 @@ WINRT_EXPORT namespace winrt { } - void await_suspend(std::experimental::coroutine_handle<> resume) + void await_suspend(std::coroutine_handle<> resume) { m_resume = resume; @@ -272,7 +313,7 @@ WINRT_EXPORT namespace winrt } T const& m_context; - std::experimental::coroutine_handle<> m_resume{ nullptr }; + std::coroutine_handle<> m_resume{ nullptr }; }; return awaitable{ context }; @@ -280,144 +321,281 @@ WINRT_EXPORT namespace winrt struct apartment_context { + apartment_context() = default; + apartment_context(std::nullptr_t) : context(nullptr) { } + + operator bool() const noexcept { return context.valid(); } + bool operator!() const noexcept { return !context.valid(); } + + impl::resume_apartment_context context; + }; +} + +WINRT_EXPORT namespace winrt::impl +{ + struct apartment_awaiter + { + apartment_context const& context; + std::int32_t failure = 0; + bool await_ready() const noexcept { return false; } - void await_resume() const noexcept + void await_resume() const { + check_hresult(failure); } - void await_suspend(std::experimental::coroutine_handle<> handle) const + bool await_suspend(std::coroutine_handle<> handle) { - impl::resume_apartment(context, handle); + auto context_copy = context; + return impl::resume_apartment(context_copy.context, handle, &failure); } - - com_ptr context = impl::apartment_context(); }; - [[nodiscard]] inline auto resume_after(Windows::Foundation::TimeSpan duration) noexcept + struct timespan_awaiter : cancellable_awaiter { - struct awaitable + explicit timespan_awaiter(Windows::Foundation::TimeSpan duration) noexcept : + m_duration(duration) { - explicit awaitable(Windows::Foundation::TimeSpan duration) noexcept : - m_duration(duration) + } + +#if defined(__GNUC__) && !defined(__clang__) + // HACK: GCC seems to require a move when calling operator co_await + // on the return value of resume_after. + // This might be related to upstream bug: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99575 + timespan_awaiter(timespan_awaiter &&other) noexcept : + m_timer{std::move(other.m_timer)}, + m_duration{std::move(other.m_duration)}, + m_handle{std::move(other.m_handle)}, + m_state{other.m_state.load()} + {} +#endif + + void enable_cancellation(cancellable_promise* promise) + { + promise->set_canceller([](void* context) { - } + auto that = static_cast(context); + if (that->m_state.exchange(state::canceled, std::memory_order_acquire) == state::pending) + { + that->fire_immediately(); + } + }, this); + } - bool await_ready() const noexcept + bool await_ready() const noexcept + { + return m_duration.count() <= 0; + } + + template + void await_suspend(std::coroutine_handle handle) + { + set_cancellable_promise_from_handle(handle); + + m_handle = handle; + create_threadpool_timer(); + } + + void await_resume() + { + if (m_state.exchange(state::idle, std::memory_order_relaxed) == state::canceled) { - return m_duration.count() <= 0; + throw hresult_canceled(); } + } + + private: + void create_threadpool_timer() + { + m_timer.attach(check_pointer(WINRT_IMPL_CreateThreadpoolTimer(callback, this, nullptr))); + std::int64_t relative_count = -m_duration.count(); + WINRT_IMPL_SetThreadpoolTimer(m_timer.get(), &relative_count, 0, 0); - void await_suspend(std::experimental::coroutine_handle<> handle) + state expected = state::idle; + if (!m_state.compare_exchange_strong(expected, state::pending, std::memory_order_release)) { - m_timer.attach(check_pointer(WINRT_IMPL_CreateThreadpoolTimer(callback, handle.address(), nullptr))); - int64_t relative_count = -m_duration.count(); - WINRT_IMPL_SetThreadpoolTimer(m_timer.get(), &relative_count, 0, 0); + fire_immediately(); } + } - void await_resume() const noexcept + void fire_immediately() noexcept + { + if (WINRT_IMPL_SetThreadpoolTimerEx(m_timer.get(), nullptr, 0, 0)) { + std::int64_t now = 0; + WINRT_IMPL_SetThreadpoolTimer(m_timer.get(), &now, 0, 0); } + } - private: + static void __stdcall callback(void*, void* context, void*) noexcept + { + auto that = reinterpret_cast(context); + that->m_handle(); + } + + struct timer_traits + { + using type = impl::ptp_timer; - static void __stdcall callback(void*, void* context, void*) noexcept + static void close(type value) noexcept { - std::experimental::coroutine_handle<>::from_address(context)(); + WINRT_IMPL_CloseThreadpoolTimer(value); } - struct timer_traits + static constexpr type invalid() noexcept { - using type = impl::ptp_timer; + return nullptr; + } + }; - static void close(type value) noexcept - { - WINRT_IMPL_CloseThreadpoolTimer(value); - } + enum class state { idle, pending, canceled }; - static constexpr type invalid() noexcept + handle_type m_timer; + Windows::Foundation::TimeSpan m_duration; + std::coroutine_handle<> m_handle; + std::atomic m_state{ state::idle }; + }; + + struct signal_awaiter : cancellable_awaiter + { + signal_awaiter(void* handle, Windows::Foundation::TimeSpan timeout) noexcept : + m_timeout(timeout), + m_handle(handle) + {} + +#if defined(__GNUC__) && !defined(__clang__) + // HACK: GCC seems to require a move when calling operator co_await + // on the return value of resume_on_signal. + // This might be related to upstream bug: + // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=99575 + signal_awaiter(signal_awaiter &&other) noexcept : + m_wait{std::move(other.m_wait)}, + m_timeout{std::move(other.m_timeout)}, + m_handle{std::move(other.m_handle)}, + m_result{std::move(other.m_result)}, + m_resume{std::move(other.m_resume)}, + m_state{other.m_state.load()} + {} +#endif + + void enable_cancellation(cancellable_promise* promise) + { + promise->set_canceller([](void* context) + { + auto that = static_cast(context); + if (that->m_state.exchange(state::canceled, std::memory_order_acquire) == state::pending) { - return nullptr; + that->fire_immediately(); } - }; + }, this); + } - handle_type m_timer; - Windows::Foundation::TimeSpan m_duration; - }; + bool await_ready() const noexcept + { + return WINRT_IMPL_WaitForSingleObject(m_handle, 0) == 0; + } - return awaitable{ duration }; - } + template + void await_suspend(std::coroutine_handle resume) + { + set_cancellable_promise_from_handle(resume); -#ifdef __cpp_coroutines - inline auto operator co_await(Windows::Foundation::TimeSpan duration) - { - return resume_after(duration); - } -#endif + m_resume = resume; + create_threadpool_wait(); + } - [[nodiscard]] inline auto resume_on_signal(void* handle, Windows::Foundation::TimeSpan timeout = {}) noexcept - { - struct awaitable + bool await_resume() { - awaitable(void* handle, Windows::Foundation::TimeSpan timeout) noexcept : - m_timeout(timeout), - m_handle(handle) - {} - - bool await_ready() const noexcept + if (m_state.exchange(state::idle, std::memory_order_relaxed) == state::canceled) { - return WINRT_IMPL_WaitForSingleObject(m_handle, 0) == 0; + throw hresult_canceled(); } + return m_result == 0; + } + + private: - void await_suspend(std::experimental::coroutine_handle<> resume) + void create_threadpool_wait() + { + m_wait.attach(check_pointer(WINRT_IMPL_CreateThreadpoolWait(callback, this, nullptr))); + std::int64_t relative_count = -m_timeout.count(); + std::int64_t* file_time = relative_count != 0 ? &relative_count : nullptr; + WINRT_IMPL_SetThreadpoolWait(m_wait.get(), m_handle, file_time); + + state expected = state::idle; + if (!m_state.compare_exchange_strong(expected, state::pending, std::memory_order_release)) { - m_resume = resume; - m_wait.attach(check_pointer(WINRT_IMPL_CreateThreadpoolWait(callback, this, nullptr))); - int64_t relative_count = -m_timeout.count(); - int64_t* file_time = relative_count != 0 ? &relative_count : nullptr; - WINRT_IMPL_SetThreadpoolWait(m_wait.get(), m_handle, file_time); + fire_immediately(); } + } - bool await_resume() const noexcept + void fire_immediately() noexcept + { + if (WINRT_IMPL_SetThreadpoolWaitEx(m_wait.get(), nullptr, nullptr, nullptr)) { - return m_result == 0; + std::int64_t now = 0; + WINRT_IMPL_SetThreadpoolWait(m_wait.get(), WINRT_IMPL_GetCurrentProcess(), &now); } + } - private: + static void __stdcall callback(void*, void* context, void*, std::uint32_t result) noexcept + { + auto that = static_cast(context); + that->m_result = result; + that->m_resume(); + } + + struct wait_traits + { + using type = impl::ptp_wait; - static void __stdcall callback(void*, void* context, void*, uint32_t result) noexcept + static void close(type value) noexcept { - auto that = static_cast(context); - that->m_result = result; - that->m_resume(); + WINRT_IMPL_CloseThreadpoolWait(value); } - struct wait_traits + static constexpr type invalid() noexcept { - using type = impl::ptp_wait; + return nullptr; + } + }; - static void close(type value) noexcept - { - WINRT_IMPL_CloseThreadpoolWait(value); - } + enum class state { idle, pending, canceled }; - static constexpr type invalid() noexcept - { - return nullptr; - } - }; + handle_type m_wait; + Windows::Foundation::TimeSpan m_timeout; + void* m_handle; + std::uint32_t m_result{}; + std::coroutine_handle<> m_resume{ nullptr }; + std::atomic m_state{ state::idle }; + }; +} - handle_type m_wait; - Windows::Foundation::TimeSpan m_timeout; - void* m_handle; - uint32_t m_result{}; - std::experimental::coroutine_handle<> m_resume{ nullptr }; - }; +WINRT_EXPORT namespace winrt +{ + inline impl::apartment_awaiter operator co_await(apartment_context const& context) + { + return{ context }; + } - return awaitable{ handle, timeout }; + [[nodiscard]] inline impl::timespan_awaiter resume_after(Windows::Foundation::TimeSpan duration) noexcept + { + return impl::timespan_awaiter{ duration }; + } + + inline impl::timespan_awaiter operator co_await(Windows::Foundation::TimeSpan duration) + { + return resume_after(duration); + } + + [[nodiscard]] inline impl::signal_awaiter resume_on_signal(void* handle, Windows::Foundation::TimeSpan timeout = {}) noexcept + { + return impl::signal_awaiter{ handle, timeout }; } struct thread_pool @@ -428,7 +606,7 @@ WINRT_EXPORT namespace winrt m_environment.Pool = m_pool.get(); } - void thread_limits(uint32_t const high, uint32_t const low) + void thread_limits(std::uint32_t const high, std::uint32_t const low) { WINRT_IMPL_SetThreadpoolThreadMaximum(m_pool.get(), high); check_bool(WINRT_IMPL_SetThreadpoolThreadMinimum(m_pool.get(), low)); @@ -443,7 +621,7 @@ WINRT_EXPORT namespace winrt { } - void await_suspend(std::experimental::coroutine_handle<> handle) + void await_suspend(std::coroutine_handle<> handle) { if (!WINRT_IMPL_TrySubmitThreadpoolCallback(callback, handle.address(), &m_environment)) { @@ -455,7 +633,7 @@ WINRT_EXPORT namespace winrt static void __stdcall callback(void*, void* context) noexcept { - std::experimental::coroutine_handle<>::from_address(context)(); + std::coroutine_handle<>::from_address(context)(); } struct pool_traits @@ -475,7 +653,7 @@ WINRT_EXPORT namespace winrt struct environment // TP_CALLBACK_ENVIRON { - uint32_t Version{ 3 }; + std::uint32_t Version{ 3 }; void* Pool{}; void* CleanupGroup{}; void* CleanupGroupCancelCallback{}; @@ -484,16 +662,16 @@ WINRT_EXPORT namespace winrt void* FinalizationCallback{}; union { - uint32_t Flags{}; + std::uint32_t Flags{}; struct { - uint32_t LongFunction : 1; - uint32_t Persistent : 1; - uint32_t Private : 30; + std::uint32_t LongFunction : 1; + std::uint32_t Persistent : 1; + std::uint32_t Private : 30; } s; } u; - int32_t CallbackPriority{ 1 }; - uint32_t Size{ sizeof(environment) }; + std::int32_t CallbackPriority{ 1 }; + std::uint32_t Size{ sizeof(environment) }; }; handle_type m_pool; @@ -503,7 +681,7 @@ WINRT_EXPORT namespace winrt struct fire_and_forget {}; } -namespace std::experimental +WINRT_IMPL_STD_EXPORT namespace std { template struct coroutine_traits @@ -519,18 +697,13 @@ namespace std::experimental { } - suspend_never initial_suspend() const noexcept + std::suspend_never initial_suspend() const noexcept { return{}; } - suspend_never final_suspend() const noexcept + std::suspend_never final_suspend() const noexcept { - if (winrt_suspend_handler) - { - winrt_suspend_handler(this); - } - return{}; } @@ -538,12 +711,7 @@ namespace std::experimental { winrt::terminate(); } - - template - auto await_transform(Expression&& expression) - { - return winrt::impl::notify_awaiter{ static_cast(expression) }; - } }; }; } +#endif diff --git a/strings/base_coroutine_ui_core.h b/strings/base_coroutine_ui_core.h index 55177b058..7efed5174 100644 --- a/strings/base_coroutine_ui_core.h +++ b/strings/base_coroutine_ui_core.h @@ -1,4 +1,5 @@ +#ifdef WINRT_IMPL_COROUTINES WINRT_EXPORT namespace winrt { [[nodiscard]] inline auto resume_foreground( @@ -22,7 +23,7 @@ WINRT_EXPORT namespace winrt { } - void await_suspend(std::experimental::coroutine_handle<> handle) const + void await_suspend(std::coroutine_handle<> handle) const { m_dispatcher.RunAsync(m_priority, [handle] { @@ -39,10 +40,9 @@ WINRT_EXPORT namespace winrt return awaitable{ dispatcher, priority }; }; -#ifdef __cpp_coroutines inline auto operator co_await(Windows::UI::Core::CoreDispatcher const& dispatcher) { return resume_foreground(dispatcher); } -#endif } +#endif diff --git a/strings/base_deferral.h b/strings/base_deferral.h index 4717df6ba..cc6f724e5 100644 --- a/strings/base_deferral.h +++ b/strings/base_deferral.h @@ -1,7 +1,7 @@ +#ifdef WINRT_IMPL_COROUTINES WINRT_EXPORT namespace winrt { -#ifdef __cpp_coroutines template struct deferrable_event_args { @@ -22,9 +22,9 @@ WINRT_EXPORT namespace winrt [[nodiscard]] Windows::Foundation::IAsyncAction wait_for_deferrals() { - struct awaitable : std::experimental::suspend_always + struct awaitable : std::suspend_always { - bool await_suspend(coroutine_handle handle) + bool await_suspend(std::coroutine_handle<> handle) { return m_deferrable.await_suspend(handle); } @@ -37,11 +37,9 @@ WINRT_EXPORT namespace winrt private: - using coroutine_handle = std::experimental::coroutine_handle<>; - void one_deferral_completed() { - coroutine_handle resume = nullptr; + std::coroutine_handle<> resume = nullptr; { slim_lock_guard const guard(m_lock); @@ -62,7 +60,7 @@ WINRT_EXPORT namespace winrt } } - bool await_suspend(coroutine_handle handle) noexcept + bool await_suspend(std::coroutine_handle<> handle) noexcept { slim_lock_guard const guard(m_lock); m_handle = handle; @@ -70,8 +68,8 @@ WINRT_EXPORT namespace winrt } slim_mutex m_lock; - int32_t m_outstanding_deferrals = 0; - coroutine_handle m_handle = nullptr; + std::int32_t m_outstanding_deferrals = 0; + std::coroutine_handle<> m_handle = nullptr; }; -#endif } +#endif diff --git a/strings/base_delegate.h b/strings/base_delegate.h index e3995dd11..1fe00ecd1 100644 --- a/strings/base_delegate.h +++ b/strings/base_delegate.h @@ -1,39 +1,65 @@ -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { - template - struct implements_delegate : abi_t, H, update_module_lock +#if defined(_MSC_VER) +#pragma warning(push) +#pragma warning(disable:4458) // declaration hides class member (okay because we do not use named members of base class) +#endif + + struct implements_delegate_base { - implements_delegate(H&& handler) : H(std::forward(handler)) + WINRT_IMPL_NOINLINE std::uint32_t increment_reference() noexcept { + return ++m_references; } - int32_t __stdcall QueryInterface(guid const& id, void** result) noexcept final + WINRT_IMPL_NOINLINE std::uint32_t decrement_reference() noexcept { - if (is_guid_of(id) || is_guid_of(id) || is_guid_of(id)) + return --m_references; + } + + WINRT_IMPL_NOINLINE std::uint32_t query_interface(guid const& id, void** result, unknown_abi* derivedAbiPtr, guid const& derivedId) noexcept + { + if (id == derivedId || is_guid_of(id) || is_guid_of(id)) { - *result = static_cast*>(this); - AddRef(); + *result = derivedAbiPtr; + increment_reference(); return 0; } if (is_guid_of(id)) { - return make_marshaler(this, result); + return make_marshaler(derivedAbiPtr, result); } *result = nullptr; return error_no_interface; } - uint32_t __stdcall AddRef() noexcept final + private: + atomic_ref_count m_references{ 1 }; + }; + + template + struct implements_delegate : abi_t, implements_delegate_base, H, update_module_lock + { + implements_delegate(H&& handler) : H(std::forward(handler)) + { + } + + std::int32_t __stdcall QueryInterface(guid const& id, void** result) noexcept final { - return ++m_references; + return query_interface(id, result, static_cast*>(this), guid_of()); } - uint32_t __stdcall Release() noexcept final + std::uint32_t __stdcall AddRef() noexcept final { - auto const remaining = --m_references; + return increment_reference(); + } + + std::uint32_t __stdcall Release() noexcept final + { + auto const remaining = decrement_reference(); if (remaining == 0) { @@ -42,10 +68,6 @@ namespace winrt::impl return remaining; } - - private: - - atomic_ref_count m_references{ 1 }; }; template @@ -68,15 +90,16 @@ namespace winrt::impl return delegate; } + const auto id = guid_of(); com_ptr ref; - get_agile_reference(guid_of(), get_abi(delegate), ref.put_void()); + get_agile_reference(id, get_abi(delegate), ref.put_void()); if (ref) { - return [ref = std::move(ref)](auto&& ... args) + return [ref = std::move(ref), id](auto&& ... args) { T delegate; - ref->Resolve(guid_of(), put_abi(delegate)); + ref->Resolve(id, put_abi(delegate)); return delegate(args...); }; } @@ -86,13 +109,13 @@ namespace winrt::impl } template - struct __declspec(novtable) variadic_delegate_abi : unknown_abi + struct WINRT_IMPL_ABI_DECL variadic_delegate_abi : unknown_abi { virtual R invoke(Args const& ...) = 0; }; template - struct variadic_delegate final : variadic_delegate_abi, H, update_module_lock + struct variadic_delegate final : variadic_delegate_abi, implements_delegate_base, H, update_module_lock { variadic_delegate(H&& handler) : H(std::forward(handler)) { @@ -110,27 +133,19 @@ namespace winrt::impl } } - int32_t __stdcall QueryInterface(guid const& id, void** result) noexcept final + std::int32_t __stdcall QueryInterface(guid const& id, void** result) noexcept final { - if (is_guid_of(id) || is_guid_of(id)) - { - *result = static_cast(this); - AddRef(); - return 0; - } - - *result = nullptr; - return error_no_interface; + return query_interface(id, result, static_cast(this), guid_of()); } - uint32_t __stdcall AddRef() noexcept final + std::uint32_t __stdcall AddRef() noexcept final { - return ++m_references; + return increment_reference(); } - uint32_t __stdcall Release() noexcept final + std::uint32_t __stdcall Release() noexcept final { - auto const remaining = --m_references; + auto const remaining = decrement_reference(); if (remaining == 0) { @@ -139,14 +154,10 @@ namespace winrt::impl return remaining; } - - private: - - atomic_ref_count m_references{ 1 }; }; template - struct __declspec(empty_bases) delegate_base : Windows::Foundation::IUnknown + struct WINRT_IMPL_EMPTY_BASES delegate_base : Windows::Foundation::IUnknown { delegate_base(std::nullptr_t = nullptr) noexcept {} delegate_base(void* ptr, take_ownership_from_abi_t) noexcept : IUnknown(ptr, take_ownership_from_abi) {} @@ -157,11 +168,11 @@ namespace winrt::impl {} template delegate_base(F* handler) : - delegate_base([=](auto&& ... args) { handler(args...); }) + delegate_base([=](auto&& ... args) { return handler(args...); }) {} template delegate_base(O* object, M method) : - delegate_base([=](auto&& ... args) { ((*object).*(method))(args...); }) + delegate_base([=](auto&& ... args) { return ((*object).*(method))(args...); }) {} template delegate_base(com_ptr&& object, M method) : @@ -169,8 +180,24 @@ namespace winrt::impl { } - template delegate_base(winrt::weak_ref&& object, M method) : - delegate_base([o = std::move(object), method](auto&& ... args) { if (auto s = o.get()) { ((*s).*(method))(args...); } }) + template delegate_base(winrt::weak_ref&& object, LM&& lambda_or_method) : + delegate_base([o = std::move(object), lm = std::forward(lambda_or_method)](auto&&... args) { if (auto s = o.get()) { + if constexpr (std::is_member_function_pointer_v) ((*s).*(lm))(args...); + else lm(args...); + }}) + { + } + + template delegate_base(std::shared_ptr&& object, M method) : + delegate_base([o = std::move(object), method](auto&& ... args) { return ((*o).*(method))(args...); }) + { + } + + template delegate_base(std::weak_ptr&& object, LM&& lambda_or_method) : + delegate_base([o = std::move(object), lm = std::forward(lambda_or_method)](auto&&... args) { if (auto s = o.lock()) { + if constexpr (std::is_member_function_pointer_v) ((*s).*(lm))(args...); + else lm(args...); + }}) { } @@ -187,18 +214,22 @@ namespace winrt::impl return { static_cast(new variadic_delegate(std::forward(handler))), take_ownership_from_abi }; } }; + +#if defined(_MSC_VER) +#pragma warning(pop) +#endif } WINRT_EXPORT namespace winrt { template - struct __declspec(empty_bases) delegate : impl::delegate_base + struct WINRT_IMPL_EMPTY_BASES delegate : impl::delegate_base { using impl::delegate_base::delegate_base; }; template - struct __declspec(empty_bases) delegate : impl::delegate_base + struct WINRT_IMPL_EMPTY_BASES delegate : impl::delegate_base { using impl::delegate_base::delegate_base; }; diff --git a/strings/base_detect_numerics.h b/strings/base_detect_numerics.h new file mode 100644 index 000000000..6a93a8ffa --- /dev/null +++ b/strings/base_detect_numerics.h @@ -0,0 +1,4 @@ + +#if __has_include() +#define WINRT_IMPL_NUMERICS +#endif diff --git a/strings/base_error.h b/strings/base_error.h index 20a13a6e1..d42ca516b 100644 --- a/strings/base_error.h +++ b/strings/base_error.h @@ -1,5 +1,13 @@ -namespace winrt::impl +#if defined(_MSC_VER) +#define WINRT_IMPL_RETURNADDRESS() _ReturnAddress() +#elif defined(__GNUC__) +#define WINRT_IMPL_RETURNADDRESS() __builtin_extract_return_addr(__builtin_return_address(0)) +#else +#define WINRT_IMPL_RETURNADDRESS() nullptr +#endif + +WINRT_EXPORT namespace winrt::impl { struct heap_traits { @@ -33,11 +41,11 @@ namespace winrt::impl using bstr_handle = handle_type; - inline hstring trim_hresult_message(wchar_t const* const message, uint32_t size) noexcept + inline hstring trim_hresult_message(wchar_t const* const message, std::uint32_t size) noexcept { wchar_t const* back = message + size - 1; - while (size&& iswspace(*back)) + while (size && std::iswspace(*back)) { --size; --back; @@ -46,116 +54,29 @@ namespace winrt::impl return { message, size }; } - constexpr int32_t hresult_from_win32(uint32_t const x) noexcept + inline hstring message_from_hresult(hresult code) noexcept { - return (int32_t)(x) <= 0 ? (int32_t)(x) : (int32_t)(((x) & 0x0000FFFF) | (7 << 16) | 0x80000000); - } + handle_type message; - constexpr int32_t hresult_from_nt(uint32_t const x) noexcept - { - return ((int32_t)((x) | 0x10000000)); + std::uint32_t const size = WINRT_IMPL_FormatMessageW(0x00001300, // FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS + nullptr, + code, + 0x00000400, // MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) + reinterpret_cast(message.put()), + 0, + nullptr); + + return trim_hresult_message(message.get(), size); } - struct error_info_fallback final : IErrorInfo, IRestrictedErrorInfo, update_module_lock + constexpr std::int32_t hresult_from_win32(std::uint32_t const x) noexcept { - error_info_fallback(int32_t code, void* message) noexcept : - m_code(code), - m_message(*reinterpret_cast(&message)) - { - } - - int32_t __stdcall QueryInterface(guid const& id, void** object) noexcept final - { - if (is_guid_of(id) || is_guid_of(id) || is_guid_of(id)) - { - *object = static_cast(this); - AddRef(); - return 0; - } - - if (is_guid_of(id)) - { - *object = static_cast(this); - AddRef(); - return 0; - } - - *object = nullptr; - return error_no_interface; - } - - uint32_t __stdcall AddRef() noexcept final - { - return ++m_references; - } - - uint32_t __stdcall Release() noexcept final - { - auto const remaining = --m_references; - - if (remaining == 0) - { - delete this; - } - - return remaining; - } - - int32_t __stdcall GetGUID(guid* value) noexcept final - { - *value = {}; - return 0; - } - - int32_t __stdcall GetSource(bstr* value) noexcept final - { - *value = nullptr; - return 0; - } - - int32_t __stdcall GetDescription(bstr* value) noexcept final - { - *value = WINRT_IMPL_SysAllocString(m_message.c_str()); - return *value ? error_ok : error_bad_alloc; - } - - int32_t __stdcall GetHelpFile(bstr* value) noexcept final - { - *value = nullptr; - return 0; - } - - int32_t __stdcall GetHelpContext(uint32_t* value) noexcept final - { - *value = 0; - return 0; - } - - int32_t __stdcall GetErrorDetails(bstr* fallback, int32_t* error, bstr* message, bstr* capability) noexcept final - { - *fallback = nullptr; - *error = m_code; - *capability = nullptr; - *message = WINRT_IMPL_SysAllocString(m_message.c_str()); - return *message ? error_ok : error_bad_alloc; - } - - int32_t __stdcall GetReference(bstr* value) noexcept final - { - *value = nullptr; - return 0; - } - - private: - - hresult const m_code; - hstring const m_message; - atomic_ref_count m_references{ 1 }; - }; + return (std::int32_t)(x) <= 0 ? (std::int32_t)(x) : (std::int32_t)(((x) & 0x0000FFFF) | (7 << 16) | 0x80000000); + } - [[noreturn]] inline void __stdcall fallback_RoFailFastWithErrorContext(int32_t) noexcept + constexpr std::int32_t hresult_from_nt(std::uint32_t const x) noexcept { - std::terminate(); + return ((std::int32_t)((x) | 0x10000000)); } } @@ -163,6 +84,9 @@ WINRT_EXPORT namespace winrt { struct hresult_error { + struct no_originate_t {}; + static constexpr no_originate_t no_originate{}; + using from_abi_t = take_ownership_from_abi_t; static constexpr auto from_abi{ take_ownership_from_abi }; @@ -183,17 +107,21 @@ WINRT_EXPORT namespace winrt return *this; } - explicit hresult_error(hresult const code) noexcept : m_code(code) + explicit hresult_error(hresult const code, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : m_code(verify_error(code)) + { + originate(code, nullptr, sourceInformation); + } + + explicit hresult_error(hresult const code, no_originate_t) noexcept : m_code(verify_error(code)) { - originate(code, nullptr); } - hresult_error(hresult const code, param::hstring const& message) noexcept : m_code(code) + hresult_error(hresult const code, param::hstring const& message, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : m_code(verify_error(code)) { - originate(code, get_abi(message)); + originate(code, get_abi(message), sourceInformation); } - hresult_error(hresult const code, take_ownership_from_abi_t) noexcept : m_code(code) + hresult_error(hresult const code, take_ownership_from_abi_t, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : m_code(verify_error(code)) { com_ptr info; WINRT_IMPL_GetErrorInfo(0, info.put_void()); @@ -223,7 +151,7 @@ WINRT_EXPORT namespace winrt message = impl::trim_hresult_message(legacy.get(), WINRT_IMPL_SysStringLen(legacy.get())); } - originate(code, get_abi(message)); + originate(code, get_abi(message), sourceInformation); } } @@ -236,7 +164,7 @@ WINRT_EXPORT namespace winrt { if (m_info) { - int32_t code{}; + std::int32_t code{}; impl::bstr_handle fallback; impl::bstr_handle message; impl::bstr_handle unused; @@ -257,17 +185,7 @@ WINRT_EXPORT namespace winrt } } - handle_type message; - - uint32_t const size = WINRT_IMPL_FormatMessageW(0x00001300, // FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS - nullptr, - m_code, - 0x00000400, // MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT) - reinterpret_cast(message.put()), - 0, - nullptr); - - return impl::trim_hresult_message(message.get(), size); + return impl::message_from_hresult(m_code); } template @@ -288,31 +206,37 @@ WINRT_EXPORT namespace winrt private: - static int32_t __stdcall fallback_RoOriginateLanguageException(int32_t error, void* message, void*) noexcept + void originate(hresult const code, void* message, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept { - com_ptr info(new (std::nothrow) impl::error_info_fallback(error, message), take_ownership_from_abi); - WINRT_VERIFY_(0, WINRT_IMPL_SetErrorInfo(0, info.get())); - return 1; - } + WINRT_VERIFY(WINRT_IMPL_RoOriginateLanguageException(code, message, nullptr)); - void originate(hresult const code, void* message) noexcept - { - static int32_t(__stdcall* handler)(int32_t error, void* message, void* exception) noexcept; - impl::load_runtime_function("RoOriginateLanguageException", handler, fallback_RoOriginateLanguageException); - WINRT_VERIFY(handler(code, message, nullptr)); + // This is an extension point that can be filled in by other libraries (such as WIL) to get call outs when errors are + // originated. This is intended for logging purposes. When possible include the std::source_information so that accurate + // information is available on the caller who generated the error. + if (winrt_throw_hresult_handler) + { + winrt_throw_hresult_handler(sourceInformation.line(), sourceInformation.file_name(), sourceInformation.function_name(), WINRT_IMPL_RETURNADDRESS(), code); + } com_ptr info; - WINRT_VERIFY_(0, WINRT_IMPL_GetErrorInfo(0, info.put_void())); - WINRT_VERIFY(info.try_as(m_info)); + WINRT_IMPL_GetErrorInfo(0, info.put_void()); + info.try_as(m_info); + } + + static hresult verify_error(hresult const code) noexcept + { + WINRT_ASSERT(code < 0); + return code; } + #ifdef __clang__ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-private-field" #endif impl::bstr_handle m_debug_reference; - uint32_t m_debug_magic{ 0xAABBCCDD }; + std::uint32_t m_debug_magic{ 0xAABBCCDD }; hresult m_code{ impl::error_fail }; com_ptr m_info; @@ -323,97 +247,103 @@ WINRT_EXPORT namespace winrt struct hresult_access_denied : hresult_error { - hresult_access_denied() noexcept : hresult_error(impl::error_access_denied) {} - hresult_access_denied(param::hstring const& message) noexcept : hresult_error(impl::error_access_denied, message) {} - hresult_access_denied(take_ownership_from_abi_t) noexcept : hresult_error(impl::error_access_denied, take_ownership_from_abi) {} + hresult_access_denied(winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_access_denied, sourceInformation) {} + hresult_access_denied(param::hstring const& message, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_access_denied, message, sourceInformation) {} + hresult_access_denied(take_ownership_from_abi_t, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_access_denied, take_ownership_from_abi, sourceInformation) {} }; struct hresult_wrong_thread : hresult_error { - hresult_wrong_thread() noexcept : hresult_error(impl::error_wrong_thread) {} - hresult_wrong_thread(param::hstring const& message) noexcept : hresult_error(impl::error_wrong_thread, message) {} - hresult_wrong_thread(take_ownership_from_abi_t) noexcept : hresult_error(impl::error_wrong_thread, take_ownership_from_abi) {} + hresult_wrong_thread(winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_wrong_thread, sourceInformation) {} + hresult_wrong_thread(param::hstring const& message, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_wrong_thread, message, sourceInformation) {} + hresult_wrong_thread(take_ownership_from_abi_t, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_wrong_thread, take_ownership_from_abi, sourceInformation) {} }; struct hresult_not_implemented : hresult_error { - hresult_not_implemented() noexcept : hresult_error(impl::error_not_implemented) {} - hresult_not_implemented(param::hstring const& message) noexcept : hresult_error(impl::error_not_implemented, message) {} - hresult_not_implemented(take_ownership_from_abi_t) noexcept : hresult_error(impl::error_not_implemented, take_ownership_from_abi) {} + hresult_not_implemented(winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_not_implemented, sourceInformation) {} + hresult_not_implemented(param::hstring const& message, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_not_implemented, message, sourceInformation) {} + hresult_not_implemented(take_ownership_from_abi_t, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_not_implemented, take_ownership_from_abi, sourceInformation) {} }; struct hresult_invalid_argument : hresult_error { - hresult_invalid_argument() noexcept : hresult_error(impl::error_invalid_argument) {} - hresult_invalid_argument(param::hstring const& message) noexcept : hresult_error(impl::error_invalid_argument, message) {} - hresult_invalid_argument(take_ownership_from_abi_t) noexcept : hresult_error(impl::error_invalid_argument, take_ownership_from_abi) {} + hresult_invalid_argument(winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_invalid_argument, sourceInformation) {} + hresult_invalid_argument(param::hstring const& message, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_invalid_argument, message, sourceInformation) {} + hresult_invalid_argument(take_ownership_from_abi_t, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_invalid_argument, take_ownership_from_abi, sourceInformation) {} }; struct hresult_out_of_bounds : hresult_error { - hresult_out_of_bounds() noexcept : hresult_error(impl::error_out_of_bounds) {} - hresult_out_of_bounds(param::hstring const& message) noexcept : hresult_error(impl::error_out_of_bounds, message) {} - hresult_out_of_bounds(take_ownership_from_abi_t) noexcept : hresult_error(impl::error_out_of_bounds, take_ownership_from_abi) {} + hresult_out_of_bounds(winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_out_of_bounds, sourceInformation) {} + hresult_out_of_bounds(param::hstring const& message, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_out_of_bounds, message, sourceInformation) {} + hresult_out_of_bounds(take_ownership_from_abi_t, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_out_of_bounds, take_ownership_from_abi, sourceInformation) {} }; struct hresult_no_interface : hresult_error { - hresult_no_interface() noexcept : hresult_error(impl::error_no_interface) {} - hresult_no_interface(param::hstring const& message) noexcept : hresult_error(impl::error_no_interface, message) {} - hresult_no_interface(take_ownership_from_abi_t) noexcept : hresult_error(impl::error_no_interface, take_ownership_from_abi) {} + hresult_no_interface(winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_no_interface, sourceInformation) {} + hresult_no_interface(param::hstring const& message, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_no_interface, message, sourceInformation) {} + hresult_no_interface(take_ownership_from_abi_t, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_no_interface, take_ownership_from_abi, sourceInformation) {} }; struct hresult_class_not_available : hresult_error { - hresult_class_not_available() noexcept : hresult_error(impl::error_class_not_available) {} - hresult_class_not_available(param::hstring const& message) noexcept : hresult_error(impl::error_class_not_available, message) {} - hresult_class_not_available(take_ownership_from_abi_t) noexcept : hresult_error(impl::error_class_not_available, take_ownership_from_abi) {} + hresult_class_not_available(winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_class_not_available, sourceInformation) {} + hresult_class_not_available(param::hstring const& message, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_class_not_available, message, sourceInformation) {} + hresult_class_not_available(take_ownership_from_abi_t, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_class_not_available, take_ownership_from_abi, sourceInformation) {} }; struct hresult_class_not_registered : hresult_error { - hresult_class_not_registered() noexcept : hresult_error(impl::error_class_not_registered) {} - hresult_class_not_registered(param::hstring const& message) noexcept : hresult_error(impl::error_class_not_registered, message) {} - hresult_class_not_registered(take_ownership_from_abi_t) noexcept : hresult_error(impl::error_class_not_registered, take_ownership_from_abi) {} + hresult_class_not_registered(winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_class_not_registered, sourceInformation) {} + hresult_class_not_registered(param::hstring const& message, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_class_not_registered, message, sourceInformation) {} + hresult_class_not_registered(take_ownership_from_abi_t, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_class_not_registered, take_ownership_from_abi, sourceInformation) {} }; struct hresult_changed_state : hresult_error { - hresult_changed_state() noexcept : hresult_error(impl::error_changed_state) {} - hresult_changed_state(param::hstring const& message) noexcept : hresult_error(impl::error_changed_state, message) {} - hresult_changed_state(take_ownership_from_abi_t) noexcept : hresult_error(impl::error_changed_state, take_ownership_from_abi) {} + hresult_changed_state(winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_changed_state, sourceInformation) {} + hresult_changed_state(param::hstring const& message, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_changed_state, message, sourceInformation) {} + hresult_changed_state(take_ownership_from_abi_t, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_changed_state, take_ownership_from_abi, sourceInformation) {} }; struct hresult_illegal_method_call : hresult_error { - hresult_illegal_method_call() noexcept : hresult_error(impl::error_illegal_method_call) {} - hresult_illegal_method_call(param::hstring const& message) noexcept : hresult_error(impl::error_illegal_method_call, message) {} - hresult_illegal_method_call(take_ownership_from_abi_t) noexcept : hresult_error(impl::error_illegal_method_call, take_ownership_from_abi) {} + hresult_illegal_method_call(winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_illegal_method_call, sourceInformation) {} + hresult_illegal_method_call(param::hstring const& message, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_illegal_method_call, message, sourceInformation) {} + hresult_illegal_method_call(take_ownership_from_abi_t, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_illegal_method_call, take_ownership_from_abi, sourceInformation) {} }; struct hresult_illegal_state_change : hresult_error { - hresult_illegal_state_change() noexcept : hresult_error(impl::error_illegal_state_change) {} - hresult_illegal_state_change(param::hstring const& message) noexcept : hresult_error(impl::error_illegal_state_change, message) {} - hresult_illegal_state_change(take_ownership_from_abi_t) noexcept : hresult_error(impl::error_illegal_state_change, take_ownership_from_abi) {} + hresult_illegal_state_change(winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_illegal_state_change, sourceInformation) {} + hresult_illegal_state_change(param::hstring const& message, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_illegal_state_change, message, sourceInformation) {} + hresult_illegal_state_change(take_ownership_from_abi_t, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_illegal_state_change, take_ownership_from_abi, sourceInformation) {} }; struct hresult_illegal_delegate_assignment : hresult_error { - hresult_illegal_delegate_assignment() noexcept : hresult_error(impl::error_illegal_delegate_assignment) {} - hresult_illegal_delegate_assignment(param::hstring const& message) noexcept : hresult_error(impl::error_illegal_delegate_assignment, message) {} - hresult_illegal_delegate_assignment(take_ownership_from_abi_t) noexcept : hresult_error(impl::error_illegal_delegate_assignment, take_ownership_from_abi) {} + hresult_illegal_delegate_assignment(winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_illegal_delegate_assignment, sourceInformation) {} + hresult_illegal_delegate_assignment(param::hstring const& message, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_illegal_delegate_assignment, message, sourceInformation) {} + hresult_illegal_delegate_assignment(take_ownership_from_abi_t, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_illegal_delegate_assignment, take_ownership_from_abi, sourceInformation) {} }; struct hresult_canceled : hresult_error { - hresult_canceled() noexcept : hresult_error(impl::error_canceled) {} - hresult_canceled(param::hstring const& message) noexcept : hresult_error(impl::error_canceled, message) {} - hresult_canceled(take_ownership_from_abi_t) noexcept : hresult_error(impl::error_canceled, take_ownership_from_abi) {} + hresult_canceled(winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_canceled, sourceInformation) {} + hresult_canceled(hresult_error::no_originate_t) noexcept : hresult_error(impl::error_canceled, hresult_error::no_originate) {} + hresult_canceled(param::hstring const& message, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_canceled, message, sourceInformation) {} + hresult_canceled(take_ownership_from_abi_t, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) noexcept : hresult_error(impl::error_canceled, take_ownership_from_abi, sourceInformation) {} }; - [[noreturn]] inline __declspec(noinline) void throw_hresult(hresult const result) + [[noreturn]] inline WINRT_IMPL_NOINLINE void throw_hresult(hresult const result, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) { + if (winrt_throw_hresult_handler) + { + winrt_throw_hresult_handler(sourceInformation.line(), sourceInformation.file_name(), sourceInformation.function_name(), WINRT_IMPL_RETURNADDRESS(), result); + } + if (result == impl::error_bad_alloc) { throw std::bad_alloc(); @@ -421,77 +351,77 @@ WINRT_EXPORT namespace winrt if (result == impl::error_access_denied) { - throw hresult_access_denied(take_ownership_from_abi); + throw hresult_access_denied(take_ownership_from_abi, sourceInformation); } if (result == impl::error_wrong_thread) { - throw hresult_wrong_thread(take_ownership_from_abi); + throw hresult_wrong_thread(take_ownership_from_abi, sourceInformation); } if (result == impl::error_not_implemented) { - throw hresult_not_implemented(take_ownership_from_abi); + throw hresult_not_implemented(take_ownership_from_abi, sourceInformation); } if (result == impl::error_invalid_argument) { - throw hresult_invalid_argument(take_ownership_from_abi); + throw hresult_invalid_argument(take_ownership_from_abi, sourceInformation); } if (result == impl::error_out_of_bounds) { - throw hresult_out_of_bounds(take_ownership_from_abi); + throw hresult_out_of_bounds(take_ownership_from_abi, sourceInformation); } if (result == impl::error_no_interface) { - throw hresult_no_interface(take_ownership_from_abi); + throw hresult_no_interface(take_ownership_from_abi, sourceInformation); } if (result == impl::error_class_not_available) { - throw hresult_class_not_available(take_ownership_from_abi); + throw hresult_class_not_available(take_ownership_from_abi, sourceInformation); } if (result == impl::error_class_not_registered) { - throw hresult_class_not_registered(take_ownership_from_abi); + throw hresult_class_not_registered(take_ownership_from_abi, sourceInformation); } if (result == impl::error_changed_state) { - throw hresult_changed_state(take_ownership_from_abi); + throw hresult_changed_state(take_ownership_from_abi, sourceInformation); } if (result == impl::error_illegal_method_call) { - throw hresult_illegal_method_call(take_ownership_from_abi); + throw hresult_illegal_method_call(take_ownership_from_abi, sourceInformation); } if (result == impl::error_illegal_state_change) { - throw hresult_illegal_state_change(take_ownership_from_abi); + throw hresult_illegal_state_change(take_ownership_from_abi, sourceInformation); } if (result == impl::error_illegal_delegate_assignment) { - throw hresult_illegal_delegate_assignment(take_ownership_from_abi); + throw hresult_illegal_delegate_assignment(take_ownership_from_abi, sourceInformation); } if (result == impl::error_canceled) { - throw hresult_canceled(take_ownership_from_abi); + throw hresult_canceled(take_ownership_from_abi, sourceInformation); } - throw hresult_error(result, take_ownership_from_abi); + throw hresult_error(result, take_ownership_from_abi, sourceInformation); } - inline __declspec(noinline) hresult to_hresult() noexcept + inline WINRT_IMPL_NOINLINE hresult to_hresult() noexcept { if (winrt_to_hresult_handler) { - return winrt_to_hresult_handler(_ReturnAddress()); + return winrt_to_hresult_handler(WINRT_IMPL_RETURNADDRESS()); } try @@ -520,52 +450,80 @@ WINRT_EXPORT namespace winrt } } - [[noreturn]] inline void throw_last_error() + inline WINRT_IMPL_NOINLINE hstring to_message() { - throw_hresult(impl::hresult_from_win32(WINRT_IMPL_GetLastError())); + if (winrt_to_message_handler) + { + return winrt_to_message_handler(WINRT_IMPL_RETURNADDRESS()); + } + + try + { + throw; + } + catch (hresult_error const& e) + { + return e.message(); + } + catch (std::exception const& ex) + { + return to_hstring(ex.what()); + } + catch (...) + { + std::abort(); + } } - inline void check_hresult(hresult const result) + [[noreturn]] inline void throw_last_error(winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) + { + throw_hresult(impl::hresult_from_win32(WINRT_IMPL_GetLastError()), sourceInformation); + } + + inline hresult check_hresult(hresult const result, winrt::impl::slim_source_location const& sourceInformation) { if (result < 0) { - throw_hresult(result); + throw_hresult(result, sourceInformation); } + return result; } template - void check_nt(T result) + void check_nt(T result, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) { if (result != 0) { - throw_hresult(impl::hresult_from_nt(result)); + throw_hresult(impl::hresult_from_nt(result), sourceInformation); } } template - void check_win32(T result) + void check_win32(T result, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) { if (result != 0) { - throw_hresult(impl::hresult_from_win32(result)); + throw_hresult(impl::hresult_from_win32(result), sourceInformation); } } template - void check_bool(T result) + T check_bool(T result, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) { if (!result) { - winrt::throw_last_error(); + winrt::throw_last_error(sourceInformation); } + + return result; } template - T* check_pointer(T* pointer) + T* check_pointer(T* pointer, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) { if (!pointer) { - throw_last_error(); + throw_last_error(sourceInformation); } return pointer; @@ -573,9 +531,21 @@ WINRT_EXPORT namespace winrt [[noreturn]] inline void terminate() noexcept { - static void(__stdcall * handler)(int32_t) noexcept; - impl::load_runtime_function("RoFailFastWithErrorContext", handler, impl::fallback_RoFailFastWithErrorContext); - handler(to_hresult()); - abort(); + WINRT_IMPL_RoFailFastWithErrorContext(to_hresult()); + std::abort(); } } + +WINRT_EXPORT namespace winrt::impl +{ + inline hresult check_hresult_allow_bounds(hresult const result, winrt::impl::slim_source_location const& sourceInformation = winrt::impl::slim_source_location::current()) + { + if (result != impl::error_out_of_bounds && result != impl::error_fail && result != impl::error_file_not_found) + { + check_hresult(result, sourceInformation); + } + return result; + } +} + +#undef WINRT_IMPL_RETURNADDRESS diff --git a/strings/base_events.h b/strings/base_events.h index d757f663d..c21124e9c 100644 --- a/strings/base_events.h +++ b/strings/base_events.h @@ -3,7 +3,7 @@ WINRT_EXPORT namespace winrt { struct event_token { - int64_t value{}; + std::int64_t value{}; explicit operator bool() const noexcept { @@ -22,7 +22,7 @@ WINRT_EXPORT namespace winrt template struct event_revoker { - using method_type = int32_t(__stdcall impl::abi_t::*)(winrt::event_token); + using method_type = std::int32_t(__stdcall impl::abi_t::*)(winrt::event_token); event_revoker() noexcept = default; event_revoker(event_revoker const&) = delete; @@ -77,7 +77,7 @@ WINRT_EXPORT namespace winrt template struct factory_event_revoker { - using method_type = int32_t(__stdcall impl::abi_t::*)(winrt::event_token); + using method_type = std::int32_t(__stdcall impl::abi_t::*)(winrt::event_token); factory_event_revoker() noexcept = default; factory_event_revoker(factory_event_revoker const&) = delete; @@ -130,7 +130,7 @@ WINRT_EXPORT namespace winrt }; } -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { template struct event_revoker @@ -267,7 +267,7 @@ namespace winrt::impl using pointer = value_type*; using iterator = value_type*; - explicit event_array(uint32_t const count) noexcept : m_size(count) + explicit event_array(std::uint32_t const count) noexcept : m_size(count) { std::uninitialized_fill_n(data(), count, value_type()); } @@ -306,7 +306,7 @@ namespace winrt::impl return data() + m_size; } - uint32_t size() const noexcept + std::uint32_t size() const noexcept { return m_size; } @@ -324,20 +324,32 @@ namespace winrt::impl } atomic_ref_count m_references{ 1 }; - uint32_t m_size{ 0 }; + std::uint32_t m_size{ 0 }; }; template - com_ptr> make_event_array(uint32_t const capacity) + com_ptr> make_event_array(std::uint32_t const capacity) { void* raw = ::operator new(sizeof(event_array) + (sizeof(T)* capacity)); +#ifdef _MSC_VER #pragma warning(suppress: 6386) +#endif return { new(raw) event_array(capacity), take_ownership_from_abi }; } - inline int32_t __stdcall fallback_RoTransformError(int32_t, int32_t, void*) noexcept + WINRT_IMPL_NOINLINE inline bool report_failed_invoke() { - return 1; + std::int32_t const code = to_hresult(); + WINRT_IMPL_RoTransformError(code, 0, nullptr); + + if (code == static_cast(0x80010108) || // RPC_E_DISCONNECTED + code == static_cast(0x800706BA) || // HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE) + code == static_cast(0x89020001)) // JSCRIPT_E_CANTEXECUTE + { + return false; + } + + return true; } template @@ -349,18 +361,7 @@ namespace winrt::impl } catch (...) { - int32_t const code = to_hresult(); - - static int32_t(__stdcall * handler)(int32_t, int32_t, void*) noexcept; - impl::load_runtime_function("RoTransformError", handler, fallback_RoTransformError); - handler(code, 0, nullptr); - - if (code == static_cast(0x80010108) || // RPC_E_DISCONNECTED - code == static_cast(0x800706BA) || // HRESULT_FROM_WIN32(RPC_S_SERVER_UNAVAILABLE) - code == static_cast(0x89020001)) // JSCRIPT_E_CANTEXECUTE - { - return false; - } + return report_failed_invoke(); } return true; @@ -375,8 +376,8 @@ WINRT_EXPORT namespace winrt using delegate_type = Delegate; event() = default; - event(event const&) = delete; - event& operator =(event const&) = delete; + event(event const&) = delete; + event& operator =(event const&) = delete; explicit operator bool() const noexcept { @@ -385,28 +386,7 @@ WINRT_EXPORT namespace winrt event_token add(delegate_type const& delegate) { - event_token token{}; - - // Extends life of old targets array to release delegates outside of lock. - delegate_array temp_targets; - - { - slim_lock_guard const change_guard(m_change); - delegate_array new_targets = impl::make_event_array((!m_targets) ? 1 : m_targets->size() + 1); - - if (m_targets) - { - std::copy_n(m_targets->begin(), m_targets->size(), new_targets->begin()); - } - - new_targets->back() = impl::make_agile_delegate(delegate); - token = get_token(new_targets->back()); - - slim_lock_guard const swap_guard(m_swap); - temp_targets = std::exchange(m_targets, std::move(new_targets)); - } - - return token; + return add_agile(impl::make_agile_delegate(delegate)); } void remove(event_token const token) @@ -422,7 +402,7 @@ WINRT_EXPORT namespace winrt return; } - uint32_t available_slots = m_targets->size() - 1; + std::uint32_t available_slots = m_targets->size() - 1; delegate_array new_targets; bool removed = false; @@ -466,6 +446,24 @@ WINRT_EXPORT namespace winrt } } + void clear() + { + // Extends life of old targets array to release delegates outside of lock. + delegate_array temp_targets; + + { + slim_lock_guard const change_guard(m_change); + + if (!m_targets) + { + return; + } + + slim_lock_guard const swap_guard(m_swap); + temp_targets = std::exchange(m_targets, nullptr); + } + } + template void operator()(Arg const&... args) { @@ -490,9 +488,35 @@ WINRT_EXPORT namespace winrt private: + WINRT_IMPL_NOINLINE event_token add_agile(delegate_type delegate) + { + event_token token{}; + + // Extends life of old targets array to release delegates outside of lock. + delegate_array temp_targets; + + { + slim_lock_guard const change_guard(m_change); + delegate_array new_targets = impl::make_event_array((!m_targets) ? 1 : m_targets->size() + 1); + + if (m_targets) + { + std::copy_n(m_targets->begin(), m_targets->size(), new_targets->begin()); + } + + new_targets->back() = std::move(delegate); + token = get_token(new_targets->back()); + + slim_lock_guard const swap_guard(m_swap); + temp_targets = std::exchange(m_targets, std::move(new_targets)); + } + + return token; + } + event_token get_token(delegate_type const& delegate) const noexcept { - return event_token{ reinterpret_cast(WINRT_IMPL_EncodePointer(get_abi(delegate))) }; + return event_token{ reinterpret_cast(WINRT_IMPL_EncodePointer(get_abi(delegate))) }; } using delegate_array = com_ptr>; diff --git a/strings/base_extern.h b/strings/base_extern.h index 59e89b610..a17908ac4 100644 --- a/strings/base_extern.h +++ b/strings/base_extern.h @@ -1,86 +1,13 @@ -__declspec(selectany) int32_t(__stdcall* winrt_to_hresult_handler)(void* address) noexcept {}; -__declspec(selectany) void(__stdcall* winrt_suspend_handler)(void const* token) noexcept {}; -__declspec(selectany) void(__stdcall* winrt_resume_handler)(void const* token) noexcept {}; -__declspec(selectany) int32_t(__stdcall* winrt_activation_handler)(void* classId, winrt::guid const& iid, void** factory) noexcept {}; - -extern "C" -{ - void* __stdcall WINRT_IMPL_LoadLibraryW(wchar_t const* name) noexcept; - int32_t __stdcall WINRT_IMPL_FreeLibrary(void* library) noexcept; - void* __stdcall WINRT_IMPL_GetProcAddress(void* library, char const* name) noexcept; - - int32_t __stdcall WINRT_IMPL_SetErrorInfo(uint32_t reserved, void* info) noexcept; - int32_t __stdcall WINRT_IMPL_GetErrorInfo(uint32_t reserved, void** info) noexcept; - int32_t __stdcall WINRT_IMPL_CoInitializeEx(void*, uint32_t type) noexcept; - void __stdcall WINRT_IMPL_CoUninitialize() noexcept; - - int32_t __stdcall WINRT_IMPL_CoCreateFreeThreadedMarshaler(void* outer, void** marshaler) noexcept; - int32_t __stdcall WINRT_IMPL_CoCreateInstance(winrt::guid const& clsid, void* outer, uint32_t context, winrt::guid const& iid, void** object) noexcept; - int32_t __stdcall WINRT_IMPL_CoGetCallContext(winrt::guid const& iid, void** object) noexcept; - int32_t __stdcall WINRT_IMPL_CoGetObjectContext(winrt::guid const& iid, void** object) noexcept; - int32_t __stdcall WINRT_IMPL_CoGetApartmentType(int32_t* type, int32_t* qualifier) noexcept; - void* __stdcall WINRT_IMPL_CoTaskMemAlloc(std::size_t size) noexcept; - void __stdcall WINRT_IMPL_CoTaskMemFree(void* ptr) noexcept; - winrt::impl::bstr __stdcall WINRT_IMPL_SysAllocString(wchar_t const* value) noexcept; - void __stdcall WINRT_IMPL_SysFreeString(winrt::impl::bstr string) noexcept; - uint32_t __stdcall WINRT_IMPL_SysStringLen(winrt::impl::bstr string) noexcept; - int32_t __stdcall WINRT_IMPL_IIDFromString(wchar_t const* string, winrt::guid* iid) noexcept; - int32_t __stdcall WINRT_IMPL_MultiByteToWideChar(uint32_t codepage, uint32_t flags, char const* in_string, int32_t in_size, wchar_t* out_string, int32_t out_size) noexcept; - int32_t __stdcall WINRT_IMPL_WideCharToMultiByte(uint32_t codepage, uint32_t flags, wchar_t const* int_string, int32_t in_size, char* out_string, int32_t out_size, char const* default_char, int32_t* default_used) noexcept; - void* __stdcall WINRT_IMPL_HeapAlloc(void* heap, uint32_t flags, size_t bytes) noexcept; - int32_t __stdcall WINRT_IMPL_HeapFree(void* heap, uint32_t flags, void* value) noexcept; - void* __stdcall WINRT_IMPL_GetProcessHeap() noexcept; - uint32_t __stdcall WINRT_IMPL_FormatMessageW(uint32_t flags, void const* source, uint32_t code, uint32_t language, wchar_t* buffer, uint32_t size, va_list* arguments) noexcept; - uint32_t __stdcall WINRT_IMPL_GetLastError() noexcept; - void __stdcall WINRT_IMPL_GetSystemTimePreciseAsFileTime(void* result) noexcept; - uintptr_t __stdcall WINRT_IMPL_VirtualQuery(void* address, void* buffer, uintptr_t length) noexcept; - void* __stdcall WINRT_IMPL_EncodePointer(void* ptr) noexcept; - - int32_t __stdcall WINRT_IMPL_OpenProcessToken(void* process, uint32_t access, void** token) noexcept; - void* __stdcall WINRT_IMPL_GetCurrentProcess() noexcept; - int32_t __stdcall WINRT_IMPL_DuplicateToken(void* existing, uint32_t level, void** duplicate) noexcept; - int32_t __stdcall WINRT_IMPL_OpenThreadToken(void* thread, uint32_t access, int32_t self, void** token) noexcept; - void* __stdcall WINRT_IMPL_GetCurrentThread() noexcept; - int32_t __stdcall WINRT_IMPL_SetThreadToken(void** thread, void* token) noexcept; - - void __stdcall WINRT_IMPL_AcquireSRWLockExclusive(winrt::impl::srwlock* lock) noexcept; - void __stdcall WINRT_IMPL_AcquireSRWLockShared(winrt::impl::srwlock* lock) noexcept; - uint8_t __stdcall WINRT_IMPL_TryAcquireSRWLockExclusive(winrt::impl::srwlock* lock) noexcept; - uint8_t __stdcall WINRT_IMPL_TryAcquireSRWLockShared(winrt::impl::srwlock* lock) noexcept; - void __stdcall WINRT_IMPL_ReleaseSRWLockExclusive(winrt::impl::srwlock* lock) noexcept; - void __stdcall WINRT_IMPL_ReleaseSRWLockShared(winrt::impl::srwlock* lock) noexcept; - int32_t __stdcall WINRT_IMPL_SleepConditionVariableSRW(winrt::impl::condition_variable* cv, winrt::impl::srwlock* lock, uint32_t milliseconds, uint32_t flags) noexcept; - void __stdcall WINRT_IMPL_WakeConditionVariable(winrt::impl::condition_variable* cv) noexcept; - void __stdcall WINRT_IMPL_WakeAllConditionVariable(winrt::impl::condition_variable* cv) noexcept; - void* __stdcall WINRT_IMPL_InterlockedPushEntrySList(void* head, void* entry) noexcept; - void* __stdcall WINRT_IMPL_InterlockedFlushSList(void* head) noexcept; - - void* __stdcall WINRT_IMPL_CreateEventW(void*, int32_t, int32_t, void*) noexcept; - int32_t __stdcall WINRT_IMPL_SetEvent(void*) noexcept; - int32_t __stdcall WINRT_IMPL_CloseHandle(void* hObject) noexcept; - uint32_t __stdcall WINRT_IMPL_WaitForSingleObject(void* handle, uint32_t milliseconds) noexcept; - - int32_t __stdcall WINRT_IMPL_TrySubmitThreadpoolCallback(void(__stdcall *callback)(void*, void* context), void* context, void*) noexcept; - winrt::impl::ptp_timer __stdcall WINRT_IMPL_CreateThreadpoolTimer(void(__stdcall *callback)(void*, void* context, void*), void* context, void*) noexcept; - void __stdcall WINRT_IMPL_SetThreadpoolTimer(winrt::impl::ptp_timer timer, void* time, uint32_t period, uint32_t window) noexcept; - void __stdcall WINRT_IMPL_CloseThreadpoolTimer(winrt::impl::ptp_timer timer) noexcept; - winrt::impl::ptp_wait __stdcall WINRT_IMPL_CreateThreadpoolWait(void(__stdcall *callback)(void*, void* context, void*, uint32_t result), void* context, void*) noexcept; - void __stdcall WINRT_IMPL_SetThreadpoolWait(winrt::impl::ptp_wait wait, void* handle, void* timeout) noexcept; - void __stdcall WINRT_IMPL_CloseThreadpoolWait(winrt::impl::ptp_wait wait) noexcept; - winrt::impl::ptp_io __stdcall WINRT_IMPL_CreateThreadpoolIo(void* object, void(__stdcall *callback)(void*, void* context, void* overlapped, uint32_t result, std::size_t bytes, void*) noexcept, void* context, void*) noexcept; - void __stdcall WINRT_IMPL_StartThreadpoolIo(winrt::impl::ptp_io io) noexcept; - void __stdcall WINRT_IMPL_CancelThreadpoolIo(winrt::impl::ptp_io io) noexcept; - void __stdcall WINRT_IMPL_CloseThreadpoolIo(winrt::impl::ptp_io io) noexcept; - winrt::impl::ptp_pool __stdcall WINRT_IMPL_CreateThreadpool(void* reserved) noexcept; - void __stdcall WINRT_IMPL_SetThreadpoolThreadMaximum(winrt::impl::ptp_pool pool, uint32_t value) noexcept; - int32_t __stdcall WINRT_IMPL_SetThreadpoolThreadMinimum(winrt::impl::ptp_pool pool, uint32_t value) noexcept; - void __stdcall WINRT_IMPL_CloseThreadpool(winrt::impl::ptp_pool pool) noexcept; - - int32_t __stdcall WINRT_CanUnloadNow() noexcept; - int32_t __stdcall WINRT_GetActivationFactory(void* classId, void** factory) noexcept; -} - +// These global function pointers must use WINRT_EXPORT (which expands to +// 'export extern "C++"' in module builds) so that module and non-module TUs +// in the same binary share the same instances. +WINRT_EXPORT __declspec(selectany) std::int32_t(__stdcall* winrt_to_hresult_handler)(void* address) noexcept {}; +WINRT_EXPORT __declspec(selectany) winrt::hstring(__stdcall* winrt_to_message_handler)(void* address) {}; +WINRT_EXPORT __declspec(selectany) void(__stdcall* winrt_throw_hresult_handler)(std::uint32_t lineNumber, char const* fileName, char const* functionName, void* returnAddress, winrt::hresult const result) noexcept {}; +WINRT_EXPORT __declspec(selectany) std::int32_t(__stdcall* winrt_activation_handler)(void* classId, winrt::guid const& iid, void** factory) noexcept {}; + +#if defined(_MSC_VER) #ifdef _M_HYBRID #define WINRT_IMPL_LINK(function, count) __pragma(comment(linker, "/alternatename:#WINRT_IMPL_" #function "@" #count "=#" #function "@" #count)) #elif _M_ARM64EC @@ -90,75 +17,98 @@ extern "C" #else #define WINRT_IMPL_LINK(function, count) __pragma(comment(linker, "/alternatename:WINRT_IMPL_" #function "=" #function)) #endif +#elif defined(__GNUC__) +#if defined(__i386__) +#define WINRT_IMPL_LINK(function, count) __asm__("_" #function "@" #count) +#else +#define WINRT_IMPL_LINK(function, count) __asm__(#function) +#endif +#endif -WINRT_IMPL_LINK(LoadLibraryW, 4) -WINRT_IMPL_LINK(FreeLibrary, 4) -WINRT_IMPL_LINK(GetProcAddress, 8) -WINRT_IMPL_LINK(SetErrorInfo, 8) -WINRT_IMPL_LINK(GetErrorInfo, 8) -WINRT_IMPL_LINK(CoInitializeEx, 8) -WINRT_IMPL_LINK(CoUninitialize, 0) - -WINRT_IMPL_LINK(CoCreateFreeThreadedMarshaler, 8) -WINRT_IMPL_LINK(CoCreateInstance, 20) -WINRT_IMPL_LINK(CoGetCallContext, 8) -WINRT_IMPL_LINK(CoGetObjectContext, 8) -WINRT_IMPL_LINK(CoGetApartmentType, 8) -WINRT_IMPL_LINK(CoTaskMemAlloc, 4) -WINRT_IMPL_LINK(CoTaskMemFree, 4) -WINRT_IMPL_LINK(SysAllocString, 4) -WINRT_IMPL_LINK(SysFreeString, 4) -WINRT_IMPL_LINK(SysStringLen, 4) -WINRT_IMPL_LINK(IIDFromString, 8) -WINRT_IMPL_LINK(MultiByteToWideChar, 24) -WINRT_IMPL_LINK(WideCharToMultiByte, 32) -WINRT_IMPL_LINK(HeapAlloc, 12) -WINRT_IMPL_LINK(HeapFree, 12) -WINRT_IMPL_LINK(GetProcessHeap, 0) -WINRT_IMPL_LINK(FormatMessageW, 28) -WINRT_IMPL_LINK(GetLastError, 0) -WINRT_IMPL_LINK(GetSystemTimePreciseAsFileTime, 4) -WINRT_IMPL_LINK(VirtualQuery, 12) -WINRT_IMPL_LINK(EncodePointer, 4) - -WINRT_IMPL_LINK(OpenProcessToken, 12) -WINRT_IMPL_LINK(GetCurrentProcess, 0) -WINRT_IMPL_LINK(DuplicateToken, 12) -WINRT_IMPL_LINK(OpenThreadToken, 16) -WINRT_IMPL_LINK(GetCurrentThread, 0) -WINRT_IMPL_LINK(SetThreadToken, 8) - -WINRT_IMPL_LINK(AcquireSRWLockExclusive, 4) -WINRT_IMPL_LINK(AcquireSRWLockShared, 4) -WINRT_IMPL_LINK(TryAcquireSRWLockExclusive, 4) -WINRT_IMPL_LINK(TryAcquireSRWLockShared, 4) -WINRT_IMPL_LINK(ReleaseSRWLockExclusive, 4) -WINRT_IMPL_LINK(ReleaseSRWLockShared, 4) -WINRT_IMPL_LINK(SleepConditionVariableSRW, 16) -WINRT_IMPL_LINK(WakeConditionVariable, 4) -WINRT_IMPL_LINK(WakeAllConditionVariable, 4) -WINRT_IMPL_LINK(InterlockedPushEntrySList, 8) -WINRT_IMPL_LINK(InterlockedFlushSList, 4) - -WINRT_IMPL_LINK(CreateEventW, 16) -WINRT_IMPL_LINK(SetEvent, 4) -WINRT_IMPL_LINK(CloseHandle, 4) -WINRT_IMPL_LINK(WaitForSingleObject, 8) - -WINRT_IMPL_LINK(TrySubmitThreadpoolCallback, 12) -WINRT_IMPL_LINK(CreateThreadpoolTimer, 12) -WINRT_IMPL_LINK(SetThreadpoolTimer, 16) -WINRT_IMPL_LINK(CloseThreadpoolTimer, 4) -WINRT_IMPL_LINK(CreateThreadpoolWait, 12) -WINRT_IMPL_LINK(SetThreadpoolWait, 12) -WINRT_IMPL_LINK(CloseThreadpoolWait, 4) -WINRT_IMPL_LINK(CreateThreadpoolIo, 16) -WINRT_IMPL_LINK(StartThreadpoolIo, 4) -WINRT_IMPL_LINK(CancelThreadpoolIo, 4) -WINRT_IMPL_LINK(CloseThreadpoolIo, 4) -WINRT_IMPL_LINK(CreateThreadpool, 4) -WINRT_IMPL_LINK(SetThreadpoolThreadMaximum, 8) -WINRT_IMPL_LINK(SetThreadpoolThreadMinimum, 8) -WINRT_IMPL_LINK(CloseThreadpool, 4) +extern "C" +{ + std::int32_t __stdcall WINRT_IMPL_RoGetActivationFactory(void* classId, winrt::guid const& iid, void** factory) noexcept WINRT_IMPL_LINK(RoGetActivationFactory, 12); + std::int32_t __stdcall WINRT_IMPL_RoGetAgileReference(std::uint32_t options, winrt::guid const& iid, void* object, void** reference) noexcept WINRT_IMPL_LINK(RoGetAgileReference, 16); + std::int32_t __stdcall WINRT_IMPL_SetThreadpoolTimerEx(winrt::impl::ptp_timer, void*, std::uint32_t, std::uint32_t) noexcept WINRT_IMPL_LINK(SetThreadpoolTimerEx, 16); + std::int32_t __stdcall WINRT_IMPL_SetThreadpoolWaitEx(winrt::impl::ptp_wait, void*, void*, void*) noexcept WINRT_IMPL_LINK(SetThreadpoolWaitEx, 16); + std::int32_t __stdcall WINRT_IMPL_RoOriginateLanguageException(std::int32_t error, void* message, void* exception) noexcept WINRT_IMPL_LINK(RoOriginateLanguageException, 12); + std::int32_t __stdcall WINRT_IMPL_RoCaptureErrorContext(std::int32_t error) noexcept WINRT_IMPL_LINK(RoCaptureErrorContext, 4); + void __stdcall WINRT_IMPL_RoFailFastWithErrorContext(std::int32_t) noexcept WINRT_IMPL_LINK(RoFailFastWithErrorContext, 4); + std::int32_t __stdcall WINRT_IMPL_RoTransformError(std::int32_t, std::int32_t, void*) noexcept WINRT_IMPL_LINK(RoTransformError, 12); + + void* __stdcall WINRT_IMPL_LoadLibraryExW(wchar_t const* name, void* unused, std::uint32_t flags) noexcept WINRT_IMPL_LINK(LoadLibraryExW, 12); + std::int32_t __stdcall WINRT_IMPL_FreeLibrary(void* library) noexcept WINRT_IMPL_LINK(FreeLibrary, 4); + void* __stdcall WINRT_IMPL_GetProcAddress(void* library, char const* name) noexcept WINRT_IMPL_LINK(GetProcAddress, 8); + + std::int32_t __stdcall WINRT_IMPL_SetErrorInfo(std::uint32_t reserved, void* info) noexcept WINRT_IMPL_LINK(SetErrorInfo, 8); + std::int32_t __stdcall WINRT_IMPL_GetErrorInfo(std::uint32_t reserved, void** info) noexcept WINRT_IMPL_LINK(GetErrorInfo, 8); + std::int32_t __stdcall WINRT_IMPL_CoInitializeEx(void*, std::uint32_t type) noexcept WINRT_IMPL_LINK(CoInitializeEx, 8); + void __stdcall WINRT_IMPL_CoUninitialize() noexcept WINRT_IMPL_LINK(CoUninitialize, 0); + + std::int32_t __stdcall WINRT_IMPL_CoCreateFreeThreadedMarshaler(void* outer, void** marshaler) noexcept WINRT_IMPL_LINK(CoCreateFreeThreadedMarshaler, 8); + std::int32_t __stdcall WINRT_IMPL_CoCreateInstance(winrt::guid const& clsid, void* outer, std::uint32_t context, winrt::guid const& iid, void** object) noexcept WINRT_IMPL_LINK(CoCreateInstance, 20); + std::int32_t __stdcall WINRT_IMPL_CoGetCallContext(winrt::guid const& iid, void** object) noexcept WINRT_IMPL_LINK(CoGetCallContext, 8); + std::int32_t __stdcall WINRT_IMPL_CoGetObjectContext(winrt::guid const& iid, void** object) noexcept WINRT_IMPL_LINK(CoGetObjectContext, 8); + std::int32_t __stdcall WINRT_IMPL_CoGetApartmentType(std::int32_t* type, std::int32_t* qualifier) noexcept WINRT_IMPL_LINK(CoGetApartmentType, 8); + void* __stdcall WINRT_IMPL_CoTaskMemAlloc(std::size_t size) noexcept WINRT_IMPL_LINK(CoTaskMemAlloc, 4); + void __stdcall WINRT_IMPL_CoTaskMemFree(void* ptr) noexcept WINRT_IMPL_LINK(CoTaskMemFree, 4); + winrt::impl::bstr __stdcall WINRT_IMPL_SysAllocString(wchar_t const* value) noexcept WINRT_IMPL_LINK(SysAllocString, 4); + void __stdcall WINRT_IMPL_SysFreeString(winrt::impl::bstr string) noexcept WINRT_IMPL_LINK(SysFreeString, 4); + std::uint32_t __stdcall WINRT_IMPL_SysStringLen(winrt::impl::bstr string) noexcept WINRT_IMPL_LINK(SysStringLen, 4); + std::int32_t __stdcall WINRT_IMPL_IIDFromString(wchar_t const* string, winrt::guid* iid) noexcept WINRT_IMPL_LINK(IIDFromString, 8); + std::int32_t __stdcall WINRT_IMPL_MultiByteToWideChar(std::uint32_t codepage, std::uint32_t flags, char const* in_string, std::int32_t in_size, wchar_t* out_string, std::int32_t out_size) noexcept WINRT_IMPL_LINK(MultiByteToWideChar, 24); + std::int32_t __stdcall WINRT_IMPL_WideCharToMultiByte(std::uint32_t codepage, std::uint32_t flags, wchar_t const* int_string, std::int32_t in_size, char* out_string, std::int32_t out_size, char const* default_char, std::int32_t* default_used) noexcept WINRT_IMPL_LINK(WideCharToMultiByte, 32); + void* __stdcall WINRT_IMPL_HeapAlloc(void* heap, std::uint32_t flags, std::size_t bytes) noexcept WINRT_IMPL_LINK(HeapAlloc, 12); + std::int32_t __stdcall WINRT_IMPL_HeapFree(void* heap, std::uint32_t flags, void* value) noexcept WINRT_IMPL_LINK(HeapFree, 12); + void* __stdcall WINRT_IMPL_GetProcessHeap() noexcept WINRT_IMPL_LINK(GetProcessHeap, 0); + std::uint32_t __stdcall WINRT_IMPL_FormatMessageW(std::uint32_t flags, void const* source, std::uint32_t code, std::uint32_t language, wchar_t* buffer, std::uint32_t size, va_list* arguments) noexcept WINRT_IMPL_LINK(FormatMessageW, 28); + std::uint32_t __stdcall WINRT_IMPL_GetLastError() noexcept WINRT_IMPL_LINK(GetLastError, 0); + void __stdcall WINRT_IMPL_GetSystemTimePreciseAsFileTime(void* result) noexcept WINRT_IMPL_LINK(GetSystemTimePreciseAsFileTime, 4); + std::uintptr_t __stdcall WINRT_IMPL_VirtualQuery(void* address, void* buffer, std::uintptr_t length) noexcept WINRT_IMPL_LINK(VirtualQuery, 12); + void* __stdcall WINRT_IMPL_EncodePointer(void* ptr) noexcept WINRT_IMPL_LINK(EncodePointer, 4); + + std::int32_t __stdcall WINRT_IMPL_OpenProcessToken(void* process, std::uint32_t access, void** token) noexcept WINRT_IMPL_LINK(OpenProcessToken, 12); + void* __stdcall WINRT_IMPL_GetCurrentProcess() noexcept WINRT_IMPL_LINK(GetCurrentProcess, 0); + std::int32_t __stdcall WINRT_IMPL_DuplicateToken(void* existing, std::uint32_t level, void** duplicate) noexcept WINRT_IMPL_LINK(DuplicateToken, 12); + std::int32_t __stdcall WINRT_IMPL_OpenThreadToken(void* thread, std::uint32_t access, std::int32_t self, void** token) noexcept WINRT_IMPL_LINK(OpenThreadToken, 16); + void* __stdcall WINRT_IMPL_GetCurrentThread() noexcept WINRT_IMPL_LINK(GetCurrentThread, 0); + std::int32_t __stdcall WINRT_IMPL_SetThreadToken(void** thread, void* token) noexcept WINRT_IMPL_LINK(SetThreadToken, 8); + + void __stdcall WINRT_IMPL_AcquireSRWLockExclusive(winrt::impl::srwlock* lock) noexcept WINRT_IMPL_LINK(AcquireSRWLockExclusive, 4); + void __stdcall WINRT_IMPL_AcquireSRWLockShared(winrt::impl::srwlock* lock) noexcept WINRT_IMPL_LINK(AcquireSRWLockShared, 4); + std::uint8_t __stdcall WINRT_IMPL_TryAcquireSRWLockExclusive(winrt::impl::srwlock* lock) noexcept WINRT_IMPL_LINK(TryAcquireSRWLockExclusive, 4); + std::uint8_t __stdcall WINRT_IMPL_TryAcquireSRWLockShared(winrt::impl::srwlock* lock) noexcept WINRT_IMPL_LINK(TryAcquireSRWLockShared, 4); + void __stdcall WINRT_IMPL_ReleaseSRWLockExclusive(winrt::impl::srwlock* lock) noexcept WINRT_IMPL_LINK(ReleaseSRWLockExclusive, 4); + void __stdcall WINRT_IMPL_ReleaseSRWLockShared(winrt::impl::srwlock* lock) noexcept WINRT_IMPL_LINK(ReleaseSRWLockShared, 4); + std::int32_t __stdcall WINRT_IMPL_SleepConditionVariableSRW(winrt::impl::condition_variable* cv, winrt::impl::srwlock* lock, std::uint32_t milliseconds, std::uint32_t flags) noexcept WINRT_IMPL_LINK(SleepConditionVariableSRW, 16); + void __stdcall WINRT_IMPL_WakeConditionVariable(winrt::impl::condition_variable* cv) noexcept WINRT_IMPL_LINK(WakeConditionVariable, 4); + void __stdcall WINRT_IMPL_WakeAllConditionVariable(winrt::impl::condition_variable* cv) noexcept WINRT_IMPL_LINK(WakeAllConditionVariable, 4); + void* __stdcall WINRT_IMPL_InterlockedPushEntrySList(void* head, void* entry) noexcept WINRT_IMPL_LINK(InterlockedPushEntrySList, 8); + void* __stdcall WINRT_IMPL_InterlockedFlushSList(void* head) noexcept WINRT_IMPL_LINK(InterlockedFlushSList, 4); + + void* __stdcall WINRT_IMPL_CreateEventW(void*, std::int32_t, std::int32_t, void*) noexcept WINRT_IMPL_LINK(CreateEventW, 16); + std::int32_t __stdcall WINRT_IMPL_SetEvent(void*) noexcept WINRT_IMPL_LINK(SetEvent, 4); + std::int32_t __stdcall WINRT_IMPL_CloseHandle(void* hObject) noexcept WINRT_IMPL_LINK(CloseHandle, 4); + std::uint32_t __stdcall WINRT_IMPL_WaitForSingleObject(void* handle, std::uint32_t milliseconds) noexcept WINRT_IMPL_LINK(WaitForSingleObject, 8); + + std::int32_t __stdcall WINRT_IMPL_TrySubmitThreadpoolCallback(void(__stdcall *callback)(void*, void* context), void* context, void*) noexcept WINRT_IMPL_LINK(TrySubmitThreadpoolCallback, 12); + winrt::impl::ptp_timer __stdcall WINRT_IMPL_CreateThreadpoolTimer(void(__stdcall *callback)(void*, void* context, void*), void* context, void*) noexcept WINRT_IMPL_LINK(CreateThreadpoolTimer, 12); + void __stdcall WINRT_IMPL_SetThreadpoolTimer(winrt::impl::ptp_timer timer, void* time, std::uint32_t period, std::uint32_t window) noexcept WINRT_IMPL_LINK(SetThreadpoolTimer, 16); + void __stdcall WINRT_IMPL_CloseThreadpoolTimer(winrt::impl::ptp_timer timer) noexcept WINRT_IMPL_LINK(CloseThreadpoolTimer, 4); + winrt::impl::ptp_wait __stdcall WINRT_IMPL_CreateThreadpoolWait(void(__stdcall *callback)(void*, void* context, void*, std::uint32_t result), void* context, void*) noexcept WINRT_IMPL_LINK(CreateThreadpoolWait, 12); + void __stdcall WINRT_IMPL_SetThreadpoolWait(winrt::impl::ptp_wait wait, void* handle, void* timeout) noexcept WINRT_IMPL_LINK(SetThreadpoolWait, 12); + void __stdcall WINRT_IMPL_CloseThreadpoolWait(winrt::impl::ptp_wait wait) noexcept WINRT_IMPL_LINK(CloseThreadpoolWait, 4); + winrt::impl::ptp_io __stdcall WINRT_IMPL_CreateThreadpoolIo(void* object, void(__stdcall *callback)(void*, void* context, void* overlapped, std::uint32_t result, std::size_t bytes, void*) noexcept, void* context, void*) noexcept WINRT_IMPL_LINK(CreateThreadpoolIo, 16); + void __stdcall WINRT_IMPL_StartThreadpoolIo(winrt::impl::ptp_io io) noexcept WINRT_IMPL_LINK(StartThreadpoolIo, 4); + void __stdcall WINRT_IMPL_CancelThreadpoolIo(winrt::impl::ptp_io io) noexcept WINRT_IMPL_LINK(CancelThreadpoolIo, 4); + void __stdcall WINRT_IMPL_CloseThreadpoolIo(winrt::impl::ptp_io io) noexcept WINRT_IMPL_LINK(CloseThreadpoolIo, 4); + winrt::impl::ptp_pool __stdcall WINRT_IMPL_CreateThreadpool(void* reserved) noexcept WINRT_IMPL_LINK(CreateThreadpool, 4); + void __stdcall WINRT_IMPL_SetThreadpoolThreadMaximum(winrt::impl::ptp_pool pool, std::uint32_t value) noexcept WINRT_IMPL_LINK(SetThreadpoolThreadMaximum, 8); + std::int32_t __stdcall WINRT_IMPL_SetThreadpoolThreadMinimum(winrt::impl::ptp_pool pool, std::uint32_t value) noexcept WINRT_IMPL_LINK(SetThreadpoolThreadMinimum, 8); + void __stdcall WINRT_IMPL_CloseThreadpool(winrt::impl::ptp_pool pool) noexcept WINRT_IMPL_LINK(CloseThreadpool, 4); + + std::int32_t __stdcall WINRT_CanUnloadNow() noexcept; + std::int32_t __stdcall WINRT_GetActivationFactory(void* classId, void** factory) noexcept; +} #undef WINRT_IMPL_LINK diff --git a/strings/base_fast_forward.h b/strings/base_fast_forward.h index 298403197..a73e70a04 100644 --- a/strings/base_fast_forward.h +++ b/strings/base_fast_forward.h @@ -1,9 +1,27 @@ #include #include +#include +#include #define WINRT_IMPL_STRING_1(expression) #expression #define WINRT_IMPL_STRING(expression) WINRT_IMPL_STRING_1(expression) +#if defined(_MSC_VER) +#define WINRT_IMPL_FF_NOVTABLE __declspec(novtable) +#else +#define WINRT_IMPL_FF_NOVTABLE +#endif + +#if defined(__clang__) && defined(__has_attribute) +#if __has_attribute(__lto_visibility_public__) +#define WINRT_IMPL_FF_PUBLIC __attribute__((lto_visibility_public)) +#else +#define WINRT_IMPL_FF_PUBLIC +#endif // __has_attribute(__lto_visibility_public__) +#else +#define WINRT_IMPL_FF_PUBLIC +#endif + #if !defined(WINRT_FAST_ABI_SIZE) #define WINRT_FAST_ABI_SIZE % #endif @@ -12,7 +30,11 @@ static_assert(WINRT_FAST_ABI_SIZE >= %); #pragma detect_mismatch("WINRT_FAST_ABI_SIZE", WINRT_IMPL_STRING(WINRT_FAST_ABI_SIZE)) -namespace winrt::impl +#ifndef WINRT_EXPORT +#define WINRT_EXPORT +#endif // WINRT_EXPORT + +WINRT_EXPORT namespace winrt::impl { // Thunk definitions are in arch-specific assembly sources % @@ -20,34 +42,34 @@ namespace winrt::impl { struct guid { - uint32_t Data1; - uint16_t Data2; - uint16_t Data3; - uint8_t Data4[8]; + std::uint32_t Data1; + std::uint16_t Data2; + std::uint16_t Data3; + std::uint8_t Data4[8]; inline bool operator!=(guid const& right) const noexcept { - return memcmp(this, &right, sizeof(guid)); + return std::memcmp(this, &right, sizeof(guid)); } }; - struct __declspec(novtable) inspectable + struct WINRT_IMPL_FF_NOVTABLE WINRT_IMPL_FF_PUBLIC inspectable { - virtual int32_t __stdcall QueryInterface(guid const& id, void** object) noexcept = 0; - virtual uint32_t __stdcall AddRef() noexcept = 0; - virtual uint32_t __stdcall Release() noexcept = 0; - virtual int32_t __stdcall GetIids(uint32_t* count, guid** ids) noexcept = 0; - virtual int32_t __stdcall GetRuntimeClassName(void** name) noexcept = 0; - virtual int32_t __stdcall GetTrustLevel(uint32_t* level) noexcept = 0; + virtual std::int32_t __stdcall QueryInterface(guid const& id, void** object) noexcept = 0; + virtual std::uint32_t __stdcall AddRef() noexcept = 0; + virtual std::uint32_t __stdcall Release() noexcept = 0; + virtual std::int32_t __stdcall GetIids(std::uint32_t* count, guid** ids) noexcept = 0; + virtual std::int32_t __stdcall GetRuntimeClassName(void** name) noexcept = 0; + virtual std::int32_t __stdcall GetTrustLevel(std::uint32_t* level) noexcept = 0; }; void* const* m_vfptr; inspectable* m_owner; std::size_t m_offset; guid m_iid; - std::atomic m_references{ 1 }; + std::atomic m_references{ 1 }; fast_abi_forwarder(void* owner, guid const& iid, std::size_t offset) noexcept : - m_vfptr(s_vtable), m_owner(static_cast(owner)), m_iid(iid), m_offset(offset) + m_vfptr(s_vtable), m_owner(static_cast(owner)), m_offset(offset), m_iid(iid) { m_owner->AddRef(); } @@ -57,7 +79,7 @@ namespace winrt::impl m_owner->Release(); } - static int32_t __stdcall QueryInterface(fast_abi_forwarder* self, guid const& iid, void** object) noexcept + static std::int32_t __stdcall QueryInterface(fast_abi_forwarder* self, guid const& iid, void** object) noexcept { if (iid != self->m_iid) { @@ -69,14 +91,14 @@ namespace winrt::impl } // Note: COM interfaces use stdcall, not thiscall, ('this' gets no special treatment), permitting static implementations - static uint32_t __stdcall AddRef(fast_abi_forwarder* self) noexcept + static std::uint32_t __stdcall AddRef(fast_abi_forwarder* self) noexcept { return 1 + self->m_references.fetch_add(1, std::memory_order_relaxed); } - static uint32_t __stdcall Release(fast_abi_forwarder* self) noexcept + static std::uint32_t __stdcall Release(fast_abi_forwarder* self) noexcept { - uint32_t const remaining = self->m_references.fetch_sub(1, std::memory_order_release) - 1; + std::uint32_t const remaining = self->m_references.fetch_sub(1, std::memory_order_release) - 1; if (remaining == 0) { std::atomic_thread_fence(std::memory_order_acquire); @@ -85,21 +107,26 @@ namespace winrt::impl return remaining; } - static uint32_t __stdcall GetIids(fast_abi_forwarder* self, uint32_t* count, guid** iids) noexcept + static std::uint32_t __stdcall GetIids(fast_abi_forwarder* self, std::uint32_t* count, guid** iids) noexcept { return self->m_owner->GetIids(count, iids); } - static uint32_t __stdcall GetRuntimeClassName(fast_abi_forwarder* self, void** name) noexcept + static std::uint32_t __stdcall GetRuntimeClassName(fast_abi_forwarder* self, void** name) noexcept { return self->m_owner->GetRuntimeClassName(name); } - static uint32_t __stdcall GetTrustLevel(fast_abi_forwarder* self, uint32_t* level) noexcept + static std::uint32_t __stdcall GetTrustLevel(fast_abi_forwarder* self, std::uint32_t* level) noexcept { return self->m_owner->GetTrustLevel(level); } + +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wmicrosoft-cast" +#endif static inline void* const s_vtable[] = { QueryInterface, @@ -109,6 +136,9 @@ namespace winrt::impl GetRuntimeClassName, GetTrustLevel, % }; +#ifdef __clang__ +#pragma clang diagnostic pop +#endif }; // Enforce assumptions made by thunk asm code @@ -120,7 +150,7 @@ namespace winrt::impl namespace winrt { template - auto make_fast_abi_forwarder(void* owner, TGuid const& guid, size_t offset) + auto make_fast_abi_forwarder(void* owner, TGuid const& guid, std::size_t offset) { using ff_guid = impl::fast_abi_forwarder::guid; static_assert(sizeof(ff_guid) == sizeof(TGuid)); @@ -130,3 +160,5 @@ namespace winrt #undef WINRT_IMPL_STRING #undef WINRT_IMPL_STRING_1 +#undef WINRT_IMPL_FF_NOVTABLE +#undef WINRT_IMPL_FF_PUBLIC diff --git a/strings/base_foundation.h b/strings/base_foundation.h index ea8881edc..083252753 100644 --- a/strings/base_foundation.h +++ b/strings/base_foundation.h @@ -100,7 +100,7 @@ WINRT_EXPORT namespace winrt::Windows::Foundation } } -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { template <> inline constexpr auto& name_v = L"Windows.Foundation.Point"; template <> inline constexpr auto& name_v = L"Windows.Foundation.Size"; diff --git a/strings/base_handle.h b/strings/base_handle.h index 60eec8be0..7a65af7b3 100644 --- a/strings/base_handle.h +++ b/strings/base_handle.h @@ -52,7 +52,7 @@ WINRT_EXPORT namespace winrt type* put() noexcept { - WINRT_ASSERT(m_value == T::invalid()); + close(); return &m_value; } diff --git a/strings/base_identity.h b/strings/base_identity.h index 5bf7bbaa8..4a5bedbb1 100644 --- a/strings/base_identity.h +++ b/strings/base_identity.h @@ -11,39 +11,39 @@ WINRT_EXPORT namespace winrt } template - bool is_guid_of(guid const& id) noexcept + constexpr bool is_guid_of(guid const& id) noexcept { return ((id == guid_of()) || ...); } } -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { - template + template constexpr std::array to_array(T const* value, std::index_sequence const) noexcept { return { value[Index]... }; } - template + template constexpr auto to_array(std::array const& value) noexcept { return value; } - template + template constexpr auto to_array(char const(&value)[Size]) noexcept { return to_array(value, std::make_index_sequence()); } - template + template constexpr auto to_array(wchar_t const(&value)[Size]) noexcept { return to_array(value, std::make_index_sequence()); } - template + template constexpr std::array concat( [[maybe_unused]] std::array const& left, [[maybe_unused]] std::array const& right, @@ -53,31 +53,31 @@ namespace winrt::impl return { left[LeftIndex]..., right[RightIndex]... }; } - template + template constexpr auto concat(std::array const& left, std::array const& right) noexcept { return concat(left, right, std::make_index_sequence(), std::make_index_sequence()); } - template + template constexpr auto concat(std::array const& left, T const(&right)[RightSize]) noexcept { return concat(left, to_array(right)); } - template + template constexpr auto concat(T const(&left)[LeftSize], std::array const& right) noexcept { return concat(to_array(left), right); } - template + template constexpr auto concat(std::array const& left, T const right) noexcept { return concat(left, std::array{right}); } - template + template constexpr auto concat(T const left, std::array const& right) noexcept { return concat(std::array{left}, right); @@ -96,31 +96,31 @@ namespace winrt::impl } } - template + template constexpr std::array zconcat_base(std::array const& left, std::array const& right, std::index_sequence const, std::index_sequence const) noexcept { return { left[LI]..., right[RI]..., T{} }; } - template + template constexpr auto zconcat(std::array const& left, std::array const& right) noexcept { return zconcat_base(left, right, std::make_index_sequence(), std::make_index_sequence()); } - template + template constexpr std::array to_zarray_base(T const(&value)[S], std::index_sequence const) noexcept { return { value[I]... }; } - template + template constexpr auto to_zarray(T const(&value)[S]) noexcept { return to_zarray_base(value, std::make_index_sequence()); } - template + template constexpr auto to_zarray(std::array const& value) noexcept { return value; @@ -139,43 +139,43 @@ namespace winrt::impl } } - constexpr std::array to_array(uint32_t value) noexcept + constexpr std::array to_array(std::uint32_t value) noexcept { - return { static_cast(value & 0x000000ff), static_cast((value & 0x0000ff00) >> 8), static_cast((value & 0x00ff0000) >> 16), static_cast((value & 0xff000000) >> 24) }; + return { static_cast(value & 0x000000ff), static_cast((value & 0x0000ff00) >> 8), static_cast((value & 0x00ff0000) >> 16), static_cast((value & 0xff000000) >> 24) }; } - constexpr std::array to_array(uint16_t value) noexcept + constexpr std::array to_array(std::uint16_t value) noexcept { - return { static_cast(value & 0x00ff), static_cast((value & 0xff00) >> 8) }; + return { static_cast(value & 0x00ff), static_cast((value & 0xff00) >> 8) }; } constexpr auto to_array(guid const& value) noexcept { return combine(to_array(value.Data1), to_array(value.Data2), to_array(value.Data3), - std::array{ value.Data4[0], value.Data4[1], value.Data4[2], value.Data4[3], value.Data4[4], value.Data4[5], value.Data4[6], value.Data4[7] }); + std::array{ value.Data4[0], value.Data4[1], value.Data4[2], value.Data4[3], value.Data4[4], value.Data4[5], value.Data4[6], value.Data4[7] }); } template - constexpr T to_hex_digit(uint8_t value) noexcept + constexpr T to_hex_digit(std::uint8_t value) noexcept { value &= 0xF; return value < 10 ? static_cast('0') + value : static_cast('a') + (value - 10); } template - constexpr std::array uint8_to_hex(uint8_t const value) noexcept + constexpr std::array uint8_to_hex(std::uint8_t const value) noexcept { return { to_hex_digit(value >> 4), to_hex_digit(value & 0xF) }; } template - constexpr auto uint16_to_hex(uint16_t value) noexcept + constexpr auto uint16_to_hex(std::uint16_t value) noexcept { - return combine(uint8_to_hex(static_cast(value >> 8)), uint8_to_hex(value & 0xFF)); + return combine(uint8_to_hex(static_cast(value >> 8)), uint8_to_hex(value & 0xFF)); } template - constexpr auto uint32_to_hex(uint32_t const value) noexcept + constexpr auto uint32_to_hex(std::uint32_t const value) noexcept { return combine(uint16_to_hex(value >> 16), uint16_to_hex(value & 0xFFFF)); } @@ -197,18 +197,18 @@ namespace winrt::impl ); } - constexpr uint32_t to_guid(uint8_t a, uint8_t b, uint8_t c, uint8_t d) noexcept + constexpr std::uint32_t to_guid(std::uint8_t a, std::uint8_t b, std::uint8_t c, std::uint8_t d) noexcept { - return (static_cast(d) << 24) | (static_cast(c) << 16) | (static_cast(b) << 8) | static_cast(a); + return (static_cast(d) << 24) | (static_cast(c) << 16) | (static_cast(b) << 8) | static_cast(a); } - constexpr uint16_t to_guid(uint8_t a, uint8_t b) noexcept + constexpr std::uint16_t to_guid(std::uint8_t a, std::uint8_t b) noexcept { - return (static_cast(b) << 8) | static_cast(a); + return (static_cast(b) << 8) | static_cast(a); } - template - constexpr guid to_guid(std::array const& arr) noexcept + template + constexpr guid to_guid(std::array const& arr) noexcept { return { @@ -219,12 +219,12 @@ namespace winrt::impl }; } - constexpr uint32_t endian_swap(uint32_t value) noexcept + constexpr std::uint32_t endian_swap(std::uint32_t value) noexcept { return (value & 0xFF000000) >> 24 | (value & 0x00FF0000) >> 8 | (value & 0x0000FF00) << 8 | (value & 0x000000FF) << 24; } - constexpr uint16_t endian_swap(uint16_t value) noexcept + constexpr std::uint16_t endian_swap(std::uint16_t value) noexcept { return (value & 0xFF00) >> 8 | (value & 0x00FF) << 8; } @@ -239,51 +239,51 @@ namespace winrt::impl constexpr guid set_named_guid_fields(guid value) noexcept { - value.Data3 = static_cast((value.Data3 & 0x0fff) | (5 << 12)); - value.Data4[0] = static_cast((value.Data4[0] & 0x3f) | 0x80); + value.Data3 = static_cast((value.Data3 & 0x0fff) | (5 << 12)); + value.Data4[0] = static_cast((value.Data4[0] & 0x3f) | 0x80); return value; } - template - constexpr std::array char_to_byte_array(std::array const& value, std::index_sequence const) noexcept + template + constexpr std::array char_to_byte_array(std::array const& value, std::index_sequence const) noexcept { - return { static_cast(value[Index])... }; + return { static_cast(value[Index])... }; } - constexpr auto sha1_rotl(uint8_t bits, uint32_t word) noexcept + constexpr auto sha1_rotl(std::uint8_t bits, std::uint32_t word) noexcept { return (word << bits) | (word >> (32 - bits)); } - constexpr auto sha_ch(uint32_t x, uint32_t y, uint32_t z) noexcept + constexpr auto sha_ch(std::uint32_t x, std::uint32_t y, std::uint32_t z) noexcept { return (x & y) ^ ((~x) & z); } - constexpr auto sha_parity(uint32_t x, uint32_t y, uint32_t z) noexcept + constexpr auto sha_parity(std::uint32_t x, std::uint32_t y, std::uint32_t z) noexcept { return x ^ y ^ z; } - constexpr auto sha_maj(uint32_t x, uint32_t y, uint32_t z) noexcept + constexpr auto sha_maj(std::uint32_t x, std::uint32_t y, std::uint32_t z) noexcept { return (x & y) ^ (x & z) ^ (y & z); } - constexpr std::array process_msg_block(uint8_t const* input, size_t start_pos, std::array const& intermediate_hash) noexcept + constexpr std::array process_msg_block(std::uint8_t const* input, std::size_t start_pos, std::array const& intermediate_hash) noexcept { - uint32_t const K[4] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 }; - std::array W = {}; + std::uint32_t const K[4] = { 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xCA62C1D6 }; + std::array W = {}; - size_t t = 0; - uint32_t temp = 0; + std::size_t t = 0; + std::uint32_t temp = 0; for (t = 0; t < 16; t++) { - W[t] = static_cast(input[start_pos + t * 4]) << 24; - W[t] = W[t] | static_cast(input[start_pos + t * 4 + 1]) << 16; - W[t] = W[t] | static_cast(input[start_pos + t * 4 + 2]) << 8; - W[t] = W[t] | static_cast(input[start_pos + t * 4 + 3]); + W[t] = static_cast(input[start_pos + t * 4]) << 24; + W[t] = W[t] | static_cast(input[start_pos + t * 4 + 1]) << 16; + W[t] = W[t] | static_cast(input[start_pos + t * 4 + 2]) << 8; + W[t] = W[t] | static_cast(input[start_pos + t * 4 + 3]); } for (t = 16; t < 80; t++) @@ -291,11 +291,11 @@ namespace winrt::impl W[t] = sha1_rotl(1, W[t - 3] ^ W[t - 8] ^ W[t - 14] ^ W[t - 16]); } - uint32_t A = intermediate_hash[0]; - uint32_t B = intermediate_hash[1]; - uint32_t C = intermediate_hash[2]; - uint32_t D = intermediate_hash[3]; - uint32_t E = intermediate_hash[4]; + std::uint32_t A = intermediate_hash[0]; + std::uint32_t B = intermediate_hash[1]; + std::uint32_t C = intermediate_hash[2]; + std::uint32_t D = intermediate_hash[3]; + std::uint32_t E = intermediate_hash[4]; for (t = 0; t < 20; t++) { @@ -340,54 +340,54 @@ namespace winrt::impl return { intermediate_hash[0] + A, intermediate_hash[1] + B, intermediate_hash[2] + C, intermediate_hash[3] + D, intermediate_hash[4] + E }; } - template - constexpr std::array process_msg_block(std::array const& input, size_t start_pos, std::array const& intermediate_hash) noexcept + template + constexpr std::array process_msg_block(std::array const& input, std::size_t start_pos, std::array const& intermediate_hash) noexcept { return process_msg_block(input.data(), start_pos, intermediate_hash); } - constexpr std::array size_to_bytes(size_t size) noexcept + constexpr std::array size_to_bytes(std::size_t size) noexcept { return { - static_cast((size & 0xff00000000000000) >> 56), - static_cast((size & 0x00ff000000000000) >> 48), - static_cast((size & 0x0000ff0000000000) >> 40), - static_cast((size & 0x000000ff00000000) >> 32), - static_cast((size & 0x00000000ff000000) >> 24), - static_cast((size & 0x0000000000ff0000) >> 16), - static_cast((size & 0x000000000000ff00) >> 8), - static_cast((size & 0x00000000000000ff) >> 0) + static_cast((size & 0xff00000000000000) >> 56), + static_cast((size & 0x00ff000000000000) >> 48), + static_cast((size & 0x0000ff0000000000) >> 40), + static_cast((size & 0x000000ff00000000) >> 32), + static_cast((size & 0x00000000ff000000) >> 24), + static_cast((size & 0x0000000000ff0000) >> 16), + static_cast((size & 0x000000000000ff00) >> 8), + static_cast((size & 0x00000000000000ff) >> 0) }; } - template - constexpr std::array make_remaining([[maybe_unused]] std::array const& input, [[maybe_unused]] size_t start_pos, std::index_sequence) noexcept + template + constexpr std::array make_remaining([[maybe_unused]] std::array const& input, [[maybe_unused]] std::size_t start_pos, std::index_sequence) noexcept { return { input[Index + start_pos]..., 0x80 }; } - template - constexpr auto make_remaining(std::array const& input, size_t start_pos) noexcept + template + constexpr auto make_remaining(std::array const& input, std::size_t start_pos) noexcept { constexpr auto remaining_size = Size % 64; return make_remaining(input, start_pos, std::make_index_sequence()); } - template - constexpr auto make_buffer(std::array const& remaining_buffer) noexcept + template + constexpr auto make_buffer(std::array const& remaining_buffer) noexcept { constexpr auto message_length = (RemainderSize + 8 <= 64) ? 64 : 64 * 2; constexpr auto padding_length = message_length - RemainderSize - 8; - auto padding_buffer = std::array{}; + auto padding_buffer = std::array{}; auto length_buffer = size_to_bytes(InputSize * 8); return combine(remaining_buffer, padding_buffer, length_buffer); } - template - constexpr std::array finalize_remaining_buffer(std::array const& input, std::array const& intermediate_hash) noexcept + template + constexpr std::array finalize_remaining_buffer(std::array const& input, std::array const& intermediate_hash) noexcept { if constexpr (Size == 64) { @@ -399,22 +399,22 @@ namespace winrt::impl } } - template - constexpr std::array get_result(std::array const& intermediate_hash, std::index_sequence) noexcept + template + constexpr std::array get_result(std::array const& intermediate_hash, std::index_sequence) noexcept { - return { static_cast(intermediate_hash[Index >> 2] >> (8 * (3 - (Index & 0x03))))... }; + return { static_cast(intermediate_hash[Index >> 2] >> (8 * (3 - (Index & 0x03))))... }; } - constexpr auto get_result(std::array const& intermediate_hash) noexcept + constexpr auto get_result(std::array const& intermediate_hash) noexcept { return get_result(intermediate_hash, std::make_index_sequence<20>{}); } - template - constexpr auto calculate_sha1(std::array const& input) noexcept + template + constexpr auto calculate_sha1(std::array const& input) noexcept { - std::array intermediate_hash{ 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }; - size_t i = 0; + std::array intermediate_hash{ 0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0 }; + std::size_t i = 0; while (i + 64 <= Size) { @@ -426,7 +426,7 @@ namespace winrt::impl return get_result(intermediate_hash); } - template + template constexpr guid generate_guid(std::array const& value) noexcept { guid namespace_guid = { 0xd57af411, 0x737b, 0xc042,{ 0xab, 0xae, 0x87, 0x8b, 0x1e, 0x16, 0xad, 0xee } }; @@ -453,17 +453,17 @@ namespace winrt::impl template struct pinterface_guid { +#ifdef _MSC_VER #pragma warning(suppress: 4307) +#endif static constexpr guid value{ generate_guid(signature::data) }; }; template -#ifdef __clang__ - inline static const auto name_v -#else +#ifdef _MSC_VER #pragma warning(suppress: 4307) - inline constexpr auto name_v #endif + inline constexpr auto name_v { combine ( @@ -472,7 +472,7 @@ namespace winrt::impl ) }; - constexpr size_t to_utf8_size(wchar_t const value) noexcept + constexpr std::size_t to_utf8_size(wchar_t const value) noexcept { if (value <= 0x7F) { @@ -487,7 +487,7 @@ namespace winrt::impl return 3; } - constexpr size_t to_utf8(wchar_t const value, char* buffer) noexcept + constexpr std::size_t to_utf8(wchar_t const value, char* buffer) noexcept { if (value <= 0x7F) { @@ -509,10 +509,10 @@ namespace winrt::impl } template - constexpr size_t to_utf8_size() noexcept + constexpr std::size_t to_utf8_size() noexcept { auto input = to_array(name_v); - size_t length = 0; + std::size_t length = 0; for (wchar_t const element : input) { @@ -527,7 +527,7 @@ namespace winrt::impl { auto input = to_array(name_v); std::array()> output{}; - size_t offset{}; + std::size_t offset{}; for (wchar_t const element : input) { @@ -544,14 +544,14 @@ namespace winrt::impl constexpr auto& basic_signature_v = ""; template <> inline constexpr auto& basic_signature_v = "b1"; - template <> inline constexpr auto& basic_signature_v = "i1"; - template <> inline constexpr auto& basic_signature_v = "i2"; - template <> inline constexpr auto& basic_signature_v = "i4"; - template <> inline constexpr auto& basic_signature_v = "i8"; - template <> inline constexpr auto& basic_signature_v = "u1"; - template <> inline constexpr auto& basic_signature_v = "u2"; - template <> inline constexpr auto& basic_signature_v = "u4"; - template <> inline constexpr auto& basic_signature_v = "u8"; + template <> inline constexpr auto& basic_signature_v = "i1"; + template <> inline constexpr auto& basic_signature_v = "i2"; + template <> inline constexpr auto& basic_signature_v = "i4"; + template <> inline constexpr auto& basic_signature_v = "i8"; + template <> inline constexpr auto& basic_signature_v = "u1"; + template <> inline constexpr auto& basic_signature_v = "u2"; + template <> inline constexpr auto& basic_signature_v = "u4"; + template <> inline constexpr auto& basic_signature_v = "u8"; template <> inline constexpr auto& basic_signature_v = "f4"; template <> inline constexpr auto& basic_signature_v = "f8"; template <> inline constexpr auto& basic_signature_v = "c2"; @@ -560,14 +560,14 @@ namespace winrt::impl template <> inline constexpr auto& basic_signature_v = "cinterface(IInspectable)"; template <> inline constexpr auto& name_v = L"Boolean"; - template <> inline constexpr auto& name_v = L"Int8"; - template <> inline constexpr auto& name_v = L"Int16"; - template <> inline constexpr auto& name_v = L"Int32"; - template <> inline constexpr auto& name_v = L"Int64"; - template <> inline constexpr auto& name_v = L"UInt8"; - template <> inline constexpr auto& name_v = L"UInt16"; - template <> inline constexpr auto& name_v = L"UInt32"; - template <> inline constexpr auto& name_v = L"UInt64"; + template <> inline constexpr auto& name_v = L"Int8"; + template <> inline constexpr auto& name_v = L"Int16"; + template <> inline constexpr auto& name_v = L"Int32"; + template <> inline constexpr auto& name_v = L"Int64"; + template <> inline constexpr auto& name_v = L"UInt8"; + template <> inline constexpr auto& name_v = L"UInt16"; + template <> inline constexpr auto& name_v = L"UInt32"; + template <> inline constexpr auto& name_v = L"UInt64"; template <> inline constexpr auto& name_v = L"Single"; template <> inline constexpr auto& name_v = L"Double"; template <> inline constexpr auto& name_v = L"Char16"; @@ -581,23 +581,23 @@ namespace winrt::impl template <> inline constexpr auto& name_v = L"IAgileObject"; template <> struct category { using type = basic_category; }; - template <> struct category { using type = basic_category; }; - template <> struct category { using type = basic_category; }; - template <> struct category { using type = basic_category; }; - template <> struct category { using type = basic_category; }; - template <> struct category { using type = basic_category; }; - template <> struct category { using type = basic_category; }; - template <> struct category { using type = basic_category; }; - template <> struct category { using type = basic_category; }; + template <> struct category { using type = basic_category; }; + template <> struct category { using type = basic_category; }; + template <> struct category { using type = basic_category; }; + template <> struct category { using type = basic_category; }; + template <> struct category { using type = basic_category; }; + template <> struct category { using type = basic_category; }; + template <> struct category { using type = basic_category; }; + template <> struct category { using type = basic_category; }; template <> struct category { using type = basic_category; }; template <> struct category { using type = basic_category; }; template <> struct category { using type = basic_category; }; template <> struct category { using type = basic_category; }; - template <> struct category { using type = struct_category; }; - template <> struct category { using type = struct_category; }; + template <> struct category { using type = struct_category; }; + template <> struct category { using type = struct_category; }; template <> struct category { using type = basic_category; }; - template <> struct category { using type = struct_category; }; - template <> struct category { using type = struct_category; }; + template <> struct category { using type = struct_category; }; + template <> struct category { using type = struct_category; }; template struct category_signature @@ -642,13 +642,13 @@ namespace winrt::impl constexpr static auto data{ combine("delegate(", to_array(guid_of()), ")") }; }; - template + template constexpr std::wstring_view to_wstring_view(std::array const& value) noexcept { return { value.data(), Size - 1 }; } - template + template constexpr std::wstring_view to_wstring_view(wchar_t const (&value)[Size]) noexcept { return { value, Size - 1 }; diff --git a/strings/base_implements.h b/strings/base_implements.h index 2457b80b4..0eb8db0bd 100644 --- a/strings/base_implements.h +++ b/strings/base_implements.h @@ -1,5 +1,12 @@ +#if defined(_MSC_VER) +#if defined(_DEBUG) && !defined(WINRT_NO_MAKE_DETECTION) +#pragma detect_mismatch("C++/WinRT WINRT_NO_MAKE_DETECTION", "make detection enabled (DEBUG and !WINRT_NO_MAKE_DETECTION)") +#else +#pragma detect_mismatch("C++/WinRT WINRT_NO_MAKE_DETECTION", "make detection disabled (!DEBUG or WINRT_NO_MAKE_DETECTION)") +#endif +#endif -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { struct marker { @@ -23,7 +30,7 @@ WINRT_EXPORT namespace winrt struct implements; } -namespace winrt::impl +WINRT_EXPORT namespace winrt::impl { template using tuple_cat_t = decltype(std::tuple_cat(std::declval()...)); @@ -37,17 +44,8 @@ namespace winrt::impl template