在 VSIX 中,读取 CSPROJ 的配置属性会引发 COMException(方法或操作未实现)

问题描述 投票:0回答:1

在我的 VSIX(VS 扩展)中,我正在读取当前加载的解决方案中每个项目的配置属性'.Value。对于 C# (csproj) 项目:

var configurationManager = project.ConfigurationManager;
if (configurationManager == null) return null;
var configuration = configurationManager.ActiveConfiguration;
if (configuration == null) return null;
var properties = configuration.Properties;
if (properties == null) return null;

foreach (var property in properties.Cast<Property>())
{
    // reading property.Name is OK but reading property.Value gives the COMException 'not implemented'
    if (property == null) continue;
    if (property.Name == "StartWorkingDirectory") workingDir = (string)property.Value;
    if (property.Name == "StartAction") startProject = 0 == (int)property.Value;
    if (property.Name == "StartProgram") startExtProg = (string)property.Value;
    if (property.Name == "StartURL") startBrowser = (string)property.Value;
    // etc...
}

对于每个属性,当我尝试读取 .Value 属性时,我都会收到 COMException: '方法或操作未实现',尽管 .Name 没问题。所有搜索到的属性似乎都存在,只是无法读取它们的 .Value。

这是一个使用 VS2022 模板创建的全新 VSIX 项目,其中包含 Microsoft.VisualStudio.SDK 和参考文献中的 Microsoft.VisualStudio.BuildTools。

这似乎包括每个 DTE* 引用、stdole 和其他 VS COM dll,如 VSLangProj* (如果我尝试将这些添加为项目引用,它表示它们已经包含在构建过程中)。因此,我尝试通过 nuget 包添加所有这些引用,但我看不到任何效果,并且错误仍然存在。

所以我找不到任何关于如何在我的 C# 项目 (csproj) 上读取这些属性的信息。它们是简单的 C# 8 控制台应用程序,我刚刚创建的空的。

以下是我的 VSIX 项目的所有参考资料,包括由 nuget 拉入但可能不需要的参考资料:

envdte
envdte80
envdte90
Microsoft.CSharp
Microsoft.VisualStudio.CommandBars
Microsoft.VisualStudio.SDK
Microsoft.VSSDK.BuildTools
NJsonSchema
PresentationCore
PresentationFramework
stdole
System
VSLangProj
VSLangProj100
VSLangProj110
VSLangProj140
VSLangProj150
VSLangProj80
VSLangProj90
WindowsBase
WindowsFormsIntegration

我使用的是 VS2022,截至 2024 年 3 月已完全更新。

有什么想法吗?

编辑:来源:https://github.com/cbordeman/switchstartupproject/pull/4

c# visual-studio visual-studio-2022 visual-studio-extensions vsix
1个回答
0
投票

您提供的错误日志表明问题集中在使用 Visual Studio 扩展的不同方面。

这与您在 VSIX 项目中尝试读取项目配置属性时遇到的

COMException
是不同的问题:它指出加载 Visual Studio 包(即
SwitchStartupProjectPackage
)时失败,因为缺少程序集引用 (
Microsoft.VisualStudio.Shell.15.0, Version=17.0.0.0
)适用于 VS2022。
考虑到 SwitchStartupProject (2021) 的最后发布日期,这可能是有意义的。您分叉的存储库 (
kemmis/switchstartupproject
) 似乎更旧。

错误:

CreateInstance failed for package [SwitchStartupProjectPackage]... Could not load file or assembly 'Microsoft.VisualStudio.Shell.15.0, Version=17.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.

它表明您的 VSIX 扩展正在尝试加载未找到的

Microsoft.VisualStudio.Shell.15.0
程序集的版本。

您的 VSIX 项目可能面向的 Visual Studio 版本与您尝试加载的

Microsoft.VisualStudio.Shell.15.0
程序集不匹配。
指定版本的程序集 (
17.0.0.0
) 可能不会随 Visual Studio 安装一起安装。如果扩展适用于不同版本的 Visual Studio,或者扩展的配置中存在错误,指定了错误的版本号,则可能会发生这种情况。
对于 Visual Studio 2022,
Microsoft.VisualStudio.Shell.15.0
应更新为与 Visual Studio 17.x.x.x(Visual Studio 2022 的内部版本号)兼容的版本。

