Skip to content

Migrate Windows interop to CsWin32 source-generated P/Invoke#13576

Merged
JeremyKuhne merged 10 commits intodotnet:mainfrom
JeremyKuhne:cswin32
May 6, 2026
Merged

Migrate Windows interop to CsWin32 source-generated P/Invoke#13576
JeremyKuhne merged 10 commits intodotnet:mainfrom
JeremyKuhne:cswin32

Conversation

@JeremyKuhne
Copy link
Copy Markdown
Member

Replace hand-written [DllImport] declarations and COM [ComImport] interfaces with CsWin32 source-generated interop. This enables Native AOT scenarios where built-in COM marshalling and System.Runtime.InteropServices marshalling infrastructure do not exist. The approach follows patterns established in WinForms, WPF, and the dotnet/sdk repository.

CsWin32 is configured in Microsoft.Build.Framework with allowMarshaling: false and useSafeHandles: false, producing raw pointer signatures that are AOT-compatible. Other projects consume generated types via InternalsVisibleTo.

Key changes:

Infrastructure:

  • Add FEATURE_WINDOWSINTEROP compile-time gate in Directory.BeforeCommon.targets, disabled for source-only builds
  • Add CsWin32 (Microsoft.Windows.CsWin32) and PolySharp package references to Framework.csproj
  • Add NativeMethods.txt and NativeMethods.json for CsWin32 configuration
  • Add .editorconfig suppression for CS3019 in Windows/ folders
  • Add cswin32-interop skill document for agent guidance

New utility types:

  • BufferScope: ref struct for stack-allocated buffers with ArrayPool fallback, replacing manual ArrayPool rent/return and stackalloc patterns throughout interop code
  • TypeInfo: cached RuntimeHelpers.IsReferenceOrContainsReferences polyfill for net472
  • ComScope: COM pointer lifetime management (using-disposable)
  • ComClassFactory: AOT-compatible COM activation without Activator.CreateInstance
  • IID: generic IID lookup via IComIID interface
  • VARIANT, BSTR, HRESULT, FILETIME extensions: CsWin32 partial type augmentations for safe usage patterns

NativeMethods.cs cleanup:

  • Delete ~130 hand-written constants, enums, structs, and [DllImport] declarations replaced by CsWin32 typed equivalents
  • Replace MemoryStatus class with MEMORYSTATUSEX struct via TryGetMemoryStatus
  • Replace GetCurrentDirectory/GetFullPath/GetShortPathName/ GetLongPathName with BufferScope-based implementations
  • Replace StreamHandleType enum with bool useStandardError parameter
  • Delete DirectoryExists/FileExists/FileOrDirectoryExists wrappers; callers use PInvoke.GetFileAttributes directly
  • Delete HResultSucceeded/HResultFailed; callers use HRESULT.Failed
  • Add [UnsupportedOSPlatformGuard("windows")] to IsUnixLike
  • Version all [SupportedOSPlatform] attributes to "windows6.1"

COM interop modernization:

  • Replace [ComImport] WMI interfaces (IWbemLocator, IWbemServices, IEnumWbemClassObject, IWbemClassObject) with struct-based COM using delegate* unmanaged[Stdcall] vtables
  • Add IDebugClient4-based command line retrieval as alternative to WMI (CommandLineSource.DebugEngine)
  • Move IFixedTypeInfo from NativeMethods.cs to its own file in Tasks

Platform guard pattern:

  • Apply dual-guard pattern: #if FEATURE_WINDOWSINTEROP wraps runtime IsWindows checks, eliminating dead code in source builds
  • Use IsUnixLike (positive guard) instead of !IsWindows for CA1416 platform compatibility analysis
  • Files excluded at project level via when FeatureWindowsInterop is off (WindowsFileSystem, Windows/ folder, WMI structs)

