How to get Project Location in Visual Studio Project Template? - visual-studio-2010

I want to create a custom Visual Studio Project Template.
Can I read the location path, that is specified in the New-Project-Wizard in Visual Studio?
My goal is to import a file with custom build targets. This file located in a directory relative to my new project.
I tried it this way:
<Import Project="..\Deployment\custom.targets" />
But when creating a new project from my template it will point to a temporary users directory .
On MSDN I found this article that indicates there is no parameter that contains the location path. I can't believe that...
Can anyone help me out here?

Since I do not need my custom targets when instanciating a project I solved this with a condition:
<Import Project="..\Deployment\custom.targets" Condition="exists('..\Deployment\custom.targets')" />

You could get the solution path in the ProjectFinishedGenerationg event:
public void ProjectFinishedGenerating(Project project)
{
string solutionPath = System.IO.Path.GetDirectoryName(project.DTE.Solution.FullName);
}

Related

how to fully rename all folders and files of a visual studio project?

So I copied a project, and change the name at every place I could find, but at one place I cant change the name without destroying the project.
So in the folder "TestName" I got another folder "TestName" and the solution "TestName.sln"
When I apply the name changes, I get already so far that the solution is now "NewName.sln" and everything inside NewName\TestName\ is using the new name.
But I cant find the place where I can change the subfolder "TestName" into "NewName".
If I try it manually, the project will not load anymore, because it is still referencing the path NewName\TestName instead of NewName\NewName
So either I need to change the used path or I need an "official" way to change the subfolders name
After renaming the project directory, you need to change the path to the project file in the solution file.
For example:
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DE.Currency", "DE.Currency\DE.Currency.csproj", "{4DE44F2B-32BF-4CCA-A2CA-248828CA2BF3}"
EndProject
To rename the DE.Currency project to DE.NewProjectName you would rename the directory DE.Currency to DE.NewProjectName, and then within that directory, rename DE.Currency.csproj to DE.NewProjectName.csproj. Then you would edit the solution file to point to the path of the project file:
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DE.NewProjectName", "DE.NewProjectName\DE.NewProject.csproj", "{4DE44F2B-32BF-4CCA-A2CA-248828CA2BF3}"
EndProject
NOTE that there are 3 places in the solution file that refer to the old project name that must be changed, the project's "friendly" name, the directory that contains the project, and the actual name of the project file.
If you have other projects in the same solution that reference that renamed project, you will have to edit their csproj files also, to update the project references.
<ItemGroup>
<ProjectReference Include="..\DE.NewProjectName\DE.NewProjectName.csproj" />
</ItemGroup>
You should do all of this with the solution closed out of Visual Studio, or it will get VERY confused.
There are probably MANY other places in your code that you will want to update, including the assembly name, resources, and namespaces, if you want everything to be consistent.

How to add existing item to Visual Studio project, copy to output but change it's original name?

I added a chm help file as link to the application's project but it's file name is not good for releasing to the public ("Compiled help.chm"). Unfortunately it's maintained in a different git submodule by other people and it's name is an automated output from their help builder.
After adding file as link there is no option to change the file name. Is there a csproj xml feature allowing user to rename a file link, possibly without breaking WiX installers depending on it and other undesired consequences?
I was a bit curious about this one, so I gave it a try myself, and I think I was able to get something that will work for you:
If you have the file already in your project as a link, skip to 2; o/w, drag the file over your project in Visual Studio and - while holding down both Ctrl and Shift - drop the file on your project, creating a link.
Close the solution and project
Using notepad or some other text editor, edit the .csproj file, then locate your logical link by looking for the filename you just added.
Edit the link node as follows:
<ItemGroup>
<None Include="....\OtherTeamOutputFolder\Compiled help.chm">
<Link>Super Cool Production Product Name.chm</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
Build your project; witness the glory.
If you control WiX project, you can specify file name in the file element:
<Component…>
<File Id=”FILE_MyProgramDir_SomeAssemblyDLL”
Name=”SomeNewNameForAssembly.dll”
Source=”SomeAssembly.dll”
KeyPath=”yes” />
</Component>
However, you should consider the new name while accessing to renamed resources both from your executable/class libraries and from other stuff.

How can an Empty Visual C++ project be created programmatically?