您的分叉中的更改集中在两个主要领域:项目文件的更新(

SwitchStartupProject.csproj
)和
SwitchStartupProjectPackage.cs
中的小更新。他们确实使项目与与 Visual Studio 2022 兼容的库和 API 保持一致 (
VS 17.x
),这应该有助于解决任何兼容性问题。

但是:

  • 查看项目的
    .vsixmanifest
    文件,确保
    Prerequisites
    部分正确指定扩展所针对的 Visual Studio 版本。
  • 日志提到由于无法加载
    Microsoft.VisualStudio.Shell.15.0, Version=17.0.0.0
    而导致的特定故障。拉取请求似乎并没有直接解决此问题,除非对
    Microsoft.VisualStudio.SDK
    Microsoft.VSSDK.BuildTools
    的更新通过确保正确引用 Visual Studio 2022 的所有必要依赖项来间接解决此问题。
    检查所使用的
    Microsoft.VisualStudio.Shell
    版本是否与 Visual Studio 2022 兼容,并且对旧版本的任何特定引用是否已更新或删除。

从 VSIX 扩展访问 CSPROJ 的配置属性有时会导致您遇到的

COMException
。这通常是由于这些属性通过DTE(开发工具环境)和 COM 接口公开的方式造成的。

您可以使用 try-catch 块实现更安全的属性访问策略:

foreach (var property in properties.Cast<Property>())
{
    try
    {
        // Attempt to read property value
        if (property.Name == "StartWorkingDirectory") workingDir = (string)property.Value;
        if (property.Name == "StartAction") startProject = 0 == (int)property.Value;
        if (property.Name == "StartProgram") startExtProg = (string)property.Value;
        if (property.Name == "StartURL") startBrowser = (string)property.Value;
        // etc
    }
    catch (COMException ex)
    {
        // Handle or log the exception as needed
        Debug.WriteLine($"Failed to read {property.Name}: {ex.Message}");
    }
}

如果由于未实现的属性而抛出

COMException
,它会捕获异常并允许循环继续:它可以防止单个不可访问的属性停止整个进程。

但是,作为一种替代方法,您可以直接将 CSPROJ 文件作为 XML 文档读取,而不是使用 ConfigurationManager 和 COM 接口来访问项目属性。这将绕过通过 COM 与属性交互的需要,避免“未实现”异常。

using System.Xml.Linq;

// Assuming 'project' is a DTE Project object
string projectFilePath = project.FullName;

// Load the CSPROJ file as an XML document
XDocument projectFile = XDocument.Load(projectFilePath);

// Namespace for MSBuild project files
XNamespace msbuild = "http://schemas.microsoft.com/developer/msbuild/2003";

// Example of reading a property directly from the project file
var startAction = projectFile.Element(msbuild + "Project")
                             .Elements(msbuild + "PropertyGroup")
                             .Elements(msbuild + "StartAction")
                             .FirstOrDefault()?.Value;

// Repeat for other properties as needed
+-----------------+       +-----------------------+          +----------------------+
| VSIX Extension  | --FS->| Read CSPROJ as XML    | --Parse->| Extract Property Data|
+-----------------+       +-----------------------+          +----------------------+

您的情况:

using System.Xml.Linq;
using EnvDTE;
// Make sure to include these namespaces based on your project's context

public static class ProjectConfigurationReader
{
    public static void ReadProjectProperties(Project project)
    {
        if (project == null) return;

        string projectFilePath = project.FullName;
        XDocument projectFile = XDocument.Load(projectFilePath);
        XNamespace msbuild = "http://schemas.microsoft.com/developer/msbuild/2003";

        // Assuming you know the property names you are interested in
        var startWorkingDirectory = projectFile.Element(msbuild + "Project")
                                               .Elements(msbuild + "PropertyGroup")
                                               .Elements(msbuild + "StartWorkingDirectory")
                                               .FirstOrDefault()?.Value;

        var startAction = projectFile.Element(msbuild + "Project")
                                     .Elements(msbuild + "PropertyGroup")
                                     .Elements(msbuild + "StartAction")
                                     .FirstOrDefault()?.Value;

        var startProgram = projectFile.Element(msbuild + "Project")
                                      .Elements(msbuild + "PropertyGroup")
                                      .Elements(msbuild + "StartProgram")
                                      .FirstOrDefault()?.Value;

        var startURL = projectFile.Element(msbuild + "Project")
                                  .Elements(msbuild + "PropertyGroup")
                                  .Elements(msbuild + "StartURL")
                                  .FirstOrDefault()?.Value;

        // Process extracted properties as needed
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.