Behavioral changes:

  • Unzip.cs: !IsWindows → IsUnixLike for correct CA1416 analysis
  • ProcessExtensions: ArrayPool replaced with BufferScope
  • AssemblyInformation/GlobalAssemblyCache: remove dead non-Windows code paths in net472-only (#if !FEATURE_ASSEMBLYLOADCONTEXT) blocks, which only run on Windows

Some related changes are also in #13426.

Copilot AI review requested due to automatic review settings April 20, 2026 03:08
@JeremyKuhne JeremyKuhne requested a review from a team as a code owner April 20, 2026 03:08
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented Apr 20, 2026

🔍 Skill Validator Results

✅ All checks passed

Scope Checked
Skills 2
Agents 0
Total 2
Severity Count
--- ---:
❌ Errors 0
⚠️ Warnings 0
ℹ️ Advisories 0

Summary

Level Finding
ℹ️ Found 2 skill(s)
ℹ️ [cswin32-com] 📊 cswin32-com: 1,258 BPE tokens [chars/4: 1,188] (detailed ✓), 8 sections, 3 code blocks
ℹ️ [cswin32-interop] 📊 cswin32-interop: 1,533 BPE tokens [chars/4: 1,474] (detailed ✓), 12 sections, 3 code blocks
ℹ️ ✅ All checks passed (2 skill(s))
Full validator output ```text Found 2 skill(s) [cswin32-com] 📊 cswin32-com: 1,258 BPE tokens [chars/4: 1,188] (detailed ✓), 8 sections, 3 code blocks [cswin32-interop] 📊 cswin32-interop: 1,533 BPE tokens [chars/4: 1,474] (detailed ✓), 12 sections, 3 code blocks ✅ All checks passed (2 skill(s)) ```

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Migrates MSBuild’s Windows interop surface from hand-written P/Invokes and [ComImport] COM interfaces to CsWin32 source-generated interop, with compile-time gating to support Native AOT and source-only builds.

Changes:

  • Introduces CsWin32 configuration + build gating (FEATURE_WINDOWSINTEROP / FeatureWindowsInterop) and updates projects/tests to consume Windows.Win32.PInvoke APIs.
  • Adds new interop helper types (e.g., BufferScope<T>, ComScope<T>, ComClassFactory, IID, partials for HRESULT/BSTR/VARIANT/FILETIME) to enable AOT-friendly patterns.
  • Reworks Windows command-line retrieval to support WMI + Debug Engine sources and expands unit test coverage for the new behavior.

Reviewed changes

Copilot reviewed 53 out of 53 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
src/Utilities.UnitTests/ProcessExtensions_Tests.cs Adds tests for new command-line retrieval sources and edge cases.
src/Tasks/Unzip.cs Adjusts platform guard logic for Unix file modes (CA1416-friendly).
src/Tasks/NativeMethods.cs Removes legacy COM interface and updates HRESULT usage for mscoree interop.
src/Tasks/Microsoft.Build.Tasks.csproj Includes moved IFixedTypeInfo definition and minor formatting.
src/Tasks/IFixedTypeInfo.cs Moves the fixed ITypeInfo COM interface into its own file.
src/Tasks/FileState.cs Switches file attribute queries to CsWin32 GetFileAttributesEx.
src/Tasks/ComReference.cs Uses HMODULE + PInvoke.LoadLibrary/FreeLibrary/GetModuleFileName with BufferScope.
src/Tasks/AssemblyDependency/GlobalAssemblyCache.cs Uses CsWin32 HRESULT.S_OK and removes dead non-Windows path for net472.
src/Tasks/AssemblyDependency/AssemblyInformation.cs Removes dead non-Windows net472 paths and updates HRESULT / buffer logic.
src/Tasks.UnitTests/AddToWin32Manifest_Tests.cs Updates platform attributes and switches to PInvoke.FreeLibrary.
src/Shared/UnitTests/NativeMethodsShared_Tests.cs Updates to PInvoke.LoadLibrary/GetProcAddress/FreeLibrary and versioned platform attributes.
src/Framework/Windows/Win32/System/Variant/VARIANT.cs Adds disposable/utility partial for CsWin32 VARIANT.
src/Framework/Windows/Win32/System/Com/ComScope.cs Adds COM pointer lifetime management for struct-based COM.
src/Framework/Windows/Win32/System/Com/ComClassFactory.cs Adds AOT-friendly COM activation via CoGetClassObject.
src/Framework/Windows/Win32/PInvoke.GetFileAttributesEx.cs Adds a typed overload wrapper for GetFileAttributesEx.
src/Framework/Windows/Win32/IID.cs Adds generic IID lookup helper for IComIID COM structs.
src/Framework/Windows/Win32/GeneratedInteropClsCompliance.cs Applies [CLSCompliant(false)] via partials for generated COM structs.
src/Framework/Windows/Win32/Foundation/HRESULT.cs Adds helper conversions and FromLastError() for HRESULT.
src/Framework/Windows/Win32/Foundation/FileTimeExtensions.cs Adds FILETIME conversion helper (ToLong).
src/Framework/Windows/Win32/Foundation/BSTR.cs Adds disposable/safe construction partial for BSTR.
src/Framework/Utilities/Wmi/IWbemServices.cs Adds struct-based COM definition for WMI IWbemServices.
src/Framework/Utilities/Wmi/IWbemLocator.cs Adds struct-based COM definition for WMI IWbemLocator (incl. CLSID).
src/Framework/Utilities/Wmi/IWbemClassObject.cs Adds struct-based COM definition for WMI IWbemClassObject.
src/Framework/Utilities/Wmi/IEnumWbemClassObject.cs Adds struct-based COM definition for WMI enumeration.
src/Framework/Utilities/TypeInfo.cs Adds cached IsReferenceOrContainsReferences helper (net472 polyfill).
src/Framework/Utilities/ProcessExtensions.cs Adds selectable Windows command-line sources (WMI / DebugEngine) and BufferScope usage.
src/Framework/Utilities/BufferScope.cs Adds stackalloc + ArrayPool buffer helper used across interop.
src/Framework/Telemetry/CrashTelemetry.cs Switches memory stats gathering to MEMORYSTATUSEX via TryGetMemoryStatus.
src/Framework/SupportedOSPlatform.cs Adds UnsupportedOSPlatformGuardAttribute polyfill for older TFMs.
src/Framework/Shared/TrimmingAttributes.cs Adds trimming/AOT attribute polyfills for net472/netstandard2.0.
src/Framework/NativeMethods.txt Declares the Win32 APIs/types for CsWin32 generation.
src/Framework/NativeMethods.json Configures CsWin32 generation (no marshaling, no safe handles, preserve sig).
src/Framework/NativeMethods.cs Large cleanup: replaces hand-written interop with CsWin32 calls + adds guards/helpers.
src/Framework/Microsoft.Build.Framework.csproj Adds CsWin32/PolySharp refs + compile removes/includes for feature-gated interop files.
src/Framework/Framework/Windows/Win32/IComIID.cs Adds net472 instance-based IComIID polyfill for COM structs.
src/Framework/FileSystem/WindowsFileSystem.cs Switches existence checks to PInvoke.GetFileAttributes.
src/Framework/FileSystem/MSBuildOnWindowsFileSystem.cs Updates [SupportedOSPlatform] versioning.
src/Framework/FileSystem/FileSystems.cs Wraps Windows file-system selection in FEATURE_WINDOWSINTEROP.
src/Framework/EncodingUtilities.cs Switches OEM code page query to PInvoke.GetOEMCP.
src/Framework/CommunicationsUtilities.cs Versions [SupportedOSPlatform] attributes to windows6.1.
src/Framework.UnitTests/TypeInfo_Tests.cs Adds tests for the new TypeInfo<T> helper.
src/Framework.UnitTests/BufferScope_Tests.cs Adds tests for BufferScope<T> behavior and edge cases.
src/Directory.BeforeCommon.targets Introduces FEATURE_WINDOWSINTEROP define + FeatureWindowsInterop property gate.
src/Build/Logging/SimpleErrorLogger.cs Updates console mode enable/restore calls to new NativeMethods API signature.
src/Build/Logging/InProcessConsoleConfiguration.cs Switches console mode/file type checks to CsWin32 APIs under feature guard.
src/Build/BackEnd/Components/Communications/NodeLauncher.cs Updates Windows handle closing to PInvoke.CloseHandle and improves platform branching.
src/Build/BackEnd/Components/BuildRequestEngine/BuildRequestEngine.cs Switches memory checks to MEMORYSTATUSEX under FEATURE_WINDOWSINTEROP.
src/Build.UnitTests/ConsoleLogger_Tests.cs Switches stdout handle/file type checks to CsWin32 APIs.
src/Build.UnitTests/BackEnd/TargetUpToDateChecker_Tests.cs Updates platform attributes and uses typed enums for CreateFile flags.
eng/dependabot/Directory.Packages.props Adds package versions for CsWin32 and PolySharp.
AGENTS.md Updates repo guidance to reflect newer C# usage expectations.
.github/skills/cswin32-interop/SKILL.md Adds internal guidance doc for CsWin32-based interop migration.
.editorconfig Suppresses CS3019 for CsWin32-generated/interop folders.
Comments suppressed due to low confidence (1)

src/Framework/NativeMethods.cs:1243

  • QueryIsScreenAndTryEnableAnsiColorCodes always checks Console.IsOutputRedirected, even when useStandardError: true. This means stderr redirection won’t be detected correctly (should use Console.IsErrorRedirected when targeting stderr), potentially enabling ANSI output when stderr is redirected or disabling it when only stdout is redirected.
    internal static (bool acceptAnsiColorCodes, bool outputIsScreen, uint? originalConsoleMode) QueryIsScreenAndTryEnableAnsiColorCodes(bool useStandardError = false)
    {
        if (Console.IsOutputRedirected)
        {
            // There's no ANSI terminal support if console output is redirected.
            return (acceptAnsiColorCodes: false, outputIsScreen: false, originalConsoleMode: null);
        }

Comment thread src/Framework/Utilities/ProcessExtensions.cs
Comment thread src/Framework/Utilities/TypeInfo.cs Outdated
Comment thread src/Utilities.UnitTests/ProcessExtensions_Tests.cs
Comment thread src/Tasks/ComReference.cs
Comment thread src/Framework/FileSystem/WindowsFileSystem.cs Outdated
Comment thread src/Framework/NativeMethods.cs Outdated
Comment thread src/Utilities.UnitTests/ProcessExtensions_Tests.cs
Comment thread src/Utilities.UnitTests/ProcessExtensions_Tests.cs
Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

# Dimension Verdict
1 Backwards Compatibility ✅ LGTM
2 ChangeWave Discipline ✅ LGTM (no behavioral change)
3 Performance & Allocation ⚠️ MODERATE — HasFlag() boxing on net472 hot paths
4 Test Coverage ✅ LGTM — Comprehensive new tests for BufferScope, TypeInfo, ProcessExtensions
5 Error Message Quality ✅ LGTM
6 Logging & Diagnostics ✅ LGTM
7 String Comparison ✅ LGTM
8 API Surface Discipline ✅ LGTM — All removed members were internal effective visibility
9 Cross-Platform Correctness ❌ BLOCKING — NodeLauncher.cs compile failure in source builds
10 Target Conventions ✅ LGTM
11 Documentation ✅ LGTM — Thorough SKILL.md and AGENTS.md updates
12 Code Simplification ✅ LGTM — Clean removal of dead code
13 Concurrency & Thread Safety ✅ LGTM
14 Naming Conventions ✅ LGTM
15 SDK Integration ✅ LGTM
16 Evaluation Model ✅ LGTM
17 Serialization ✅ LGTM
18 Incremental Build ✅ LGTM
19 Resource Strings ✅ LGTM
20 Interop Correctness ✅ LGTM — GUID bytes, vtable indices, COM lifetime, BSTR/VARIANT all correct
21 Trimmability / AOT ✅ LGTM — Good TrimmingAttributes polyfill
22 Adversarial Input ✅ LGTM
23 Dependency Management ✅ LGTM — CsWin32 and PolySharp appropriately configured
24 Security ✅ LGTM

Overall Assessment

This is a well-executed CsWin32 migration. The new BufferScope<T>, ComScope<T>, and struct-based COM patterns are clean and follow established conventions (dotnet/sdk patterns). The WMI interface vtable indices and GUID byte sequences are correct. Test coverage is comprehensive.

1 BLOCKING issue:

  • NodeLauncher.cs — The return keyword is inside #if FEATURE_WINDOWSINTEROP, causing a compile error in source builds where the flag is not defined. The #if/ternary split leaves a bare conditional expression without a return statement.

2 MODERATE issues:

  • HasFlag() on hot pathsWindowsFileSystem.DirectoryExists and NativeMethods.LastWriteFileUtcTime use HasFlag() which boxes on .NET Framework. The rest of the PR correctly uses bitwise & for these checks. This is easily fixed for consistency.

Positive highlights:

  • Excellent BufferScope<T> utility replacing new char[] with stackalloc + ArrayPool
  • Correct dual guard pattern (#if FEATURE_WINDOWSINTEROP + if (IsWindows)) throughout
  • Clean COM lifetime management with ComScope<T> and using declarations
  • New CommandLineSource.DebugEngine alternative to WMI is a nice addition
  • Comprehensive test coverage for new utilities and interop paths

Note

🔒 Integrity filter blocked 2 items

The following items were blocked because they don't meet the GitHub integrity level.

  • #13576 pull_request_read: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".
  • #13576 pull_request_read: has lower integrity than agent requires. The agent cannot read data with integrity below "approved".

To allow these resources, lower min-integrity in your GitHub frontmatter:

tools:
  github:
    min-integrity: approved  # merged | approved | unapproved | none

Generated by Expert Code Review (on open) for issue #13576 · ● 18.4M

Comment thread src/Framework/SupportedOSPlatform.cs Outdated
Comment thread src/Framework/Utilities/TypeInfo.cs Outdated
Comment thread src/Framework/Shared/TrimmingAttributes.cs Outdated
Comment thread src/Framework/Utilities/ProcessExtensions.cs Outdated
Comment thread src/Framework/Utilities/ProcessExtensions.cs Outdated
Comment thread src/Framework/Windows/Win32/Foundation/HRESULT.cs
Comment thread src/Framework/Microsoft.Build.Framework.csproj
@rainersigwald
Copy link
Copy Markdown
Member

  • Version all [SupportedOSPlatform] attributes to "windows6.1"

Could you give a brief explanation of why this is helpful please?

@JeremyKuhne
Copy link
Copy Markdown
Member Author

  • Version all [SupportedOSPlatform] attributes to "windows6.1"

Could you give a brief explanation of why this is helpful please?

Because you can't mix and match versioned and unversioned attributes without getting warnings. CsWin32 puts the actual version on the attributes it adds.

@JeremyKuhne
Copy link
Copy Markdown
Member Author

I intend to follow up with the rest of the interop, didn't want to make this any bigger than it already was, but still wanted enough to stress the skill doc.

@JeremyKuhne
Copy link
Copy Markdown
Member Author

Rebased and force pushed to attempt to satisfy the skill validator, but it is still failing. :/

@JeremyKuhne
Copy link
Copy Markdown
Member Author

The skill validator appears to have an issue with it. Put up #13592. @JanKrivanek

@JeremyKuhne
Copy link
Copy Markdown
Member Author

Rebased and force pushed to pick up the Skill Validation fix.

@JeremyKuhne
Copy link
Copy Markdown
Member Author

Rebased to fix merge conflicts

@JeremyKuhne
Copy link
Copy Markdown
Member Author

Rebased again to fix merge conflicts

Replace hand-written [DllImport] declarations and COM [ComImport]
interfaces with CsWin32 source-generated interop. This enables
Native AOT scenarios where built-in COM marshalling and
System.Runtime.InteropServices marshalling infrastructure do not
exist. The approach follows patterns established in WinForms, WPF,
and the dotnet/sdk repository.

CsWin32 is configured in Microsoft.Build.Framework with
allowMarshaling: false and useSafeHandles: false, producing raw
pointer signatures that are AOT-compatible. Other projects consume
generated types via InternalsVisibleTo.

Key changes:

Infrastructure:
- Add FEATURE_WINDOWSINTEROP compile-time gate in
  Directory.BeforeCommon.targets, disabled for source-only builds
- Add CsWin32 (Microsoft.Windows.CsWin32) and PolySharp package
  references to Framework.csproj
- Add NativeMethods.txt and NativeMethods.json for CsWin32
  configuration
- Add .editorconfig suppression for CS3019 in Windows/ folders
- Add cswin32-interop skill document for agent guidance

New utility types:
- BufferScope<T>: ref struct for stack-allocated buffers with
  ArrayPool fallback, replacing manual ArrayPool rent/return and
  stackalloc patterns throughout interop code
- TypeInfo<T>: cached RuntimeHelpers.IsReferenceOrContainsReferences
  polyfill for net472
- ComScope<T>: COM pointer lifetime management (using-disposable)
- ComClassFactory: AOT-compatible COM activation without
  Activator.CreateInstance
- IID: generic IID lookup via IComIID interface
- VARIANT, BSTR, HRESULT, FILETIME extensions: CsWin32 partial
  type augmentations for safe usage patterns

NativeMethods.cs cleanup:
- Delete ~130 hand-written constants, enums, structs, and
  [DllImport] declarations replaced by CsWin32 typed equivalents
- Replace MemoryStatus class with MEMORYSTATUSEX struct via
  TryGetMemoryStatus
- Replace GetCurrentDirectory/GetFullPath/GetShortPathName/
  GetLongPathName with BufferScope-based implementations
- Replace StreamHandleType enum with bool useStandardError parameter
- Delete DirectoryExists/FileExists/FileOrDirectoryExists wrappers;
  callers use PInvoke.GetFileAttributes directly
- Delete HResultSucceeded/HResultFailed; callers use HRESULT.Failed
- Add [UnsupportedOSPlatformGuard("windows")] to IsUnixLike
- Version all [SupportedOSPlatform] attributes to "windows6.1"

COM interop modernization:
- Replace [ComImport] WMI interfaces (IWbemLocator, IWbemServices,
  IEnumWbemClassObject, IWbemClassObject) with struct-based COM
  using delegate* unmanaged[Stdcall] vtables
- Add IDebugClient4-based command line retrieval as alternative to
  WMI (CommandLineSource.DebugEngine)
- Move IFixedTypeInfo from NativeMethods.cs to its own file in Tasks

Platform guard pattern:
- Apply dual-guard pattern: #if FEATURE_WINDOWSINTEROP wraps runtime
  IsWindows checks, eliminating dead code in source builds
- Use IsUnixLike (positive guard) instead of !IsWindows for CA1416
  platform compatibility analysis
- Files excluded at project level via <Compile Remove> when
  FeatureWindowsInterop is off (WindowsFileSystem, Windows/ folder,
  WMI structs)

Behavioral changes:
- Unzip.cs: !IsWindows → IsUnixLike for correct CA1416 analysis
- ProcessExtensions: ArrayPool<T> replaced with BufferScope<T>
- AssemblyInformation/GlobalAssemblyCache: remove dead non-Windows
  code paths in net472-only (#if !FEATURE_ASSEMBLYLOADCONTEXT)
  blocks, which only run on Windows

Some related changes are also in dotnet#13426.
@JeremyKuhne
Copy link
Copy Markdown
Member Author

#13610 broke this. I'm updating with both options (WMI, debugger) behind opt-ins.

Comment thread src/Tasks/ComReference.cs
Comment thread src/Framework/Microsoft.Build.Framework.csproj Outdated
Comment thread src/Framework.UnitTests/BufferScope_Tests.cs
Comment thread src/Framework.UnitTests/TypeInfo_Tests.cs
Comment thread src/Framework/Utilities/Wmi/IEnumWbemClassObject.cs
Comment thread src/Framework/Utilities/TypeInfo.cs
Comment thread src/Framework/NativeMethods.cs Outdated
@JeremyKuhne
Copy link
Copy Markdown
Member Author

/ba-g #13703 is the only failure.

@JeremyKuhne JeremyKuhne enabled auto-merge (squash) May 6, 2026 17:47
@JeremyKuhne
Copy link
Copy Markdown
Member Author

WriteLinesToFile_Tests.TransactionalModePreservesAllData and DistributedFileLoggers test failed this time.

@JeremyKuhne JeremyKuhne merged commit fbf2ce7 into dotnet:main May 6, 2026
11 checks passed
@JeremyKuhne JeremyKuhne deleted the cswin32 branch May 6, 2026 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants