How to get IntermediateOutputPath from Visual Studio extension and new csproj format
Asked Answered
V

1

10

I have a new-style csproj project file that overrides IntermediateOutputPath. It looks like this:

<PropertyGroup>
    <TargetFramework>netstandard1.6</TargetFramework>
    <IntermediateOutputPath>new\path\to\obj</IntermediateOutputPath>
</PropertyGroup>

The problem is, my Visual Studio extension can't access IntermediateOutputPath property. Project.Properties seems to have much less stuff compared to old project format.

I've also tried project.ConfigurationManager.ActiveConfiguration.Properties with the similar success.

Is there any way to get this information from Visual Studio extension?

Velocity answered 3/10, 2017 at 9:5 Comment(3)
Can you share a sample project on github.com which can debugged?Cullie
There is not much code to share. Basically, you create a sample Visual Studio VSIX extension. In the extension get the DTE object like this var dte = (DTE)GetService(typeof(SDTE)); Then, when debugging your new extension, you can access a Solution and enumerate its' Projects. Previously I could read all MSBuild properties from there, but not anymore.Velocity
I have all the setup done but not able to set this option? How do I set it? Manually adding it creates error while loading projectCullie
C
4

So I created a simple extension to print all the properties

    private string GetPropertiesString(Properties properties)
    {
        StringBuilder test = new StringBuilder();
        foreach (Property property in properties)
        {
            try
            {
                test.AppendLine(property.Name + ":=" + property.Value.ToString());
                Console.WriteLine(property.Name + ":=" + property.Value.ToString());
            }
            catch (Exception ex)
            {
                var x = ex.Message;
            }
        }

        return test.ToString();
    }

    private void MenuItemCallback(object sender, EventArgs e)
    {
        DTE2 dte2 = Package.GetGlobalService(typeof(DTE)) as DTE2;
        var sol = dte2.Solution;
        var projs = sol.Projects;
        foreach (var proj in sol)
        {
            var project = proj as Project;
            var rows = project.ConfigurationManager.ConfigurationRowNames as IEnumerable<object>;
            foreach (var row in rows)
            {
                var config = project.ConfigurationManager.ConfigurationRow(row.ToString()).Item(1) as Configuration;
                string configs = GetPropertiesString(config.Properties);
            }
        }
    }

And this gave below output

LanguageVersion:=
RunCodeAnalysis:=False
NoStdLib:=False
ErrorReport:=prompt
CodeAnalysisUseTypeNameInSuppression:=True
CodeAnalysisInputAssembly:=bin\Debug\WindowsFormsApp1.exe
CodeAnalysisDictionaries:=
GenerateSerializationAssemblies:=2
CodeAnalysisModuleSuppressionsFile:=GlobalSuppressions.cs
StartWorkingDirectory:=
Optimize:=False
DocumentationFile:=
StartPage:=
OutputPath:=bin\Debug\
TreatWarningsAsErrors:=False
EnableASPDebugging:=False
IncrementalBuild:=True
CodeAnalysisFailOnMissingRules:=False
CodeAnalysisLogFile:=bin\Debug\WindowsFormsApp1.exe.CodeAnalysisLog.xml
DefineConstants:=DEBUG;TRACE
UseVSHostingProcess:=True
StartProgram:=
DefineDebug:=False
CodeAnalysisIgnoreBuiltInRules:=True
CodeAnalysisRuleSetDirectories:=;F:\VS2017\Team Tools\Static Analysis Tools\\Rule Sets
CodeAnalysisCulture:=
CodeAnalysisOverrideRuleVisibilities:=False
CodeAnalysisRuleAssemblies:=
DefineTrace:=False
DebugSymbols:=True
CodeAnalysisIgnoreBuiltInRuleSets:=True
CodeAnalysisRuleSet:=MinimumRecommendedRules.ruleset
NoWarn:=
CodeAnalysisIgnoreGeneratedCode:=True
EnableSQLServerDebugging:=False
BaseAddress:=4194304
RemoteDebugEnabled:=False
StartURL:=
AllowUnsafeBlocks:=False
TreatSpecificWarningsAsErrors:=
PlatformTarget:=AnyCPU
EnableUnmanagedDebugging:=False
StartWithIE:=False
StartArguments:=
IntermediatePath:=new\path\to\obj2\
CodeAnalysisRuleDirectories:=;F:\VS2017\Team Tools\Static Analysis Tools\FxCop\\Rules
DebugInfo:=full
CheckForOverflowUnderflow:=False
RemoteDebugMachine:=
Prefer32Bit:=True
CodeAnalysisSpellCheckLanguages:=
CodeAnalysisRules:=
RegisterForComInterop:=False
FileAlignment:=512
StartAction:=0
EnableASPXDebugging:=False
ConfigurationOverrideFile:=
WarningLevel:=4
RemoveIntegerChecks:=False