I wanted to know how to use the template for an Empty Visual C++ project to programmatically create a new solution with this empty project. The code (C#) I have right now is:
string VSProgID = "VisualStudio.Solution.10.0";
Type solnObjType = System.Type.GetTypeFromProgID(VSProgID, true);
Solution4 soln = (Solution4) System.Activator.CreateInstance(solnObjType, true);
soln.Create(#"C:\NewSoln", "New");
soln.SaveAs(#"C:\NewSoln\New.sln");
string emptyVCProjPath = soln.GetProjectTemplate("General/Empty Project", "VC++"); // Here's where I need help
soln.AddFromTemplate(emptyVCProjPath, #"C:\NewSoln\NewProj", "New.vcxproj", false);
I wasn't able to find the VC++ Empty Project template in the location where all the other templates (C#/F#/VB) are located. The VC++ Empty Project Template does appear in the installed templates when I create a new project manually through the IDE. What should the parameters be for soln.GetProjectTemplate()? It doesn't seem to recognize "VC++" as a language. I know that "CSharp" and "VisualBasic" are valid options. But I wanted a VC++ Empty Project. Any help will be greatly appreciated.
I am posting this answer in the hope that it may help someone facing the same problem that I was facing.
Apparently the Visual Studio Extensibility framework API doesn't provide an option to create an Empty Visual C++ project programmatically. I had to generate the solution and project files to achieve this. Specifically, the [solution_name].sln, [project_name].vcxproj, and [project_name].vcxproj.filters files had to be generated. A GUID had to be generated for the new project and inserted at the appropriate places. If one needs to add files to this empty project, ClInclude tags need to be generated for header files, and ClCompile tags for source files. These tags are added to the [project_name].vcxproj, and [project_name].vcxproj.filters files. Refer to an existing Visual C++ project's files to help you figure out what goes where. It is pretty straightforward.
UPDATE
For some reason, the VC++ solution generated this way does not open directly (by double-clicking the solution file in Windows Explorer). I have to launch Visual Studio and open the solution from there.

Visual Studio 2010 DTE: How to make added DLL reference absolute and not copied

Summary:
We need to duplicate the behaviour of the Add Reference dialog, using DTE, when you add a specific DLL (it adds a Hint path entry to the reference in the CSProj file).
**Note: There is another related, but not duplicated, post from me here: https://stackoverflow.com/questions/6690655/visual-studio-2010-add-in-how-to-get-a-references-hint-path-property Please also read that one for more information about this issue. I have now added a decent bounty to get an answer to this and will happily spread up-votes over any decent answers :)*
The story so far:
I am converting a project reference to a direct DLL reference programmatically using DTE.
Assuming I have a simple solution with a Project2 (the parent project) which references a Project1 (the child project), I make the change like this:
project1Reference = FindProjectReference(project2.References, project1);
project1Reference.Remove();
Reference dllReference = project2.References.Add(project1DllPath);
where project1DllPath refers to the "c:\somewhere\Project1\Bin\Debug\Project1.dll" file.
The problem I cannot yet solve is that the new reference is not to
"c:\somewhere\Project1\Bin\Debug\Project1.dll" but instead points to
"c:\somewhere\Project2\Bin\Debug\Project1.dll" (and the file is copied there).
If I add the DLL directly/manually using the Add Reference menu, it does not do this copying.
How do I add a DLL reference to an existing project's DLL without it taking a copy and referencing that instead?
I have tried adding dllReference.CopyLocal = false; after the Add but aside from setting the flag it made no difference. There appear to be no options to modify the path after creation.
Update: I have also tried programmatically removing any Build dependency on Project1 from Project2, but that had no effect.
Below is the difference between the csproj files:
As a project:
<ItemGroup>
<ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj">
<Project>{86B3E118-2CD1-49E7-A180-C1346EC223B9}</Project>
<Name>ClassLibrary1</Name>
</ProjectReference>
</ItemGroup>
As a DLL reference (path was lost completely):
<ItemGroup>
<Reference Include="ClassLibrary1, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<Private>False</Private>
</Reference>
...
</ItemGroup>
As a manually referenced DLL:
<ItemGroup>
<Reference Include="ClassLibrary1, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\ClassLibrary1\bin\Debug\ClassLibrary1.dll</HintPath>
</Reference>
...
</ItemGroup>
It looks like being able to specify the hint path for the DLL reference is the key. How do you set a hint path on a DLL reference (assuming you only have a handle to the Reference property)?
More information (20 July 2011):
The suggestion from Muse VSExtensions below does not impact the DLLs in question, as a copy has already been made from the DLL's project BIN to the parent project's BIN folder. The parent project does not bother to make use of the reference path as it already has the child DLL in its output folder.
Also the Reference Paths of a project are saved to the project.csproj.user file and not to the project.csproj file.
I'm convinced that this is a new bug/feature in VS 2010 because I have an add-in that started showing similar behaviour a couple of days ago after I migrated it from VS 2008... Basically, if you add a reference to anything within VS's assembly search path, it will be added without the path hint.
I managed to find other VS add-ins that solved this problem (Power Tools, NuGet among others), and they all seem to use MsBuild. I don't know if MsBuild raises resource usage much - I myself haven't seen too big a slowdown, possibly because References.Add() is very slow to start with. But note that to get an instance of MsBuild project, a method called "GetLoadedProjects" is used, which may mean that it works on data already present in memory.
Below is the code I used to fix my add-in, it's a simplified version of what I found on the net... Essentially, the idea is to add the reference as usual, then use MsBuild to set the path hint. Setting the hint is a trivial operation, but finding the instance of the MsBuild project item to add the hint to is incredibly complicated. I tried to hack an alternative using MsBuild only, but ran into other problems... This one seems to work.
One other thing that may be of interest here: the code contains a sort of optimization - it doesn't add the hint to the new reference if the reference's path is equal to the path we wanted to add. This is good enough for the case in question, and detects correctly when VS decides to use the dll from the output folder instead of what we told it. But when I tried to add a reference to the dll already in the output folder (I use a single output folder for many related projects), the add-in didn't set the hint path and project seemed to switch to using some other dll in the path (in my case the one from its PublicAssemblies folder)... So it may be useful to remove that "if (!newRef.Path.Equals(..." line altogether and add the hint always. I'm still investigating this case, so any additional - uhm, hints or improvements of the code are welcome.
string newFileName = "the path to your.dll";
VSLangProj.VSProject containingProject = yourProject;
VSLangProj.Reference newRef;
newRef = containingProject.References.Add(newFileName);
if (!newRef.Path.Equals(newFileName, StringComparison.OrdinalIgnoreCase))
{
Microsoft.Build.Evaluation.Project msBuildProj = Microsoft.Build.Evaluation.ProjectCollection.GlobalProjectCollection.GetLoadedProjects(containingProject.Project.FullName).First();
Microsoft.Build.Evaluation.ProjectItem msBuildRef = null;
AssemblyName newFileAssemblyName = AssemblyName.GetAssemblyName(newFileName);
foreach(var item in msBuildProj.GetItems("Reference"))
{
AssemblyName refAssemblyName = null;
try
{
refAssemblyName = new AssemblyName(item.EvaluatedInclude);
}
catch {}
if (refAssemblyName != null)
{
var refToken = refAssemblyName.GetPublicKeyToken();
var newToken = newFileAssemblyName.GetPublicKeyToken();
if
(
refAssemblyName.Name.Equals(newFileAssemblyName.Name, StringComparison.OrdinalIgnoreCase)
&& ((refAssemblyName.Version != null && refAssemblyName.Version.Equals(newFileAssemblyName.Version))
|| (refAssemblyName.Version == null && newFileAssemblyName.Version == null))
&& (refAssemblyName.CultureInfo != null && (refAssemblyName.CultureInfo.Equals(newFileAssemblyName.CultureInfo))
|| (refAssemblyName.CultureInfo == null && newFileAssemblyName.CultureInfo == null))
&& ((refToken != null && newToken != null && Enumerable.SequenceEqual(refToken, newToken))
|| (refToken == null && newToken == null))
)
{
msBuildRef = item;
break;
}
}
}
if (msBuildRef != null)
{
Uri newFileUri = new Uri(newFileName);
Uri projectUri = new Uri(Path.GetDirectoryName(containingProject.Project.FullName).TrimEnd(Path.DirectorySeparatorChar) + Path.DirectorySeparatorChar);
Uri relativeUri = projectUri.MakeRelativeUri(newFileUri);
msBuildRef.SetMetadataValue("HintPath", relativeUri.ToString());
}
}
Does this have to be solved using only DTE? You could use MSBuild automation to do this... it has classes that can parse the contents of a csproj file.
Take a look at:
Microsoft.Build.Evaluation Namespace,
there is some useful information on how to load the csproj file, and then how to change it.
See also the Project Class.
To solve this problem you need to add a reference path to the Project properties from DTE
To set a reference path in Visual Studio:
In Solution Explorer, select the project.
On the Project menu, click Properties.
Click Reference Paths.
In the Folder text box, specify the path of the folder that contains assemblies. To browse to the folder, click the ellipsis (…).
Click Add Folder.
You have to do the same from the automation object
Let me know if it helps
Hard to reject a good challenge ...
I'm not there but I think I have some decent hints to move this forward.
First off I create a miniscule test addin to reproduce your problem and .. I failed!
foreach (Project project in (object[])_applicationObject.ActiveSolutionProjects)
{
((VSProject)project.Object).References.Add(#"c:\temp\test\FromFolder\bin\debug\KmlLib.dll");
}
This is a random dll from somewhere on my disk. The dll is unsigned and ended up in my csproj as (just what you want to accomplish):
<Reference Include="KmlLib">
<HintPath>..\..\FromFolder\bin\debug\KmlLib.dll</HintPath>
</Reference>
Then I noticed you dll was signed. That did not make any difference either. The next test I did was copy some standard MS dll. I picked VsWebSite.Interop.dll from \Program Files\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies\ and copied it to my FromFolder\bin\debug\ Adding that as a reference suddenly reproduced your scenario> I got the include but not the hintpath.
Then the final test: I renamed VsWebSite.Interop.dll to xxVsWebSite.Interop.dll and included that dll. Suddenly the hintpath was added again!
Combining all this and your description together my guess is that when adding a reference VS first looks if the referenced dll can be found in it's current search locations (GAC, Project folder, path(?), ..) If so then no hintpath is added. If it can't be found then a hintpath is required and will be added.
To see if this theory holds up you could do two tests:
Copy the referenced dll to a totally different folder and then reference from there --> Will still not included the hintpath because dll with same signature is still in the 'path'
Copy and Rename the referenced dll to a totally different folder and reference the new name --> Should add the hintpath
Curious about your results :)
I had an issue recently with adding a dll reference in Visual Studio 2010, I couldn't make it add the HintPath, which caused an issue with the TFS build. I noticed that the Productivity Power Tools add-on that I installed a few weeks ago, had changed the add reference dialog. I switched off the "Searchable Add Reference Dialog" of the add-on from the Tools menu and after restart of the Visual Studio 2010 I was happy again - I was able to add the reference and this time the HintPath was there too.

How to change WebProjectOutputDir default location?

Does anybody know, on a per project basis, whether you can set the WebProjectOutputDir in Visual Studio. I want to be able to have it so that when i hit Ctrl + Shift + B it automatically builds my web project to a specific directory, but I also need to be able to override this in my build script.
It is unnecessarily awkward to correctly do this based on the way that Microsoft.WebApplications.targets defines the _CopyWebApplication target and how Microsoft.Common.targets treats the OutDir and OutputPath properties.
If you want to change that in the project file itself then you should:
Declare the property WebProjectOutputDir after the import to Microsoft.WebApplications.targets
Declare the property OutDir before the import to Microsoft.WebApplications.targets
There are a few reasons why you have to do this.
Microsoft.WebApplications.targets will override any declaration of WebProjectOutputDir if it is declared before the import statement. Therefore it has to come after.
Also inside of Microsoft.WebApplications.targets the _CopyWebApplication is defined as follows:
<Target Name="_CopyWebApplication" Condition="'$(OutDir)' != '$(OutputPath)'" >
....
</Target>
Taking a look at the condition you will see that the target will not be executed if OutDir and OutputPath are equal to the same value. You cannot just change OutputPath because OutDir is based on OutputPath, so you have to change OutDir and make sure that it is before the import to that file because other properties are built based on that property.
Less than ideal, but hopefully that helps you.
I ran into this when using Visual Studio for Mac, compiling an Umbraco project. Building would issue the following error:
Error MSB4044: The "KillProcess" task was not given a value for the
required parameter "ImagePath"
It was returning an empty value for WebProjectOutputDir when compiling Rosyln, etc. Only the web project in the solution was compiling these items, so I edited the web project csproj file manually, and added the following to the global at the top of the file:
...
<WebProjectOutputDir>.\</WebProjectOutputDir>
</PropertyGroup>
Problem solved.

Resources