Skip to content

Multi-target System.Private.Windows.Core#14384

Open
JeremyKuhne wants to merge 6 commits intodotnet:mainfrom
JeremyKuhne:mpcore
Open

Multi-target System.Private.Windows.Core#14384
JeremyKuhne wants to merge 6 commits intodotnet:mainfrom
JeremyKuhne:mpcore

Conversation

@JeremyKuhne
Copy link
Member

@JeremyKuhne JeremyKuhne commented Mar 11, 2026

This builds our shared functionality assembly for .NET Framework 4.7.2 as Microsoft.Private.Windows.Core with the same namespaces. This is for internal scenarios where we need this shared functionality on .NET Framework.

Static and default interface methods don't work on .NET Framework.

For our composition usages of static interfaces we build the same interface with instance methods and constrain the consuming types to new() as well so they can create a static instance of the T that they need.

On Framework we still use direct COM to look at existing COM objects. For CCWs, however, we let Marshal provide them on Framework. Most of the complexity around that is with unwrapping DataObject. I believe things should work as expected but it is a particular focus point for both testing and following up with issues when utilizing this code.

There are some CsWin32 tweaks that are necessary, notably around IComIId. Added partials for the COM classes where we needed them.

Framework only code is in the Framework subfolder and ingested through the previously committed shared polyfill project.

Some tests needed to be tweaked for a few reasons. One in particular was that some validated implementation details rather than functional.

Some shared test functionality (notably validators that depend on static numeric interfaces) was simply excluded on .NET Framework as it wasn't being used in the tests that we needed to multitarget. .NET Framework versions can be added if/when needed.

Microsoft Reviewers: Open in CodeFlow

This builds our shared functionality assembly for .NET Framework 4.7.2 as Microsoft.Private.Windows.Core with the same namespaces. This is for internal scenarios where we need this shared functionality on .NET Framework.

Static and default interface methods don't work on .NET Framework.

For our composition usages of static interfaces we build the same interface with instance methods and constrain the consuming types to `new()` as well so they can create a static instance of the `T` that they need.

On Framework we still use direct COM to look at existing COM objects. For CCWs, however, we let `Marshal` provide them on Framework. Most of the complexity around that is with unwrapping DataObject. I believe things should work as expected but it is a particular focus point for both testing and following up with issues when utilizing this code.

There are some CsWin32 tweaks that are necessary, notably around `IComIId`. Added partials for the COM classes where we needed them.

Framework only code is in the `Framework` subfolder and ingested through the previously committed shared polyfill  project.

Some tests needed to be tweaked for a few reasons. One in particular was that some validated implementation details rather than functional.

Some shared test functionality (notably validators that depend on static numeric interfaces) was simply excluded on .NET Framework as it wasn't being used in the tests that we needed to multitarget. .NET Framework versions can be added if/when needed.
@JeremyKuhne JeremyKuhne requested a review from a team as a code owner March 11, 2026 17:33
@github-actions github-actions bot added the area-Clipboard Issues related to Clipboard label Mar 11, 2026
Copy link
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

Multi-targets System.Private.Windows.Core for .NET Framework 4.7.2 as Microsoft.Private.Windows.Core, adapting static interface methods to instance methods and handling COM interop differences.

Changes:

  • Adds Microsoft.Private.Windows.Core.csproj targeting net472 with #if NET/#if NETFRAMEWORK conditionals throughout for static vs instance interface methods, COM interop, and JSON serialization
  • Adapts test infrastructure and tests to multi-target net481, with Framework-specific workarounds for clipboard, serialization, and COM behaviors
  • Adds Framework-only IComIID partials, polyfills (IsSZArray), and DataObjectFactory helper for non-static interface consumption

Reviewed changes

Copilot reviewed 110 out of 111 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
Microsoft.Private.Windows.Core.csproj New project file for .NET Framework build
IComIID.cs (Framework) Instance-based IComIID interface for Framework
Framework COM partials IComIID implementations for IUnknown, IDataObject, IStream, etc.
DataObjectFactory.cs Helper to create data objects via instance methods on Framework
IOleServices.cs, INrbfSerializer.cs, IDataFormat.cs, IDataObjectInternal.cs Conditional static abstract vs instance interface members
ClipboardCore.cs, Composition.cs, DataStore.cs, DragDropHelper.cs, BinaryFormatUtilities.cs, TypeBinder.cs Add s_oleServices/s_nrbfSerializer instances and conditional dispatch
ComScope.cs, ComHelpers.cs Alias IUnknown as ComUnknown for Framework XML doc compatibility
IID.cs Framework path using default(T).Guid instead of T.Guid
PInvokeCore.Enum*.cs Framework callback marshaling via delegate + GetFunctionPointerForDelegate
SpanReader.cs, SpanWriter.cs, BinaryReaderExtensions.cs Framework-safe span/memory operations
IHandle.cs, NullHandle.cs, HdcHandle.cs Add explicit Wrapper property for Framework
StreamExtensions.cs Handle zero-size HGLOBAL allocation
Various test files Multi-target adaptations, conditional test exclusions
TestUtilities Multi-target support, polyfills, NoAssertContext initialization
VARIANT.cs Framework fallback to Marshal.GetObjectForNativeVariant
OLE_HANDLE.cs Fix sign extension with explicit (nint) cast
IRecord.cs, NullRecord.cs, etc. Make Id a required property, remove RecordMap
DataObjectExtensions.cs Convert to C# 14 extension type syntax
Comments suppressed due to low confidence (3)

src/System.Private.Windows.Core/src/System/Private/Windows/Ole/DataObjectExtensions.cs:1

  • This uses C# 14 extension types syntax (extension(...)), which requires .NET 9+ / C# 14 preview. Since this PR targets .NET Framework 4.7.2, this file would fail to compile on Framework unless it's excluded. Verify this file is excluded from the Framework build, or use a traditional extension method with #if guards.
    src/System.Private.Windows.Core/src/System/Private/Windows/Ole/DragDropHelper.cs:1
  • The original code used Marshal.GetLastSystemError() which was changed to Marshal.GetLastWin32Error(). GetLastSystemError() was introduced in .NET 8 and maps errno on Unix; GetLastWin32Error() is correct for Windows-only P/Invoke scenarios and works on both .NET and .NET Framework, so this change is appropriate for multi-targeting. However, if the original GetLastSystemError() was intentional for a reason, this is a behavioral change on .NET.
    src/System.Private.Windows.Core/src/System/Private/Windows/Ole/Composition.NativeToManagedAdapter.cs:1
  • This null/whitespace guard was added to GetDataPresent but not to the other methods that take format (e.g., GetData). If this is needed to prevent a downstream crash on Framework, consider applying it consistently or documenting why only GetDataPresent needs it.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area-Clipboard Issues related to Clipboard

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants