I want to create a context menu command for a project. I managed to get the command to show on the right menu by setting the id in the .vsct file to "IDM_VS_CTXT_PROJNODE". And my code example is called correctly:
private FirstCommand(AsyncPackage package, OleMenuCommandService commandService)
{
this.package = package ?? throw new ArgumentNullException(nameof(package));
commandService = commandService ?? throw new ArgumentNullException(nameof(commandService));
var menuCommandID = new CommandID(CommandSet, CommandId);
var menuItem = new MenuCommand(StartNotepad, menuCommandID);
commandService.AddCommand(menuItem);
}
private void StartNotepad(object sender, EventArgs e)
{
//example code
/*var process = new Process();
process.StartInfo.FileName = "Notepad.exe";
process.Start();*/
}
I now need Information about the project (directory, name etc). But all examples on vsix projects only show how to get the current project (I don't even know whether that is the project I clicked on) or don't work for me. They are all old and I don't know if they are currently best practice.
So my question is how do I get information about the project in StartNotepad()?
Thanks for your help.
Use the following method to get an item you clicked on:
private EnvDTE.UIHierarchyItem GetSelectedSolutionExplorerItem()
{
EnvDTE.UIHierarchy solutionExplorer = dte.ToolWindows.SolutionExplorer;
object[] items = solutionExplorer.SelectedItems as object[];
if (items.Length != 1)
return null;
return items[0] as EnvDTE.UIHierarchyItem;
}
And then convert it to a project with GetSelectedSolutionExplorerItem()?.Object as EnvDTE.Project.
Sergey's answer helped me to find the solution. The only think missing from it was how to get the dte in an async way:
private EnvDTE.UIHierarchyItem GetSelectedSolutionExplorerItem()
{
ThreadHelper.ThrowIfNotOnUIThread();
var dte = ServiceProvider.GetServiceAsync(typeof(DTE)).Result as DTE2;
if (dte == null) return null;
var solutionExplorer = dte.ToolWindows.SolutionExplorer;
object[] items = solutionExplorer.SelectedItems as object[];
if (items.Length != 1)
return null;
return items[0] as UIHierarchyItem;
}
Related
I need help using Utils in C#.
private void manageUsersToolStripMenuItem_Click(object sender, EventArgs e)
{
if (!Utils.FormIsOpen("ManageUsers"))// this line gives an error
{
var manageUsers = new ManageUsers();
manageUsers.MdiParent = this;
manageUsers.Show();
}
}
I am using Microsoft SQL Server Management Studio 18 along with visual studio and I am trying to manage my users in the database while not physically adding them to the database but adding them at runtime.
It seems that there is no FormIsOpen method in Utils Class.
If you want to check if a "ManageUsers" has been opened, you can call Form.MdiChildren Property to get all mdichildforms and filter them.
// get all child form
Form[] children = this.MdiChildren;
var query = children.Select(c => c)
.Where(c => c is ManageUsers).ToList();
if(query.Count == 0)
{
var manageUsers = new ManageUsers();
manageUsers.MdiParent = this;
manageUsers.Show();
}
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?
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
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
I'm developing a code gen (VSIX) for my team and I'm intendedly using Roslyn as a fundamental engine after I have struggling with VSIX extensibility framework.
My code gen is currently able generate a new csproj for the solution and able to churn out boilerplate code base based on Templating project from VSIX extensibility. As ambitious I am, I'm trying not to rely on static templating project and use Roslyn to churn out the code instead.
My solution has a list of folders and each folder has a list of csproj.
My Question 1 is I'm trying to use Roslyn Workspace API to detect the current document (.cs) that has been open in code editor or am trying to get the current doc id of the selected cs file I right click from the solution explorer.
I have tried to use AdhocWorkspace which so far have failed as I'm not able to get any.
Question 2: If I were to use AdhocWorkspace, am I able to change the default namespace in the csproj properties? Or was it not part of the functionalities in Roslyn Workspace API at the moment?
Thanks.
For #1 some code I have to do the same thing. I am doing stuff with the cursor so I'm going through caretPosition (cursor). There are other ways but the gist is the same, get the current textview the go from that to Roslyn.
You will need to install Microsoft.CodeAnalysis.EditorFeatures.Text which brings in allot of code analysis packages but allows you to use he GetOpenDocumentInCurrentContextWithChanges extension applied on ITextSnapshot
private IVsEditorAdaptersFactoryService GetEditorAdaptersFactoryService()
{
IComponentModel componentModel =(IComponentModel)GetService(typeof(SComponentModel));
return componentModel.GetService<IVsEditorAdaptersFactoryService>();
}
private Microsoft.VisualStudio.Text.Editor.IWpfTextView GetTextView()
{
IVsTextManager textManager = (IVsTextManager)GetService(typeof(SVsTextManager));
if (textManager == null)
return null;
IVsTextView textView = null;
textManager.GetActiveView(1, null, out textView);
if (textView == null)
return null;
return GetEditorAdaptersFactoryService().GetWpfTextView(textView);
}
//code to get the doc
Microsoft.VisualStudio.Text.Editor.IWpfTextView textView = GetTextView();
if (textView != null)
{
SnapshotPoint caretPosition = textView.Caret.Position.BufferPosition;
Document document = caretPosition.Snapshot.GetOpenDocumentInCurrentContextWithChanges();
//do stuff with Roslyn Document
}
"or am trying to get the current doc id of the selected cs file I right click from the solution explorer."
This is really ugly but what I used from a different SO post (don't remember the author) that does work well.
private 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)
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;
// 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 );
}
}
IVsHierarchy hierarchy = null;
uint itemid = VSConstants.VSITEMID_NIL;
if (!IsSingleProjectItemSelection(out hierarchy, out itemid))
return;
string itemFullPath = null;
((IVsProject)hierarchy).GetMkDocument(itemid, out itemFullPath);
if (itemFullPath.EndsWith(".cs"))
I'm trying to publish a media item programmatically but I am encountering a problem with the PublishOptions object. At runtime, the line of code where the PublishOptions object is being instantiated is breaking when I upload a media item. I get the following message saying:
"Value cannot be null. Parameter Name: item"
And the stacktrace is:
at Sitecore.Diagnostics.Assert.ArgumentNotNull(Object argument, String argumentName)
at Sitecore.Data.Managers.ItemProvider.ApplySecurity(Item item, SecurityCheck securityCheck)
at Sitecore.Data.Managers.ItemProvider.GetRootItem(Language language, Version version, Database database, SecurityCheck securityCheck)
at Sitecore.Data.Managers.ItemManager.GetRootItem(Language language, Version version, Database database)
at Sitecore.Nexus.Data.DataCommands.ResolvePathCommand.(String itemPath, Database database)
at Sitecore.Nexus.Data.DataCommands.ResolvePathCommand.(String itemPath, Database database)
at Sitecore.Nexus.Data.NexusDataApi.ResolvePath(String itemPath, Database database)
at Sitecore.Data.Engines.DataCommands.ResolvePathCommand.DoExecute()
at Sitecore.Data.Engines.EngineCommand`2.Execute()
at Sitecore.Data.Engines.DataEngine.ResolvePath(String itemPath)
at Sitecore.Data.Managers.ItemProvider.ResolvePath(String itemPath, Database database)
at Sitecore.Data.Managers.ItemProvider.GetItem(String itemPath, Language language, Version version, Database database, SecurityCheck
Here's the code where the PublishOptions object is being instantiated (where it is breaking):
public void OnItemSaved(Object sender, EventArgs args)
{
var item = Event.ExtractParameter(args, 0) as Item;
using (new SecurityDisabler())
{
if (item != null)
{
if (item.Paths.IsMediaItem)
{
var source = new Database("master");
var target = new Database("web");
var options = new PublishOptions(source, target, PublishMode.SingleItem, item.Language, DateTime.Now)
{
RootItem = item,
Deep = true,
};
var publisher = new Publisher(options);
publisher.PublishAsync();
}
}
}
}
What could be the reason why I'm getting this error?
Try this and see if this works, I am guessing it is to with the database.
public void OnItemSaved(Object sender, EventArgs args)
{
var item = Event.ExtractParameter(args, 0) as Item;
using (new SecurityDisabler())
{
if (item != null)
{
if (item.Paths.IsMediaItem)
{
var source = Factory.GetDatabase("master");
var target = Factory.GetDatabase("web");
var options = new PublishOptions(source, target,
PublishMode.SingleItem, item.Language,
DateTime.Now)
{
RootItem = item,
Deep = true,
};
var publisher = new Publisher(options);
publisher.PublishAsync();
}
}
}
}
I think you're being caught in a classic mistake. item:saved will also fire for your items as they're being published.
My initial guess would be, that you should expand your check to be
if ( item != null && item.Database.Name == "master" )
to prevent your code from attempting to publish the item, as the item:saved event fires on "web" during publishing.
I have a program that populates a combo box with details of files contained in a selected directory in a perforce depot.
The relevant piece of code is this:
PerforcePath dir = _ctlProductSelect.SelectedItem as PerforcePath;
_ctlServicePackSelect.Items.Clear();
if (dir != null)
{
foreach (P4.File file in _perforce.GetFiles(null, P4.FileSpec.DepotSpec(dir.Path + "/*.sp")))
{
_ctlServicePackSelect.Items.Add(new PerforcePath(file.DepotPath.Path));
}
}
The problem is that this also includes files that are marked as deleted. Is there any way I can filter the deleted files from the list returned by GetFiles method? I can't find any likely suspects in the P4_dotNet API documentation.
Using P4API.NET, you can add the -e option to GetFiles:
IList filesToFind = new List();
FileSpec fileToFind = new FileSpec(new DepotPath("//depot/..."), null, null, VersionSpec.Head);
filesToFind.Add(fileToFind);
Options o = new Options();
o.Add("-e", "");
IList filesFound = pRep.GetFiles(filesToFind, o);
What I eventually got working was to do this inisde the foreach loop:
foreach (P4.File file in _perforce.GetFiles(null, P4.FileSpec.DepotSpec(dir.Path + "/*.sp")))
{
if (_perforce.GetFileMetaData(null, file)[0].HeadAction.ToString() != "MoveDelete")
_ctlServicePackSelect.Items.Add(new PerforcePath(file.DepotPath.Path));
}
basically checking the metadata for each file before adding it to the combobox.
IList<FileSpec> filesToFind = new List<FileSpec>();
FileSpec fileToFind = new FileSpec(FileSpec.DepotSpec("//depot/...").DepotPath, Revision.Head);
filesToFind.Add(fileToFind);
Options o = new Options();
o.Add("-m", "changelistid");
IList<File> FilesFound = rep.GetFiles(filesToFind, o)