In the CS project I had added

<IntermediateOutputPath>new\path\to\obj2</IntermediateOutputPath>

As you can see IntermediateOutputPath is coming up as IntermediatePath. So you can use

var config = project.ConfigurationManager.ConfigurationRow("Debug").Item(1) as Configuration;
config.Properties.Item("IntermediatePath").Value

Edit-1 - .NET standard project

Output

Edit-2 - 12-Aug-2017

So after digging more into the issue I found out the property is a MSBuild property as such and not a CSProject related property. That is why you don't see it in the Properties attribute. This requires a bit different direction to get the value using IVsBuildPropertyStorage.GetPropertyValue

    private IVsBuildPropertyStorage GetBuildPropertyStorage(EnvDTE.Project project)
    {
        IVsSolution solution = (IVsSolution)ServiceProvider.GetService(typeof(SVsSolution));

        IVsHierarchy hierarchy;
        int hr = solution.GetProjectOfUniqueName(project.FullName, out hierarchy);
        System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(hr);

        return hierarchy as IVsBuildPropertyStorage;
    }

    private string GetBuildProperty(string key, IVsBuildPropertyStorage Storage)
    {
        string value;
        int hr = Storage.GetPropertyValue(key, null, (uint)_PersistStorageType.PST_USER_FILE, out value);
        int E_XML_ATTRIBUTE_NOT_FOUND = unchecked((int)0x8004C738);

        // ignore this HR, it means that there's no value for this key
        if (hr != E_XML_ATTRIBUTE_NOT_FOUND)
        {
            System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(hr);

        }

        return value;
    }

And then later use these methods to get the value

var project = proj as EnvDTE.Project;
IVsBuildPropertyStorage storage = GetBuildPropertyStorage(project);
string outputPath = GetBuildProperty("IntermediateOutputPath", storage);

And this gives me the correct value of the property

Cullie answered 10/10, 2017 at 6:14 Comment(11)
Are you sure you are loading new csproj format? By new I mean the one that has most of the properties and content defined implicitly.Velocity
I just created a new project in visual studio 2017. How do I create one with the most properties?Cullie
You can create a NetStandard project. It should definitely use the new format.Velocity
@Velocity In the first data dump here I see IntermediatePath not IntermediateOutputPath so maybe that's the difference?Condensate
Send me the sample project zipped some place and i will run my code on the project you have. Because I don't see those properties in my projectCullie
Here you go: mega.nz/#!AJYVjY4A!YhlBwLOZtx7frV679sNhk-hW9wKtK7LPB6tqgmRKZaQVelocity
It's applicable since if you build the project, intermediate files would be placed at the new location.Velocity
@ionoy, please check the latest answerCullie
Awesome, it worked! Thank you for your work, really helped me out here.Velocity
Hi! I also need to find project properties and this thread looked like a good candidate. I tried the first example with GetPropertiesString but unfortunately, I always get null config.Properties. I am using VS 2017 (15.8.2). My project file is a vcxproj instead of a vcproj but I don't see anything in the code example that would explain it works on vcproj and not vcxproj. Any idea why it wouldn't work on my environment?Locale
@Stephane, not sure, this was long back and now I don't even use a windows OS to check it outCullie

© 2022 - 2024 — McMap. All rights reserved.