My question is very similar to this one: How to cast ComObject to ENVDTE.Project?
I want to process the Project items selected in Visual Studio -> Solution Explorer. If project is loaded the code works fine but I have troubles for unloaded projects (they are called Unmodeled projects (http://msdn.microsoft.com/en-us/library/hw7ek4f4%28v=vs.80%29.aspx).
Casting selected item for loaded projects uiItem.Object is EnvDTE.Project is fine, but how to cast Unmodeled projects?
There is no 'UnmodeledProject' class and casting uiItem.Object is ProjectItem does not work.
This is my code:
Window solutionExplorer = mApplicationObject.Windows.Item(Constants.vsWindowKindSolutionExplorer);
if(solutionExplorer != null)
{
UIHierarchy uiHierarchy = (UIHierarchy)solutionExplorer.Object;
if (uiHierarchy != null)
{
object[] selectedItems = (object[])uiHierarchy.SelectedItems;
foreach (UIHierarchyItem uiItem in selectedItems)
{
// Valid project
if (uiItem.Object is EnvDTE.Project)
{
EnvDTE.Project project = uiItem.Object as EnvDTE.Project;
if (project.FullName.Contains(".vdproj") || project.Kind == "{54435603-DBB4-11D2-8724-00A0C9A8B90C}")
{
}
}
else if (uiItem.Object is ProjectItem)
{
// This is never jumped...
}
else
{ ...
As I did not find a solution for this situation I used this trick:
string pathToVdProject = null;
try
{
Window solutionExplorer = mApplicationObject.Windows.Item(Constants.vsWindowKindSolutionExplorer);
if (solutionExplorer != null)
{
UIHierarchy uiHierarchy = (UIHierarchy)solutionExplorer.Object;
if (uiHierarchy != null)
{
object[] selectedItems = (object[])uiHierarchy.SelectedItems;
foreach (UIHierarchyItem uiItem in selectedItems)
{
// Valid project
if (uiItem.Object is EnvDTE.Project)
{
EnvDTE.Project project = uiItem.Object as EnvDTE.Project;
if (project.FullName.Contains(".vdproj") || project.UniqueName.Contains(".vdproj")
|| (String.Compare(project.Kind, ProjectsGuids.guidVdSetupProject, true) == 0))
{
// Valid Project has property FullName which is full path to .vdproj file
pathToVdProject = project.FullName;
break;
}
}
else if (uiItem.Object is ProjectItem)
{
// This never happens...
}
else
{
// This is a little tricky: Unmodeled Projects cannot be casted to EnvDTE.Project http://msdn.microsoft.com/en-us/library/hw7ek4f4%28v=vs.80%29.aspx
Solution2 solution = (Solution2)mApplicationObject.Solution;
// So get all projects in solution (including unmodeled) and try to find a match by name
foreach (Project project in solution.Projects)
{
if (project.Kind == EnvDTE.Constants.vsProjectKindUnmodeled)
{
// Unmodeled project found (Normal projects are recognized in 'uiItem.Object is EnvDTE.Project'
if (project.Name.Contains(uiItem.Name))
{
// This is 'Project' for selected item
if (project.Name.Contains(".vdproj") || project.UniqueName.Contains(".vdproj"))
{
// Unmodeled projects does not offer property FullName and UniqueName does NOT contain full path to file!
FileInfo fileInfo = new FileInfo(solution.FullName);
// Create full path from solution (.sln) path and project relative path
pathToVdProject = fileInfo.DirectoryName + "\\" + project.UniqueName;
break;
}
}
}
}
}
}
}
}
List of all loaded/Unloaded projects inside the solution explorer will be available in your EnvDTE application object. Without using solution Explorer window and UIHierarchy i got the project details. Below code snippets working fine for me. Please check out weather it will fit for you..
For Each item As EnvDTE.Project In mApplicationObject.Solution.Projects
If item.Globals Is Nothing AndAlso item.Object Is Nothing Then
Console.WriteLine(item.Name + " is currently unloaded!")
End If
Next
Related
I would like to retrieve the full path of a right-clicked file (within the editor of Visual Studio 2017). I already implemented the following code to retrieve the path of a file when a project and/or solution is opened.
This implementation isn't working if a single file is opened.
Scenario:
Open VS (2017)
Navigate to File -> Open -> File.
Right click on a file and click on the desired context menu. (It calls the IsSingleProjectItemSelection method).
monitorSelection.GetCurrentSelection(out hierarchyPtr fails, because hierarchyPtr remains IntPtr.Zero.
Value cannot be null. Parameter name: pUnk
Perhaps you know a solution to retrieve the full path of a right-clicked file within the editor of Visual Studio (2017)?
Thank you in advance.
if (!IsSingleProjectItemSelection(out hierarchy, out itemid)) return;
// Get the file path
string itemFullPath = null;
((IVsProject) hierarchy).GetMkDocument(itemid, out itemFullPath);
var transformFileInfo = new FileInfo(itemFullPath);
string fullPath = itemFullPath.FullName;
public static bool IsSingleProjectItemSelection(out IVsHierarchy hierarchy, out uint itemid)
{
hierarchy = null;
itemid = VSConstants.VSITEMID_NIL;
int hr = VSConstants.S_OK;
var monitorSelection = Package.GetGlobalService(typeof(SVsShellMonitorSelection)) as IVsMonitorSelection;
var solution = Package.GetGlobalService(typeof(SVsSolution)) as IVsSolution;
if (monitorSelection == null || solution == null)
{
return false;
}
IVsMultiItemSelect multiItemSelect = null;
IntPtr hierarchyPtr = IntPtr.Zero;
IntPtr selectionContainerPtr = IntPtr.Zero;
try
{
hr = monitorSelection.GetCurrentSelection(out hierarchyPtr, out itemid, out multiItemSelect, out selectionContainerPtr);
if (ErrorHandler.Failed(hr) || hierarchyPtr == IntPtr.Zero || itemid == VSConstants.VSITEMID_NIL)
{
// there is no selection
return false;
}
// multiple items are selected
if (multiItemSelect != null) return false;
// there is a hierarchy root node selected, thus it is not a single item inside a project
if (itemid == VSConstants.VSITEMID_ROOT) return false;
hierarchy = Marshal.GetObjectForIUnknown(hierarchyPtr) as IVsHierarchy;
if (hierarchy == null) return false;
Guid guidProjectID = Guid.Empty;
if (ErrorHandler.Failed(solution.GetGuidOfProject(hierarchy, out guidProjectID)))
{
return false; // hierarchy is not a project inside the Solution if it does not have a ProjectID Guid
}
// if we got this far then there is a single project item selected
return true;
}
finally
{
if (selectionContainerPtr != IntPtr.Zero)
{
Marshal.Release(selectionContainerPtr);
}
if (hierarchyPtr != IntPtr.Zero)
{
Marshal.Release(hierarchyPtr);
}
}
}
DTE.ActiveDocument.FullName returns full path of the file you right clicked in.
I have an visual studio 2015 extension which reads files from a DB and creates a solution and project from a template. The files are then added as ProjectItems to the project (type VSProject2).
I would like to save the files in a folder, together with the csproj and sln file so that one can open it later without the extension.
However, the folder which contains the files in the solution window, is empty on disk.
So I'm confused. I've tried to save the projectItems, but it still doesn't end up in die folder on disk.
I add the folder with variable name "foldername" to the project. Let's make it TimeAndAttendance. So on disk a folder TempProject is created and in that, a folder TimeAndAttendance. I would like the csproj file and the source files (added as ProjectItems) to be saved in that folder.
public void CreateSolution()
{
// This function creates a solution and adds a Visual C# Class library project to it.
try
{
Solution2 soln = (Solution2)visualStudioInstance.Solution;
String csTemplatePath;
Project projCS = null;
parameterWindowEventsList = new Dictionary<EnvDTE.Window, WindowEvents>();
String csPrjPath = Path.Combine(Config.Cluster.WorkingPath, "TempProject");
if (Directory.Exists(csPrjPath))
{
DirectoryInfo di = new DirectoryInfo(csPrjPath);
foreach (var dir in di.GetDirectories())
{
dir.Attributes = FileAttributes.Normal;
FileInfo fi = new FileInfo(dir.FullName);
fi.IsReadOnly = false;
dir.Delete(true);
}
di.Attributes = FileAttributes.Normal;
di.Delete(true);
}
csTemplatePath = soln.GetProjectTemplate("TAEngineTemplate.zip", "CSharp");
// Create a new C# Class Library project using the template obtained above.
soln.AddFromTemplate(csTemplatePath, csPrjPath, "TAEngineProject", false);
foreach (Project p in soln.Projects)
{
if (String.Compare(p.Name, "TAEngineProject") == 0)
{
projCS = p;
break;
}
}
System.Windows.Forms.Application.DoEvents();
if (projCS != null)
{
taEngineProject = projCS.Object as VSProject2;
}
taEngineProject.References.Add(Path.Combine(Config.Engine.EngineBinPath, "Engines.TimeAndAttendance.dll"));
taEngineProject.References.Add("System.Data.DLL");
taEngineProject.References.Add("System.Xml.DLL");
taEngineProject.References.Add("System.Core.dll");
taEngineProject.Project.ProjectItems.AddFolder(folderName);
taEngineProject.Project.Save(null);
}
catch (SystemException ex)
{
log.Error("Problem with creating solution. " + ex);
}
}
The last line
taEngineProject.Project.Save(null);
doesn't seem to do anything.
The following is where I load a source file as a ProjectItem. Any improvement on how to do it without the try-catch functionality is also welcome. I inherited this code and just shook my head the first time I saw it, but I don't know how to improve it either. :-(
public ProjectItem LoadAsProjectItem(string sourcePath)
{
if (taEngineProject != null)
{
string filename = sourcePath.Substring(sourcePath.LastIndexOf("\\") + 1);
ProjectItem retItem;
ProjectItem projFolderItem = taEngineProject.Project.ProjectItems.Item(folderName);
try
{
retItem = projFolderItem.ProjectItems.Item(filename);
}
catch (Exception)
{
retItem = projFolderItem.ProjectItems.AddFromFile(sourcePath);
}
return retItem;
}
return null;
}
I'm trying to read (and change) the project properties "Start external program" and "Command line arguments" of a VisualStudio 2013 project within a VSPackage. The code I wrote so far looks like:
var dte = GetService(typeof(DTE)) as DTE2;
if (dte == null)
return;
var sb = (SolutionBuild2)dte.Solution.SolutionBuild;
var projectNames = sb.StartupProjects as Array;
if (projectNames == null || projectNames.Length == 0)
return;
var project = dte.Solution.Item(projectNames.GetValue(0));
var config = project.ConfigurationManager.ActiveConfiguration;
But I can't find the two spcific properties neither in the project nor in the config.
The EnvDTE.Configuration class has a Properties collection that has your desired values:
config.Properties.Item("StartProgram").Value
config.Properties.Item("StartArguments").Value
FWIW, the VSLangProj.dll assembly has a VSLangProj.ProjectConfigurationProperties class with the property names that you can expect in the config.Properties collection.
I have listed all the projects in my solution, using EnvDTE, but I found a bug in my code: I can't get the projects that are Unloaded.
I found a way to skip the Unloaded projects:
if (string.Compare(EnvDTE.Constants.vsProjectKindUnmodeled, project.Kind, System.StringComparison.OrdinalIgnoreCase) == 0)
continue;
This way, my code doesn't crash - but I am unable to load the missing projects through code, since they exist already.
How can I Load the Unloaded projects into the solution ?
I have tried:
project.DTE.ExecuteCommand("Project.ReloadProject");
And got error:
System.Runtime.InteropServices.COMException (...): Command "Project.ReloadProject" is not available.
So I tried to somehow get
application.DTE.ExecuteCommand("Project.ReloadProject");
But before that, from every place I searched on the NET, I must pre-select the project in the solution - and for that, I need project.Name (which I have), and the path, which I don't (every example I have found assumes that the solution path is the same as the project path, which is highly unlikely in a generic situation).
The Visual Studio SDK is apparently the way to do this.
var dte = (EnvDTE.DTE)System.Runtime.InteropServices.Marshal.GetActiveObject("VisualStudio.DTE");
Microsoft.VisualStudio.Shell.Interop.IVsUIHierarchyWindow hierarchy;
ServiceProvider sp = new ServiceProvider((Microsoft.VisualStudio.OLE.Interop.IServiceProvider)dte);
IVsSolution sol = (IVsSolution)sp.GetService(typeof(SVsSolution));
foreach (ProjInfo info in GetProjectInfo(sol))
{
info.Dump();
}
//from http://social.msdn.microsoft.com/Forums/en-US/vsx/thread/60fdd7b4-2247-4c18-b1da-301390edabf3/
static IEnumerable<ProjInfo> GetProjectInfo(IVsSolution sol)
{
Guid ignored = Guid.Empty;
IEnumHierarchies hierEnum;
if (ErrorHandler.Failed(sol.GetProjectEnum((int)__VSENUMPROJFLAGS.EPF_ALLPROJECTS, ref ignored, out hierEnum)))
{
yield break;
}
IVsHierarchy[] hier = new IVsHierarchy[1];
uint fetched;
while ((hierEnum.Next((uint)hier.Length, hier, out fetched) == VSConstants.S_OK) && (fetched == hier.Length))
{
int res = (int)VSConstants.S_OK;
Guid projGuid;
if (ErrorHandler.Failed(res = sol.GetGuidOfProject(hier[0], out projGuid)))
{
Debug.Fail(String.Format("IVsolution::GetGuidOfProject returned 0x{0:X}.", res));
continue;
}
string uniqueName;
if (ErrorHandler.Failed(res = sol.GetUniqueNameOfProject(hier[0], out uniqueName)))
{
Debug.Fail(String.Format("IVsolution::GetUniqueNameOfProject returned 0x{0:X}.", res));
continue;
}
if( System.IO.Path.GetInvalidPathChars().Any (p =>uniqueName.Contains(p) ))
{
uniqueName.Dump("invalid filename found");
yield return new ProjInfo(projGuid,uniqueName);
}
else {
yield return new ProjInfo(projGuid, Path.GetFileName(uniqueName).BeforeOrSelf("{"));
}
}
}
got most of it from http://social.msdn.microsoft.com/Forums/en-US/vsx/thread/60fdd7b4-2247-4c18-b1da-301390edabf3/
I am attempting to programmatically add a test project to a solution. However when the code below executes I receive a File IO exception on the line "vhaSolution.GetProjectTemplate("TestProject.zip", "Csharp")". The error indicates that "he language specified is not supported by any of the installed packages". Does anyone have any idea what could be causing this?
public enum TestProjectType
{
Unit,
Acceptance,
Integration
}
public static void CreateTestProject(string fullyQualifiedSolutionFileName,string projectName,TestProjectType testProjectType)
{
#region Argument Validation
if (String.IsNullOrEmpty(fullyQualifiedSolutionFileName) || String.IsNullOrEmpty(fullyQualifiedSolutionFileName.Trim()))
{
throw new ArgumentNullException("fullyQualifiedSolutionFileName", "The solution file location is required.");
}
if (String.IsNullOrEmpty(projectName) || String.IsNullOrEmpty(projectName.Trim()))
{
throw new ArgumentNullException("projectName", "The project name is required.");
}
if (!File.Exists(fullyQualifiedSolutionFileName))
{
throw new ArgumentException(String.Format("The file {0} specified does not exist.", fullyQualifiedSolutionFileName));
}
if (testProjectType == null) testProjectType = TestProjectType.Unit;
#endregion
System.Type vsType = System.Type.GetTypeFromProgID("VisualStudio.DTE.8.0");
Object vs = System.Activator.CreateInstance(vsType, true);
EnvDTE80.DTE2 dte8Obj = (EnvDTE80.DTE2)vs;
Solution2 vhaSolution = (Solution2)dte8Obj.Solution;
vhaSolution.Open(fullyQualifiedSolutionFileName);
//TODO: Externalize company name
string cmpnyName = "Vha";
string testProjectName = String.Format("{0}.{1}.{2}{3}",cmpnyName,projectName,testProjectType.ToString(),"Test");
string testTemplateLocation = vhaSolution.GetProjectTemplate("TestProject.zip", "CSharp");
FileInfo rootSolutionFolder = new FileInfo(fullyQualifiedSolutionFileName);
//TODO: Externalize test directory name
string testDirName = String.Format("{0}\\{1}\\{2}\\{3}",rootSolutionFolder.DirectoryName,"test",testProjectType.ToString(),testProjectName);
if (!Directory.Exists(testDirName))
{
//may throw an exception if the dir can't be created...
Directory.CreateDirectory(testDirName);
}
Project vhaTestProj = vhaSolution.AddFromTemplate(testTemplateLocation,testDirName,testProjectName + ".proj",false);
vhaTestProj.Save(String.Format("{0}\\{1}.proj",testDirName , testProjectName));
}
I figured it out. I need to use the prog ID VisualStudio.DTE.9.0 so that it would point to the correct registry location for VS 2008.