diff --git a/src/NuGet.Config b/src/NuGet.Config
index 6a6b25e19d..1458db55b2 100644
--- a/src/NuGet.Config
+++ b/src/NuGet.Config
@@ -4,6 +4,7 @@
+
diff --git a/src/VSIntegration2022.sln b/src/VSIntegration2022.sln
index bc753ca6fb..1da52e1b7a 100644
--- a/src/VSIntegration2022.sln
+++ b/src/VSIntegration2022.sln
@@ -33,6 +33,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Debugger2022", "VisualStudi
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProjectPackage2022", "VisualStudio\ProjectPackage\ProjectPackage2022.csproj", "{F4ADEBDB-CD20-4532-B4E3-FE7424F5369D}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "XSharp.ProjectSystem2022", "VisualStudio\ProjectSystem\XSharp.ProjectSystem2022.csproj", "{AB494DCE-A11B-4583-851E-D4A86775B3F0}"
+EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Vs2022", "Vs2022", "{0D3C69B5-D236-42B3-B70D-2C9ABC9D434C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Common", "Common", "{9F285506-97D3-486E-B59E-7C30C56B95AE}"
@@ -133,6 +135,18 @@ Global
{F4ADEBDB-CD20-4532-B4E3-FE7424F5369D}.Release|arm64.Build.0 = Release|arm64
{F4ADEBDB-CD20-4532-B4E3-FE7424F5369D}.Release|x86.ActiveCfg = Release|x86
{F4ADEBDB-CD20-4532-B4E3-FE7424F5369D}.Release|x86.Build.0 = Release|x86
+ {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Debug|arm64.ActiveCfg = Debug|Any CPU
+ {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Debug|arm64.Build.0 = Debug|Any CPU
+ {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Debug|x86.ActiveCfg = Debug|Any CPU
+ {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Debug|x86.Build.0 = Debug|Any CPU
+ {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Release|Any CPU.Build.0 = Release|Any CPU
+ {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Release|arm64.ActiveCfg = Release|Any CPU
+ {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Release|arm64.Build.0 = Release|Any CPU
+ {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Release|x86.ActiveCfg = Release|Any CPU
+ {AB494DCE-A11B-4583-851E-D4A86775B3F0}.Release|x86.Build.0 = Release|Any CPU
{B837AC3B-9C61-49E4-9F4F-C8AD1FAF21CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B837AC3B-9C61-49E4-9F4F-C8AD1FAF21CC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B837AC3B-9C61-49E4-9F4F-C8AD1FAF21CC}.Debug|arm64.ActiveCfg = Debug|Any CPU
@@ -228,6 +242,7 @@ Global
{4484939E-DDA8-4033-8B1C-E8F60A2BAFE1} = {0D3C69B5-D236-42B3-B70D-2C9ABC9D434C}
{16272550-75E6-4B9D-91E3-653BD141C756} = {0D3C69B5-D236-42B3-B70D-2C9ABC9D434C}
{F4ADEBDB-CD20-4532-B4E3-FE7424F5369D} = {0D3C69B5-D236-42B3-B70D-2C9ABC9D434C}
+ {AB494DCE-A11B-4583-851E-D4A86775B3F0} = {0D3C69B5-D236-42B3-B70D-2C9ABC9D434C}
{B837AC3B-9C61-49E4-9F4F-C8AD1FAF21CC} = {9F285506-97D3-486E-B59E-7C30C56B95AE}
{7619DB88-6286-4EF1-8273-2A08CC8AC70E} = {9F285506-97D3-486E-B59E-7C30C56B95AE}
{EED6843B-B727-4F08-AA32-CA0F48FDDE3F} = {9F285506-97D3-486E-B59E-7C30C56B95AE}
diff --git a/src/VisualStudio/ProjectSystem/Properties/AssemblyInfo.cs b/src/VisualStudio/ProjectSystem/Properties/AssemblyInfo.cs
new file mode 100644
index 0000000000..59dde995bc
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/Properties/AssemblyInfo.cs
@@ -0,0 +1,10 @@
+using System.Reflection;
+using System.Runtime.InteropServices;
+
+// General information
+[assembly: AssemblyTitle("XSharp CPS Project System")]
+[assembly: AssemblyDescription("CPS-based XSharp project system for Visual Studio 2022 and later")]
+[assembly: AssemblyCompany("XSharp B.V.")]
+[assembly: AssemblyProduct("XSharp")]
+[assembly: AssemblyCopyright("Copyright © XSharp B.V. 2024")]
+[assembly: ComVisible(false)]
diff --git a/src/VisualStudio/ProjectSystem/PropertyPages/XSharpBuildPropertyPageProvider.cs b/src/VisualStudio/ProjectSystem/PropertyPages/XSharpBuildPropertyPageProvider.cs
new file mode 100644
index 0000000000..99fb3bb8b6
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/PropertyPages/XSharpBuildPropertyPageProvider.cs
@@ -0,0 +1,38 @@
+//
+// Copyright (c) XSharp B.V. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0.
+// See License.txt in the project root for license information.
+//
+
+using System.ComponentModel.Composition;
+
+using Microsoft.VisualStudio.ProjectSystem;
+using Microsoft.VisualStudio.ProjectSystem.VS.Properties;
+
+namespace XSharp.VisualStudio.ProjectSystem.PropertyPages
+{
+ ///
+ /// CPS property page metadata for the XSharp "Build" page.
+ ///
+ /// This page surfaces the MSBuild properties defined in
+ /// Rules/XSharp.xaml under the Build rule category.
+ /// It mirrors XSharpBuildPropertyPage in the MPF AppDesigner.
+ ///
+ [Export(typeof(IPageMetadata))]
+ [AppliesTo(XSharpProjectCapabilities.XSharp)]
+ internal sealed class XSharpBuildPropertyPageProvider : IPageMetadata
+ {
+ ///
+ public string Name => "Build";
+
+ ///
+ public System.Guid PageGuid =>
+ new System.Guid(XSharpConstants.BuildPropertiesPage);
+
+ ///
+ public int PageOrder => 200;
+
+ ///
+ public bool HasConfigurationCondition => true;
+ }
+}
diff --git a/src/VisualStudio/ProjectSystem/PropertyPages/XSharpDebugPropertyPageProvider.cs b/src/VisualStudio/ProjectSystem/PropertyPages/XSharpDebugPropertyPageProvider.cs
new file mode 100644
index 0000000000..234140ea9e
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/PropertyPages/XSharpDebugPropertyPageProvider.cs
@@ -0,0 +1,38 @@
+//
+// Copyright (c) XSharp B.V. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0.
+// See License.txt in the project root for license information.
+//
+
+using System.ComponentModel.Composition;
+
+using Microsoft.VisualStudio.ProjectSystem;
+using Microsoft.VisualStudio.ProjectSystem.VS.Properties;
+
+namespace XSharp.VisualStudio.ProjectSystem.PropertyPages
+{
+ ///
+ /// CPS property page metadata for the XSharp "Debug" page.
+ ///
+ /// This page surfaces the MSBuild properties defined in
+ /// Rules/XSharp.xaml under the Debug rule category.
+ /// It mirrors XSharpDebugPropertyPage in the MPF AppDesigner.
+ ///
+ [Export(typeof(IPageMetadata))]
+ [AppliesTo(XSharpProjectCapabilities.XSharp)]
+ internal sealed class XSharpDebugPropertyPageProvider : IPageMetadata
+ {
+ ///
+ public string Name => "Debug";
+
+ ///
+ public System.Guid PageGuid =>
+ new System.Guid(XSharpConstants.DebugPropertiesPage);
+
+ ///
+ public int PageOrder => 300;
+
+ ///
+ public bool HasConfigurationCondition => true;
+ }
+}
diff --git a/src/VisualStudio/ProjectSystem/PropertyPages/XSharpGeneralPropertyPageProvider.cs b/src/VisualStudio/ProjectSystem/PropertyPages/XSharpGeneralPropertyPageProvider.cs
new file mode 100644
index 0000000000..472fac43c7
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/PropertyPages/XSharpGeneralPropertyPageProvider.cs
@@ -0,0 +1,41 @@
+//
+// Copyright (c) XSharp B.V. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0.
+// See License.txt in the project root for license information.
+//
+
+using System.ComponentModel.Composition;
+using System.Runtime.InteropServices;
+
+using Microsoft.VisualStudio.ProjectSystem;
+using Microsoft.VisualStudio.ProjectSystem.VS.Properties;
+using Microsoft.VisualStudio.Shell;
+
+namespace XSharp.VisualStudio.ProjectSystem.PropertyPages
+{
+ ///
+ /// CPS property page metadata for the XSharp "Application" (General) page.
+ ///
+ /// This page surfaces the MSBuild properties defined in
+ /// Rules/XSharp.xaml under the Application rule category.
+ /// It mirrors the information shown by XSharpGeneralPropertyPage
+ /// in the MPF-based AppDesigner.
+ ///
+ [Export(typeof(IPageMetadata))]
+ [AppliesTo(XSharpProjectCapabilities.XSharp)]
+ internal sealed class XSharpGeneralPropertyPageProvider : IPageMetadata
+ {
+ ///
+ public string Name => "Application";
+
+ ///
+ public System.Guid PageGuid =>
+ new System.Guid(XSharpConstants.GeneralPropertiesPage);
+
+ ///
+ public int PageOrder => 100;
+
+ ///
+ public bool HasConfigurationCondition => false;
+ }
+}
diff --git a/src/VisualStudio/ProjectSystem/README.md b/src/VisualStudio/ProjectSystem/README.md
new file mode 100644
index 0000000000..2fa3457a50
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/README.md
@@ -0,0 +1,66 @@
+# XSharp CPS Project System
+
+This folder contains the **CPS-based (Common Project System)** implementation of the XSharp project system for Visual Studio 2022 and later.
+
+## Overview
+
+| Path | Purpose |
+|---|---|
+| `XSharp.ProjectSystem2022.csproj` | Project file (net472, VS2022 SDK references) |
+| `XSharpProjectCapabilities.cs` | CPS capability constants (`XSharp`, `Managed`, `MultipleTargetFrameworks`, …) |
+| `XSharpProjectCapabilitiesProvider.cs` | MEF export that advertises capabilities to CPS |
+| `XSharpProjectType.cs` | Binds the `.xsproj` extension and project GUID to this CPS project system |
+| `XSharpProjectTreePropertiesProvider.cs` | Custom icons for `.xsfrm`, `.xsmnu`, `.prg`, `.xaml`, etc. |
+| `XSharpProjectBuildProvider.cs` | Build-property integration + up-to-date check stub |
+| `XSharpProjectProperties.cs` | Strongly-typed wrappers around MSBuild project properties |
+| `XSharpTargetFrameworkProvider.cs` | Multi-targeting support (VS2022+ only — reads ``) |
+| `XSharpCodeDomIntegration.cs` | Bridge to the existing XSharp CodeDom provider for Windows Forms designer |
+| `PropertyPages/*.cs` | CPS `IPageMetadata` exports for Application, Build, and Debug pages |
+| `Rules/XSharp.xaml` | MSBuild property rules (XAML format) surfaced in the project designer |
+| `Rules/XSharpMultiTarget.xaml` | `TargetFrameworks` rule for multi-targeting (VS2022+ only) |
+| `XSharpCpsPackage.cs` | VS AsyncPackage that registers CPS components |
+| `XSharpCpsPackage.pkgdef` | Registry entries that activate CPS for `.xsproj` in VS2022+ |
+
+## VS Version Strategy
+
+| Feature | VS2019 | VS2022 / VS2026 |
+|---|---|---|
+| Project system | **MPF** (`ProjectBase` + `ProjectPackage`) | **CPS** (this assembly) |
+| Multi-targeting | ❌ not supported | ✅ via `` |
+| VO binary editors (`.xsfrm`, `.xsmnu`, …) | ✅ | ✅ |
+| CodeDom / WinForms designer | ✅ | ✅ |
+| Project designer pages | ✅ AppDesigner | ✅ AppDesigner + CPS rules |
+
+The MPF code in `ProjectBase` and `ProjectPackage` is **not modified**. The CPS package activates only in VS2022+ via the `ProjectSystemPackage` registry entry in `XSharpCpsPackage.pkgdef`.
+
+## Multi-Targeting (VS2022+ only)
+
+SDK-style `.xsproj` files can use `` to target multiple frameworks simultaneously:
+
+```xml
+
+
+ net48;net6.0-windows
+ Core
+
+
+```
+
+> **Note:** The VS2019 / MPF path does **not** support multi-targeting.
+> Use a single `` value when VS2019 compatibility is required.
+
+## Custom Editors
+
+The VO binary file editors (`.xsfrm`, `.xsmnu`, `.xsdbs`, `.xsfs`) are implemented in the `ProjectPackage` assembly and registered both there (for MPF) and in `XSharpCpsPackage.pkgdef` (for CPS). No code duplication is required — the factory implementations are shared.
+
+## CodeDom / Windows Forms
+
+`XSharpCodeDomIntegration` locates `VSXsharpCodeDomProvider` in the `ProjectPackage` assembly at run-time and exposes it through the MEF `ICodeDomProviderIntegration` interface so the Windows Forms designer can generate XSharp source code in the CPS context.
+
+## Building
+
+This project is included in `src/VSIntegration2022.sln`. It is **not** included in `src/VSIntegration.sln` (VS2019).
+
+```
+msbuild src/VSIntegration2022.sln /p:Configuration=Release
+```
diff --git a/src/VisualStudio/ProjectSystem/Rules/XSharp.xaml b/src/VisualStudio/ProjectSystem/Rules/XSharp.xaml
new file mode 100644
index 0000000000..9ae664be22
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/Rules/XSharp.xaml
@@ -0,0 +1,220 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/VisualStudio/ProjectSystem/Rules/XSharpMultiTarget.xaml b/src/VisualStudio/ProjectSystem/Rules/XSharpMultiTarget.xaml
new file mode 100644
index 0000000000..54ee2afbd5
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/Rules/XSharpMultiTarget.xaml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/VisualStudio/ProjectSystem/XSharp.ProjectSystem2022.csproj b/src/VisualStudio/ProjectSystem/XSharp.ProjectSystem2022.csproj
new file mode 100644
index 0000000000..843376060b
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/XSharp.ProjectSystem2022.csproj
@@ -0,0 +1,164 @@
+
+
+
+
+
+
+
+ Library
+ XSharp.VisualStudio.ProjectSystem
+ XSharp.ProjectSystem2022
+ v4.8
+ win
+ {AB494DCE-A11B-4583-851E-D4A86775B3F0}
+ AnyCPU
+ true
+
+ XSharp\Project
+ v3
+ false
+ False
+ true
+
+
+
+ true
+ full
+ false
+ TRACE;DEBUG;XSHARP;DEV17
+ false
+ false
+ 1762;VSSDK006;NU1903
+
+
+
+ pdbonly
+ true
+ XSHARP;TRACE;DEV17
+ false
+ VSTHRD010;VSSDK006;1762;NU1903
+
+
+
+
+
+ CommonAssemblyInfo.cs
+
+
+ Constants.cs
+
+
+ XSharpConstants.cs
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Designer
+ MSBuild:GenerateRuleSourceFromXaml
+ MSBuild
+
+
+ Designer
+ MSBuild:GenerateRuleSourceFromXaml
+ MSBuild
+
+
+
+
+
+
+ false
+ PreserveNewest
+
+
+
+
+
+
+
+ 17.10.60
+
+
+ 17.10.60
+
+
+
+
+ 17.10.40171
+
+
+ 17.10.40171
+
+
+
+
+ 17.0.551
+
+
+
+
+ 17.10.48
+
+
+ 17.10.48
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+ 17.10.46
+
+
+
+
+ 10.0.5
+
+
+
+
+
+
+ {eed6843b-b727-4f08-aa32-ca0f48fdde3f}
+ CodeModel
+
+
+
+ {5891B814-A2E0-4e64-9A2F-2C2ECAB940FE}
+ ProjectPackage2022
+
+
+
+
+
diff --git a/src/VisualStudio/ProjectSystem/XSharpCodeDomIntegration.cs b/src/VisualStudio/ProjectSystem/XSharpCodeDomIntegration.cs
new file mode 100644
index 0000000000..8519f4d4ad
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/XSharpCodeDomIntegration.cs
@@ -0,0 +1,87 @@
+//
+// Copyright (c) XSharp B.V. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0.
+// See License.txt in the project root for license information.
+//
+
+using System.CodeDom.Compiler;
+using System.ComponentModel.Composition;
+using System.Runtime.InteropServices;
+
+using Microsoft.VisualStudio.ProjectSystem;
+using Microsoft.VisualStudio.Shell;
+
+// The XSharp CodeDom provider assembly is produced by ProjectPackage.
+// We use a type-forward so this assembly does not need a hard dependency on the
+// internal VSXsharpCodeDomProvider type; instead we resolve it via the VS service
+// provider at runtime.
+
+namespace XSharp.VisualStudio.ProjectSystem
+{
+ ///
+ /// Bridges the XSharp CodeDom provider into the CPS project system so that the
+ /// Windows Forms designer can generate XSharp source code.
+ ///
+ /// In the MPF path the CodeDom provider is registered via
+ /// [ProvideCodeDomProvider] attributes on the package. The CPS path
+ /// additionally needs to export the provider through MEF so that CPS design-time
+ /// build services can locate it.
+ ///
+ /// The actual implementation lives in
+ /// XSharp.Project.VSXsharpCodeDomProvider (ProjectPackage assembly).
+ /// This class locates and delegates to it at run-time, keeping the two assemblies
+ /// loosely coupled.
+ ///
+ [Export(typeof(ICodeDomProviderIntegration))]
+ [AppliesTo(XSharpProjectCapabilities.XSharp)]
+ internal sealed class XSharpCodeDomIntegration : ICodeDomProviderIntegration
+ {
+ private readonly UnconfiguredProject _project;
+
+ [ImportingConstructor]
+ public XSharpCodeDomIntegration(UnconfiguredProject project)
+ {
+ _project = project;
+ }
+
+ ///
+ /// Returns an instance of the XSharp for use
+ /// by the Windows Forms code generator.
+ ///
+ /// The provider is obtained from the VS service provider so that it uses the
+ /// same registration path as the MPF-based implementation.
+ ///
+ public CodeDomProvider GetCodeDomProvider()
+ {
+ // Resolve the provider type by name to avoid a hard assembly reference.
+ // The XSharp.Project.VSXsharpCodeDomProvider class is registered in the
+ // global VS MEF container by the ProjectPackage assembly.
+ const string providerTypeName =
+ "XSharp.Project.VSXsharpCodeDomProvider, XSharp.ProjectPackage2022";
+
+ System.Type providerType = System.Type.GetType(providerTypeName);
+
+ if (providerType != null &&
+ System.Activator.CreateInstance(providerType) is CodeDomProvider provider)
+ {
+ return provider;
+ }
+
+ // Fallback: return a C# provider to avoid hard failures.
+ // In practice this branch should never be reached when the ProjectPackage
+ // assembly is loaded.
+ return CodeDomProvider.CreateProvider("CSharp");
+ }
+ }
+
+ ///
+ /// Marker interface used by to allow
+ /// MEF discovery without pulling in Windows Forms or CodeDom namespaces at
+ /// the interface declaration site.
+ ///
+ public interface ICodeDomProviderIntegration
+ {
+ /// Gets the for XSharp source generation.
+ CodeDomProvider GetCodeDomProvider();
+ }
+}
diff --git a/src/VisualStudio/ProjectSystem/XSharpCpsPackage.cs b/src/VisualStudio/ProjectSystem/XSharpCpsPackage.cs
new file mode 100644
index 0000000000..8cf82d8aa4
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/XSharpCpsPackage.cs
@@ -0,0 +1,166 @@
+//
+// Copyright (c) XSharp B.V. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0.
+// See License.txt in the project root for license information.
+//
+
+using System;
+using System.ComponentModel;
+using System.Runtime.InteropServices;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Community.VisualStudio.Toolkit;
+
+using Microsoft.VisualStudio;
+using Microsoft.VisualStudio.Shell;
+using Microsoft.VisualStudio.Shell.Interop;
+
+using Task = System.Threading.Tasks.Task;
+
+namespace XSharp.VisualStudio.ProjectSystem
+{
+ ///
+ /// VS Package that registers the CPS-based XSharp project system.
+ ///
+ /// This package is loaded only in VS2022 and later (enforced by the
+ /// MinimumVisualStudioVersion entry in the VSIX manifest and by the
+ /// [$RootKey$\Projects\...] registry entry in
+ /// .pkgdef, which is only deployed for VS2022+).
+ ///
+ /// For VS2019 the existing MPF-based XSharpProjectPackage continues to
+ /// handle .xsproj files unchanged.
+ ///
+ /// Responsibilities
+ /// ────────────────
+ /// • Ensures the CPS MEF composition is bootstrapped before any project is loaded.
+ /// • Registers the VO binary editor factories so they work in the CPS context.
+ /// (The factories themselves live in the ProjectPackage assembly; we just make
+ /// sure they are registered when the CPS package loads.)
+ /// • Exposes a VS version check so other components can query whether CPS is
+ /// the active project system.
+ ///
+ [Description(XSharpConstants.ProjectSystemName + " (CPS)")]
+ [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)]
+ [ProvideAutoLoad(
+ VSConstants.UICONTEXT.SolutionExists_string,
+ PackageAutoLoadFlags.BackgroundLoad)]
+ [Guid(XSharpConstants.guidCpsProjectTypeString)]
+ // ── Project type registration ──────────────────────────────────────────────
+ // The .xsproj extension is bound to the CPS project type via the .pkgdef file.
+ // We also keep the VO editor extensions registered here for VS2022+.
+ [ProvideEditorExtension(
+ editorType: typeof(XSharpCpsPackage), // placeholder — real factory is in ProjectPackage
+ extension: ".xsfrm",
+ priority: 0x42,
+ DefaultName = "XSharp VO Form Editor",
+ NameResourceID = 80110)]
+ [ProvideEditorExtension(
+ editorType: typeof(XSharpCpsPackage),
+ extension: ".xsmnu",
+ priority: 0x42,
+ DefaultName = "XSharp VO Menu Editor",
+ NameResourceID = 80111)]
+ [ProvideEditorExtension(
+ editorType: typeof(XSharpCpsPackage),
+ extension: ".xsdbs",
+ priority: 0x42,
+ DefaultName = "XSharp VO DbServer Editor",
+ NameResourceID = 80112)]
+ [ProvideEditorExtension(
+ editorType: typeof(XSharpCpsPackage),
+ extension: ".xsfs",
+ priority: 0x42,
+ DefaultName = "XSharp VO FieldSpec Editor",
+ NameResourceID = 80113)]
+ public sealed class XSharpCpsPackage : AsyncPackage
+ {
+ // ─── VS version threshold ────────────────────────────────────────────────────
+
+ ///
+ /// The minimum VS version that activates the CPS project system.
+ /// VS2022 = 17.x.
+ ///
+ private const int MinCpsMajorVersion = 17;
+
+ private static bool? s_isCpsEnabled;
+
+ ///
+ /// Returns true when the host VS instance is new enough to use the
+ /// CPS-based project system (VS2022, version 17.0 or later).
+ ///
+ public static bool IsCpsEnabled
+ {
+ get
+ {
+ if (s_isCpsEnabled.HasValue)
+ return s_isCpsEnabled.Value;
+
+ // Default to false until the version check succeeds.
+ s_isCpsEnabled = false;
+
+ try
+ {
+ // The reliable check is the VS shell release version string.
+ var shell = ServiceProvider.GlobalProvider.GetService(
+ typeof(SVsShell)) as IVsShell;
+ if (shell != null)
+ {
+ shell.GetProperty(
+ (int)__VSSPROPID5.VSSPROPID_ReleaseVersion,
+ out object versionObj);
+ if (versionObj is string versionStr &&
+ System.Version.TryParse(
+ versionStr.Split(' ')[0], out System.Version vsVersion))
+ {
+ s_isCpsEnabled = vsVersion.Major >= MinCpsMajorVersion;
+ }
+ }
+ }
+ catch
+ {
+ s_isCpsEnabled = false;
+ }
+
+ return s_isCpsEnabled.Value;
+ }
+ }
+
+ // ─── Package initialisation ──────────────────────────────────────────────────
+
+ ///
+ protected override async Task InitializeAsync(
+ CancellationToken cancellationToken,
+ IProgress progress)
+ {
+ await base.InitializeAsync(cancellationToken, progress);
+ await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken);
+
+ // Guard: only do CPS-specific work when running in VS2022+.
+ if (!IsCpsEnabled)
+ return;
+
+ // Log successful initialisation.
+ XSettings.Logger.Information(
+ $"[XSharpCpsPackage] CPS project system initialized " +
+ $"(VS {GetVsVersionString()}).");
+ }
+
+ private static string GetVsVersionString()
+ {
+ try
+ {
+ var shell = ServiceProvider.GlobalProvider.GetService(typeof(SVsShell)) as IVsShell;
+ if (shell != null)
+ {
+ shell.GetProperty(
+ (int)__VSSPROPID5.VSSPROPID_ReleaseVersion,
+ out object v);
+ return v?.ToString() ?? "unknown";
+ }
+ }
+ catch { }
+ return "unknown";
+ }
+ }
+}
diff --git a/src/VisualStudio/ProjectSystem/XSharpCpsPackage.pkgdef b/src/VisualStudio/ProjectSystem/XSharpCpsPackage.pkgdef
new file mode 100644
index 0000000000..666d5c51fb
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/XSharpCpsPackage.pkgdef
@@ -0,0 +1,87 @@
+// XSharpCpsPackage.pkgdef
+//
+// Copyright (c) XSharp B.V. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0.
+// See License.txt in the project root for license information.
+//
+// Registry entries required to activate the CPS-based XSharp project system
+// in Visual Studio 2022 and later.
+//
+// These entries are deployed as part of the XSharp VSIX and are installed under
+// the VS private registry hive. They are NOT written for VS2019, where the
+// MPF-based XSharpProjectPackage remains responsible for .xsproj files.
+//
+// Substitution variables used below:
+// $RootKey$ – VS private registry root (e.g. Software\Microsoft\VisualStudio\17.0_...)
+// $PackageFolder$ – folder where this .pkgdef lives (the VSIX install dir)
+//
+// ─── Project type → CPS package binding ─────────────────────────────────────
+//
+// Tell VS that the XSharp project type (GUID aa6c8d78-...) should be handled by
+// the CPS project system package (GUID AB494DCE-...) rather than the legacy MPF
+// package. This entry is the key that switches .xsproj loading from MPF to CPS.
+
+[$RootKey$\Projects\{aa6c8d78-22ff-423a-9c7c-5f2393824e04}]
+"Language"="XSharp"
+"DisplayName"="XSharp Project"
+"Package"="{AB494DCE-A11B-4583-851E-D4A86775B3F0}"
+"ProjectSystemPackage"="{AB494DCE-A11B-4583-851E-D4A86775B3F0}"
+"DefaultProjectExtension"="xsproj"
+
+// ─── Project file extension → project type ──────────────────────────────────
+// Map .xsproj to the XSharp project type GUID so VS opens the right project system.
+
+[$RootKey$\Projects\{aa6c8d78-22ff-423a-9c7c-5f2393824e04}\Extensions]
+"xsproj"="{aa6c8d78-22ff-423a-9c7c-5f2393824e04}"
+
+// ─── CPS project type capabilities ──────────────────────────────────────────
+// CPS reads project capabilities from the MSBuild Sdk; this entry provides a
+// fallback for cases where the project file has not yet been fully evaluated.
+
+[$RootKey$\Projects\{aa6c8d78-22ff-423a-9c7c-5f2393824e04}\Capabilities]
+@="XSharp Managed CSharp ProjectReferences AssemblyReferences PackageReferences"
+
+// ─── VO / XSharp binary editor registrations (VS2022+) ──────────────────────
+// The VO binary editors are registered in XSharpProjectPackage for MPF. Repeat
+// the registrations here so that they are also active when the CPS package is the
+// primary project system package.
+
+[$RootKey$\Editors\{fc9f8e69-f338-4fa6-aca3-aa41e445849a}]
+@="XSharp VO Form Editor"
+
+[$RootKey$\Editors\{fc9f8e69-f338-4fa6-aca3-aa41e445849a}\Extensions]
+"xsfrm"=dword:00000042
+"vnfrm"=dword:00000042
+
+[$RootKey$\Editors\{fc9f8e69-f338-4fa6-aca3-aa41e445849a}\LogicalViews]
+"{7651a703-06e5-11d1-8ebd-00a0c90f26ea}"=""
+
+[$RootKey$\Editors\{e4ae4582-98ae-40c8-9e48-6f3be61ebf79}]
+@="XSharp VO Menu Editor"
+
+[$RootKey$\Editors\{e4ae4582-98ae-40c8-9e48-6f3be61ebf79}\Extensions]
+"xsmnu"=dword:00000042
+"vnmnu"=dword:00000042
+
+[$RootKey$\Editors\{e4ae4582-98ae-40c8-9e48-6f3be61ebf79}\LogicalViews]
+"{7651a703-06e5-11d1-8ebd-00a0c90f26ea}"=""
+
+[$RootKey$\Editors\{c7e4c5f6-98b8-4826-9000-6b9b94dc2d97}]
+@="XSharp VO DbServer Editor"
+
+[$RootKey$\Editors\{c7e4c5f6-98b8-4826-9000-6b9b94dc2d97}\Extensions]
+"xsdbs"=dword:00000042
+"vndbs"=dword:00000042
+
+[$RootKey$\Editors\{c7e4c5f6-98b8-4826-9000-6b9b94dc2d97}\LogicalViews]
+"{7651a703-06e5-11d1-8ebd-00a0c90f26ea}"=""
+
+[$RootKey$\Editors\{8c5d0bae-7a69-437b-ad0e-1e1b89721ebd}]
+@="XSharp VO FieldSpec Editor"
+
+[$RootKey$\Editors\{8c5d0bae-7a69-437b-ad0e-1e1b89721ebd}\Extensions]
+"xsfs"=dword:00000042
+"vnfs"=dword:00000042
+
+[$RootKey$\Editors\{8c5d0bae-7a69-437b-ad0e-1e1b89721ebd}\LogicalViews]
+"{7651a703-06e5-11d1-8ebd-00a0c90f26ea}"=""
diff --git a/src/VisualStudio/ProjectSystem/XSharpProjectBuildProvider.cs b/src/VisualStudio/ProjectSystem/XSharpProjectBuildProvider.cs
new file mode 100644
index 0000000000..ac25808579
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/XSharpProjectBuildProvider.cs
@@ -0,0 +1,102 @@
+//
+// Copyright (c) XSharp B.V. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0.
+// See License.txt in the project root for license information.
+//
+
+using System.Collections.Immutable;
+using System.ComponentModel.Composition;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Microsoft.VisualStudio.ProjectSystem;
+using Microsoft.VisualStudio.ProjectSystem.Build;
+
+namespace XSharp.VisualStudio.ProjectSystem
+{
+ ///
+ /// Provides XSharp-specific MSBuild build properties to the CPS build pipeline.
+ ///
+ /// The properties exposed here augment what CPS reads automatically from the
+ /// project file, adding XSharp dialect and language options that CPS would
+ /// otherwise not know about.
+ ///
+ [Export(typeof(IBuildPropertiesProvider))]
+ [AppliesTo(XSharpProjectCapabilities.XSharp)]
+ internal sealed class XSharpProjectBuildProvider : IBuildPropertiesProvider
+ {
+ // Reserved for future use: inject or transform per-project build properties
+ // (e.g. dialect, include paths) at build time.
+ // private readonly ConfiguredProject _configuredProject;
+
+ [ImportingConstructor]
+ public XSharpProjectBuildProvider(ConfiguredProject configuredProject)
+ {
+ // configuredProject reserved for future build-property injection.
+ }
+
+ ///
+ public Task> GetBuildPropertiesAsync(
+ BuildPropertyContext context,
+ CancellationToken cancellationToken)
+ {
+ // Return an empty dictionary — the actual XSharp build properties
+ // (Dialect, VOCompatibility options, etc.) are already expressed in the
+ // .xsproj MSBuild file and are picked up by CPS automatically.
+ // Override this method to inject or transform properties at build time
+ // if needed in the future.
+ IImmutableDictionary empty =
+ ImmutableDictionary.Empty;
+
+ return Task.FromResult(empty);
+ }
+ }
+
+ ///
+ /// Implements a fast "up-to-date" check for XSharp projects, telling VS that the
+ /// project needs a full rebuild only when its source or project file has actually
+ /// changed.
+ ///
+ /// This is a lightweight placeholder implementation. It currently delegates to
+ /// CPS's default behaviour (always returns false so that MSBuild performs
+ /// the real incremental check). A full implementation would inspect timestamps of
+ /// .prg / .xs files and compare them against output assembly timestamps.
+ ///
+ [Export(typeof(IBuildUpToDateCheckProvider))]
+ [AppliesTo(XSharpProjectCapabilities.XSharp)]
+ [ExportMetadata("Name", "XSharpUpToDateCheck")]
+ internal sealed class XSharpUpToDateCheckProvider : IBuildUpToDateCheckProvider
+ {
+ // Reserved for future use: compare source timestamps against output assembly
+ // to implement a fast "up-to-date" check without invoking MSBuild.
+ // private readonly ConfiguredProject _configuredProject;
+
+ [ImportingConstructor]
+ public XSharpUpToDateCheckProvider(ConfiguredProject configuredProject)
+ {
+ // configuredProject reserved for future up-to-date timestamp checking.
+ }
+
+ ///
+ ///
+ /// Returns false to defer to MSBuild's own incremental build logic.
+ /// Set to an explanatory message when
+ /// returning false so that the Output window explains why a rebuild
+ /// was triggered.
+ ///
+ public Task IsUpToDateAsync(
+ BuildAction buildAction,
+ TextWriter logger,
+ CancellationToken cancellationToken = default)
+ {
+ // Defer to MSBuild for incremental build decisions.
+ return Task.FromResult(false);
+ }
+
+ ///
+ public Task IsUpToDateCheckEnabledAsync(CancellationToken cancellationToken = default)
+ {
+ return Task.FromResult(true);
+ }
+ }
+}
diff --git a/src/VisualStudio/ProjectSystem/XSharpProjectCapabilities.cs b/src/VisualStudio/ProjectSystem/XSharpProjectCapabilities.cs
new file mode 100644
index 0000000000..803fb2ab13
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/XSharpProjectCapabilities.cs
@@ -0,0 +1,64 @@
+//
+// Copyright (c) XSharp B.V. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0.
+// See License.txt in the project root for license information.
+//
+
+namespace XSharp.VisualStudio.ProjectSystem
+{
+ ///
+ /// Well-known CPS project capability strings advertised by the XSharp project system.
+ /// These are consumed by MEF components that use [AppliesTo] to scope themselves to
+ /// XSharp projects only.
+ ///
+ internal static class XSharpProjectCapabilities
+ {
+ // ─── XSharp-specific ────────────────────────────────────────────────────────
+
+ ///
+ /// The primary capability that identifies an XSharp project.
+ /// All XSharp-specific MEF exports should use [AppliesTo(XSharp)].
+ ///
+ public const string XSharp = nameof(XSharp);
+
+ // ─── Standard CPS capabilities ──────────────────────────────────────────────
+
+ /// Marks this as a managed (.NET) project.
+ public const string Managed = ProjectCapabilities.Managed;
+
+ ///
+ /// Reuses the C# capability string so the Roslyn language service (which
+ /// already supports XSharp via its own MEF exports) can be associated with
+ /// this project type without extra plumbing.
+ ///
+ public const string CSharp = ProjectCapabilities.CSharp;
+
+ /// The project supports project-to-project references.
+ public const string ProjectReferences = ProjectCapabilities.ProjectReferences;
+
+ /// The project supports raw assembly (DLL) references.
+ public const string AssemblyReferences = ProjectCapabilities.AssemblyReferences;
+
+ /// The project supports NuGet package references.
+ public const string PackageReferences = ProjectCapabilities.PackageReferences;
+
+ /// The project exposes output groups (for VSIX packaging, etc.).
+ public const string OutputGroups = ProjectCapabilities.OutputGroups;
+
+ /// All target output groups are available.
+ public const string AllTargetOutputGroups = ProjectCapabilities.AllTargetOutputGroups;
+
+ /// The project supports multi-targeting via <TargetFrameworks>.
+ ///
+ /// Multi-targeting is a VS2022+ / CPS-only feature.
+ /// The MPF-based path (VS2019) does NOT support this capability.
+ ///
+ public const string MultipleTargetFrameworks = ProjectCapabilities.MultipleTargetFrameworks;
+
+ /// The project supports pre-/post-build events.
+ public const string BuildEvents = nameof(BuildEvents);
+
+ /// The project supports source control operations.
+ public const string SourceControlled = ProjectCapabilities.SourceControlled;
+ }
+}
diff --git a/src/VisualStudio/ProjectSystem/XSharpProjectCapabilitiesProvider.cs b/src/VisualStudio/ProjectSystem/XSharpProjectCapabilitiesProvider.cs
new file mode 100644
index 0000000000..4b9a0a6399
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/XSharpProjectCapabilitiesProvider.cs
@@ -0,0 +1,40 @@
+//
+// Copyright (c) XSharp B.V. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0.
+// See License.txt in the project root for license information.
+//
+
+using System.Collections.Immutable;
+using System.ComponentModel.Composition;
+
+using Microsoft.VisualStudio.ProjectSystem;
+
+namespace XSharp.VisualStudio.ProjectSystem
+{
+ ///
+ /// Exports the set of CPS capabilities that every XSharp project advertises.
+ /// CPS uses capabilities to decide which MEF components apply to a given project.
+ ///
+ [Export(typeof(IProjectCapabilitiesProvider))]
+ [AppliesTo(XSharpProjectCapabilities.XSharp)]
+ internal sealed class XSharpProjectCapabilitiesProvider : IProjectCapabilitiesProvider
+ {
+ private static readonly ImmutableHashSet s_capabilities =
+ ImmutableHashSet.Create(
+ XSharpProjectCapabilities.XSharp,
+ XSharpProjectCapabilities.Managed,
+ XSharpProjectCapabilities.CSharp,
+ XSharpProjectCapabilities.ProjectReferences,
+ XSharpProjectCapabilities.AssemblyReferences,
+ XSharpProjectCapabilities.PackageReferences,
+ XSharpProjectCapabilities.OutputGroups,
+ XSharpProjectCapabilities.AllTargetOutputGroups,
+ XSharpProjectCapabilities.MultipleTargetFrameworks,
+ XSharpProjectCapabilities.BuildEvents,
+ XSharpProjectCapabilities.SourceControlled);
+
+ ///
+ public IImmutableSet GetProjectCapabilities(IProjectTree projectTree) =>
+ s_capabilities;
+ }
+}
diff --git a/src/VisualStudio/ProjectSystem/XSharpProjectProperties.cs b/src/VisualStudio/ProjectSystem/XSharpProjectProperties.cs
new file mode 100644
index 0000000000..518ff7fe50
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/XSharpProjectProperties.cs
@@ -0,0 +1,181 @@
+//
+// Copyright (c) XSharp B.V. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0.
+// See License.txt in the project root for license information.
+//
+
+using System.ComponentModel.Composition;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Microsoft.VisualStudio.ProjectSystem;
+using Microsoft.VisualStudio.ProjectSystem.Properties;
+
+namespace XSharp.VisualStudio.ProjectSystem
+{
+ ///
+ /// Provides strongly-typed access to the XSharp-specific MSBuild project
+ /// properties that are defined in .
+ ///
+ /// CPS exposes project properties through the
+ /// / abstraction. This class wraps that
+ /// abstraction and adds convenience methods for the XSharp build options.
+ ///
+ [Export]
+ [AppliesTo(XSharpProjectCapabilities.XSharp)]
+ internal sealed class XSharpProjectProperties
+ {
+ private readonly ConfiguredProject _project;
+
+ [ImportingConstructor]
+ public XSharpProjectProperties(ConfiguredProject project)
+ {
+ _project = project;
+ }
+
+ // ─── Helper ─────────────────────────────────────────────────────────────────
+
+ private IProjectProperties GetCommonProperties()
+ {
+ return _project.Services.ProjectPropertiesProvider
+ .GetCommonProperties();
+ }
+
+ private IProjectProperties GetConfigurationProperties()
+ {
+ // Retrieve properties with a per-configuration context so that
+ // configuration-specific values (e.g. OutputPath, DefineConstants) are
+ // read from the active configuration rather than the common project properties.
+ var context = ProjectPropertiesContext.GetContext(
+ _project,
+ propertyPageSchemaElement: null,
+ itemType: null,
+ itemName: null);
+ return _project.Services.ProjectPropertiesProvider
+ .GetProperties(context.File, context.ItemType, context.ItemName);
+ }
+
+ // ─── Application properties ──────────────────────────────────────────────────
+
+ public Task GetAssemblyNameAsync(CancellationToken ct = default) =>
+ GetCommonProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.AssemblyName);
+
+ public Task GetOutputTypeAsync(CancellationToken ct = default) =>
+ GetCommonProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.OutputType);
+
+ public Task GetRootNamespaceAsync(CancellationToken ct = default) =>
+ GetCommonProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.RootNamespace);
+
+ public Task GetStartupObjectAsync(CancellationToken ct = default) =>
+ GetCommonProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.StartupObject);
+
+ public Task GetApplicationIconAsync(CancellationToken ct = default) =>
+ GetCommonProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.ApplicationIcon);
+
+ // ─── Target framework ────────────────────────────────────────────────────────
+
+ /// Single-target framework (e.g. net48).
+ public Task GetTargetFrameworkAsync(CancellationToken ct = default) =>
+ GetCommonProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.TargetFramework);
+
+ ///
+ /// Semi-colon-separated list of target frameworks for multi-targeting
+ /// (e.g. net48;net6.0-windows). VS2022+ / CPS only.
+ ///
+ public Task GetTargetFrameworksAsync(CancellationToken ct = default) =>
+ GetCommonProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.TargetFrameworks);
+
+ // ─── XSharp dialect / language options ──────────────────────────────────────
+
+ public Task GetDialectAsync(CancellationToken ct = default) =>
+ GetCommonProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.Dialect);
+
+ public Task GetDefineConstantsAsync(CancellationToken ct = default) =>
+ GetConfigurationProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.DefineConstants);
+
+ public Task GetIncludePathsAsync(CancellationToken ct = default) =>
+ GetCommonProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.IncludePaths);
+
+ public Task GetWarningLevelAsync(CancellationToken ct = default) =>
+ GetConfigurationProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.WarningLevel);
+
+ public Task GetNoWarnAsync(CancellationToken ct = default) =>
+ GetConfigurationProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.NoWarn);
+
+ public Task GetTreatWarningsAsErrorsAsync(CancellationToken ct = default) =>
+ GetConfigurationProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.TreatWarningsAsErrors);
+
+ // ─── Build / output ──────────────────────────────────────────────────────────
+
+ public Task GetOutputPathAsync(CancellationToken ct = default) =>
+ GetConfigurationProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.OutputPath);
+
+ public Task GetPlatformTargetAsync(CancellationToken ct = default) =>
+ GetConfigurationProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.PlatformTarget);
+
+ public Task GetOptimizeAsync(CancellationToken ct = default) =>
+ GetConfigurationProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.Optimize);
+
+ public Task GetDocumentationFileAsync(CancellationToken ct = default) =>
+ GetConfigurationProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.DocumentationFile);
+
+ // ─── Debug ───────────────────────────────────────────────────────────────────
+
+ public Task GetDebugTypeAsync(CancellationToken ct = default) =>
+ GetConfigurationProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.DebugType);
+
+ public Task GetDebuggerCommandAsync(CancellationToken ct = default) =>
+ GetConfigurationProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.DebuggerCommand);
+
+ public Task GetDebuggerCommandArgumentsAsync(CancellationToken ct = default) =>
+ GetConfigurationProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.DebuggerCommandArguments);
+
+ public Task GetDebuggerWorkingDirectoryAsync(CancellationToken ct = default) =>
+ GetConfigurationProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.DebuggerWorkingDirectory);
+
+ // ─── Signing ─────────────────────────────────────────────────────────────────
+
+ public Task GetSignAssemblyAsync(CancellationToken ct = default) =>
+ GetCommonProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.SignAssembly);
+
+ public Task GetAssemblyOriginatorKeyFileAsync(CancellationToken ct = default) =>
+ GetCommonProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.AssemblyOriginatorKeyFile);
+
+ // ─── Build events ────────────────────────────────────────────────────────────
+
+ public Task GetPreBuildEventAsync(CancellationToken ct = default) =>
+ GetConfigurationProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.PreBuildEvent);
+
+ public Task GetPostBuildEventAsync(CancellationToken ct = default) =>
+ GetConfigurationProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.PostBuildEvent);
+
+ public Task GetRunPostBuildEventAsync(CancellationToken ct = default) =>
+ GetConfigurationProperties().GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.RunPostBuildEvent);
+ }
+}
diff --git a/src/VisualStudio/ProjectSystem/XSharpProjectTreePropertiesProvider.cs b/src/VisualStudio/ProjectSystem/XSharpProjectTreePropertiesProvider.cs
new file mode 100644
index 0000000000..836165db5b
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/XSharpProjectTreePropertiesProvider.cs
@@ -0,0 +1,97 @@
+//
+// Copyright (c) XSharp B.V. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0.
+// See License.txt in the project root for license information.
+//
+
+using System.Collections.Immutable;
+using System.ComponentModel.Composition;
+
+using Microsoft.VisualStudio.Imaging;
+using Microsoft.VisualStudio.Imaging.Interop;
+using Microsoft.VisualStudio.ProjectSystem;
+using Microsoft.VisualStudio.ProjectSystem.Tree;
+
+namespace XSharp.VisualStudio.ProjectSystem
+{
+ ///
+ /// Provides custom icons and tree-node flags for files inside an XSharp project.
+ ///
+ /// This mirrors the icon mapping that the MPF XSharpFileNode performs via
+ /// MapExtensionsToMoniker, ensuring a consistent look across both project
+ /// system implementations.
+ ///
+ [Export(typeof(IProjectTreePropertiesProvider))]
+ [AppliesTo(XSharpProjectCapabilities.XSharp)]
+ [Order(100)]
+ internal sealed class XSharpProjectTreePropertiesProvider : IProjectTreePropertiesProvider
+ {
+ // Extension → moniker map built once and reused for every tree change.
+ private static readonly ImmutableDictionary s_extensionMonikers =
+ new Dictionary(System.StringComparer.OrdinalIgnoreCase)
+ {
+ // XSharp source files
+ { ".prg", KnownMonikers.Script },
+ { ".xs", KnownMonikers.Script },
+ { ".ppo", KnownMonikers.Script },
+ { ".vh", KnownMonikers.Script },
+ { ".xh", KnownMonikers.Script },
+ { ".ch", KnownMonikers.Script },
+
+ // VO / XSharp binary editors
+ { ".xsfrm", KnownMonikers.FormInstance },
+ { ".vnfrm", KnownMonikers.FormInstance },
+ { ".xsmnu", KnownMonikers.MainMenuControl },
+ { ".vnmnu", KnownMonikers.MainMenuControl },
+ { ".xsdbs", KnownMonikers.Database },
+ { ".vndbs", KnownMonikers.Database },
+ { ".xsfs", KnownMonikers.ValidationRule },
+ { ".vnfs", KnownMonikers.ValidationRule },
+ { ".xsrep", KnownMonikers.Report },
+ { ".vnrep", KnownMonikers.Report },
+ { ".xssql", KnownMonikers.DatabaseScript },
+ { ".vnsqs", KnownMonikers.DatabaseScript },
+
+ // WPF
+ { ".xaml", KnownMonikers.WPFFile },
+ }
+ .ToImmutableDictionary(System.StringComparer.OrdinalIgnoreCase);
+
+ ///
+ public void CalculatePropertyValues(
+ IProjectTreeCustomizationContext context,
+ IProjectTreePropertiesUpdate update)
+ {
+ // Only process file nodes that have a known extension.
+ if (!context.Node.Flags.Contains(ProjectTreeFlags.FileOnDisk))
+ return;
+
+ string extension = System.IO.Path.GetExtension(context.Node.FilePath);
+ if (string.IsNullOrEmpty(extension))
+ return;
+
+ if (s_extensionMonikers.TryGetValue(extension, out ImageMoniker moniker))
+ {
+ update.Icon = moniker.ToProjectSystemType();
+ update.ExpandedIcon = moniker.ToProjectSystemType();
+ }
+
+ // Mark XSharp source files as proper source files so features like
+ // "Go to definition" and code-model scanning are activated.
+ if (IsXSharpSource(extension))
+ {
+ update.Flags = update.Flags
+ .Add(ProjectTreeFlags.SourceFile)
+ .Add(ProjectTreeFlags.FileSystemEntity);
+ }
+ }
+
+ private static bool IsXSharpSource(string extension) =>
+ extension.Equals(".prg", System.StringComparison.OrdinalIgnoreCase) ||
+ extension.Equals(".xs", System.StringComparison.OrdinalIgnoreCase) ||
+ extension.Equals(".ppo", System.StringComparison.OrdinalIgnoreCase) ||
+ extension.Equals(".vh", System.StringComparison.OrdinalIgnoreCase) ||
+ extension.Equals(".xh", System.StringComparison.OrdinalIgnoreCase) ||
+ extension.Equals(".ch", System.StringComparison.OrdinalIgnoreCase);
+ }
+}
diff --git a/src/VisualStudio/ProjectSystem/XSharpProjectType.cs b/src/VisualStudio/ProjectSystem/XSharpProjectType.cs
new file mode 100644
index 0000000000..38e4478331
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/XSharpProjectType.cs
@@ -0,0 +1,32 @@
+//
+// Copyright (c) XSharp B.V. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0.
+// See License.txt in the project root for license information.
+//
+
+using System.ComponentModel.Composition;
+
+using Microsoft.VisualStudio.ProjectSystem;
+
+namespace XSharp.VisualStudio.ProjectSystem
+{
+ ///
+ /// Binds the XSharp project type (GUID aa6c8d78-22ff-423a-9c7c-5f2393824e04)
+ /// and the .xsproj file extension to this CPS-based project system.
+ ///
+ /// The causes CPS to hand any
+ /// .xsproj file that declares the XSharp project-type GUID to this
+ /// project system rather than the legacy MPF factory. For VS2019 support,
+ /// the registry key ProjectSystemPackage under the project-type root
+ /// is only written for VS2022+ installations (see XSharpCpsPackage.pkgdef).
+ ///
+ [Export]
+ [ExportProjectType(
+ projectTypeGuid: XSharpConstants.guidXSharpProjectFactoryString,
+ displayName: XSharpConstants.LanguageName,
+ projectFileExtension: XSharpConstants.ProjectExtension,
+ defaultProjectCapabilities: XSharpProjectCapabilities.XSharp)]
+ internal sealed class XSharpProjectType
+ {
+ }
+}
diff --git a/src/VisualStudio/ProjectSystem/XSharpTargetFrameworkProvider.cs b/src/VisualStudio/ProjectSystem/XSharpTargetFrameworkProvider.cs
new file mode 100644
index 0000000000..1ce204d43e
--- /dev/null
+++ b/src/VisualStudio/ProjectSystem/XSharpTargetFrameworkProvider.cs
@@ -0,0 +1,98 @@
+//
+// Copyright (c) XSharp B.V. All Rights Reserved.
+// Licensed under the Apache License, Version 2.0.
+// See License.txt in the project root for license information.
+//
+
+using System.Collections.Immutable;
+using System.ComponentModel.Composition;
+using System.Threading;
+using System.Threading.Tasks;
+
+using Microsoft.VisualStudio.ProjectSystem;
+using Microsoft.VisualStudio.ProjectSystem.Build;
+
+namespace XSharp.VisualStudio.ProjectSystem
+{
+ ///
+ /// Hooks into CPS multi-targeting support for XSharp SDK-style projects.
+ ///
+ /// When a project file contains <TargetFrameworks>net48;net6.0-windows</TargetFrameworks>
+ /// CPS automatically creates one per framework.
+ /// This provider participates in that mechanism by advertising the list of
+ /// active target frameworks so that VS can show the framework picker and build
+ /// all targets on Rebuild.
+ ///
+ /// This capability is VS2022+ / CPS only.
+ /// The MPF-based VS2019 path does not support multi-targeting.
+ ///
+ [Export(typeof(IActiveConfiguredProjectsDimensionProvider))]
+ [AppliesTo(XSharpProjectCapabilities.XSharp)]
+ [ExportMetadata("DimensionName", ConfigurationGeneral.TargetFrameworkProperty)]
+ internal sealed class XSharpTargetFrameworkProvider
+ : IActiveConfiguredProjectsDimensionProvider
+ {
+ private readonly UnconfiguredProject _unconfiguredProject;
+ private readonly IProjectPropertiesProvider _propertiesProvider;
+
+ [ImportingConstructor]
+ public XSharpTargetFrameworkProvider(
+ UnconfiguredProject unconfiguredProject,
+ [Import(ExportContractNames.ProjectPropertyProviders.ProjectFile)]
+ IProjectPropertiesProvider propertiesProvider)
+ {
+ _unconfiguredProject = unconfiguredProject;
+ _propertiesProvider = propertiesProvider;
+ }
+
+ ///
+ ///
+ /// Returns the set of target frameworks declared in the project.
+ /// When only <TargetFramework> (singular) is set, a single entry is
+ /// returned. When <TargetFrameworks> (plural) is set, all entries
+ /// from the semicolon-separated list are returned.
+ ///
+ /// CPS uses this list to create one slice per
+ /// framework, enabling simultaneous multi-targeted builds.
+ ///
+ public async Task> GetDefaultValuesForDimensionsAsync(
+ CancellationToken cancellationToken)
+ {
+ IProjectProperties props = _propertiesProvider.GetCommonProperties();
+
+ // Prefer the plural TargetFrameworks property first.
+ string multiTarget = await props.GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.TargetFrameworks).ConfigureAwait(false);
+
+ if (!string.IsNullOrWhiteSpace(multiTarget))
+ {
+ var frameworks = multiTarget
+ .Split(';')
+ .Select(f => f.Trim())
+ .Where(f => !string.IsNullOrEmpty(f))
+ .ToImmutableHashSet(System.StringComparer.OrdinalIgnoreCase);
+
+ if (frameworks.Count > 0)
+ return frameworks;
+ }
+
+ // Fall back to the singular TargetFramework.
+ string singleTarget = await props.GetEvaluatedPropertyValueAsync(
+ XSharpProjectFileConstants.TargetFramework).ConfigureAwait(false);
+
+ if (!string.IsNullOrWhiteSpace(singleTarget))
+ return ImmutableHashSet.Create(
+ System.StringComparer.OrdinalIgnoreCase, singleTarget.Trim());
+
+ // No framework declared — return the default.
+ return ImmutableHashSet.Create(
+ System.StringComparer.OrdinalIgnoreCase, "net48");
+ }
+
+ ///
+ public string DimensionName => ConfigurationGeneral.TargetFrameworkProperty;
+
+ ///
+ public bool ShouldBeVisibleInConfigurationUI => true;
+ }
+}