I'm trying to create simple extension for Visual Studio for Mac which will handle the moment when user saves the document (sample project is on GitHub, right here).
Here's how looks my implementation of ICommandHandler<SaveCommandArgs>:
[Export(typeof(ICommandHandler))]
[Name(nameof(SaveCommandHandler))]
[ContentType(StandardContentTypeNames.Code)]
[TextViewRole(PredefinedTextViewRoles.PrimaryDocument)]
public class SaveCommandHandler : ICommandHandler<SaveCommandArgs>
{
public string DisplayName => nameof(SaveCommandHandler);
private readonly IEditorCommandHandlerServiceFactory _editorCommandHandlerServiceFactory;
[ImportingConstructor]
public SaveCommandHandler(IEditorCommandHandlerServiceFactory editorCommandHandlerServiceFactory)
{
_editorCommandHandlerServiceFactory = editorCommandHandlerServiceFactory;
}
public bool ExecuteCommand(SaveCommandArgs args, CommandExecutionContext executionContext)
{
try
{
var service = _editorCommandHandlerServiceFactory.GetService(args.TextView);
Debug.WriteLine($"I am executing something on save with {service.GetType()}");
}
catch (Exception ex)
{
Debug.WriteLine(ex);
}
return true;
}
public CommandState GetCommandState(SaveCommandArgs args)
{
return CommandState.Available;
}
}
Good point: system call the constructor of this handler, when you start editing the first file
Bad point: ExecuteCommand method never called, as well as GetCommandState method
Any ideas why it doesn't work?
I was trying to do all the things according to the documentation on official wiki in github project for visual studio api
Unfortunately, there're only samples for quick info and autocomplete features. No samples for ICommandHandler-s, haven't found any similar projects for Visual Studio for Mac as well
I agree the examples since Microsoft Visual Studio SDK documentation is horrible with no good examples of ICommandHandler.
I tried your exact code in visual studio 2022 on Windows 10 and the ExecuteCommand function is called.
So your code is fine.
I think the core problem here is that the ICommandHandler interface structure is somewhat newer and Microsoft did a poor job of properly checking for custom command handlers and adding calls.
I am trying to do a similar thing where I add an ICommandHandler to intercept the GoToDefinition command using an ICommandHandler, using this very similar code I can't get the ExecuteCommand function to fire ever.
I have been able to intercept commands using Microsofts older, yuckier DTE or DTE2 interface.
//provider constructor code
var dte2 = (DTE2)Package.GetGlobalService(typeof(DTE));
dte2.Events.CommandEvents.BeforeExecute += CommandEvents_BeforeExecute;
private static void CommandEvents_BeforeExecute(string guid, int id, object customIn, object customOut, ref bool cancelDefault)
{ //All events fired here use, the guid you want is likely
// Microsoft.VisualStudio.VSConstants.CMDSETID.StandardCommandSet97_string
// With an ID defined in
// Microsoft.VisualStudio.VSConstants.VSStd97CmdID
Debug.WriteLine("CommandEvents_BeforeExecute1 " + String.Format(
"dte2 GUID: {0}\nID: {1}\nIn: {2}\nOut: {3}",
guid, id, customIn, customOut));
}
Related
Following various examples from MS and elsewhere, I have written this piece of test code...
[ComImport]
[Guid("4AEEEC08-7C92-4456-A0D6-1B675C7AC005")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IInitializeWithWindow
{
void Initialize(IntPtr hwnd);
}
and..
private async Task<bool> TestCode()
{
StoreContext Store = StoreContext.GetDefault();
StoreAppLicense licence = await Store.GetAppLicenseAsync();
bool trial = licence.IsTrial;
bool full = licence.IsActive;
IInitializeWithWindow initWindow = (IInitializeWithWindow)(object)Store;
initWindow.Initialize(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle);
StoreProductResult App = await Store.GetStoreProductForCurrentAppAsync();
StoreProduct p = App.Product; // Title, price
string title = p.Title;
StorePrice price = p.Price;
return true;
}
And I call it with using
bool x = TestCode().Result;
It all compiles and runs, so I presumably have all the right usings and references added. But when run, the line:
IInitializeWithWindow initWindow = (IInitializeWithWindow)(object)Store;
stops with the exception..
Unable to cast object of type 'Windows.Services.Store.StoreContext'
to type 'IInitializeWithWindow'
and I have no clue why.
This is a C# program with a UWP wrapper creating an MSIX package.
This seems to be a pretty standard block adapted from various examples from MS.
Within VS 2019, I have associated the program with the store app.
The 'trail' and 'full' variables seem to be populating correctly.
I have called this from various locations, Constructor, random button, etc.
My questions...
Why does the cast throw an exception?
Is this an old way of doing things that no longer applies?
Does associating the package in VS 2019 to the store app make the call to IInitalizeWithWindow redundant?
How do I fix the code so that 'title' and 'price' populate correctly?
Heaps of head bashing and I finally have it working...
Considering that in the last few days there was not a combination/permutation that I did not try, I don't know really the logic of it working now, but anyway..
Within the UWP installer project I associated the project with the App in the Microsoft Store, then I removed the lines:
[ComImport]
[Guid("4AEEEC08-7C92-4456-A0D6-1B675C7AC005")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
private interface IInitializeWithWindow
{
void Initialize(IntPtr hwnd);
}
IInitializeWithWindow initWindow = (IInitializeWithWindow)(object)Store;
initWindow.Initialize(System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle);
The rest is now working just fine. Funny as I had already associated the app with the store AND removed the offending lines. I must have done something just a little bit different this time!
I'd like to create a solution that controls the project references in C# projects. Ideally, this solution is IDE-agnostic so that it can be used with Visual Studio, Jetbrains Rider, or even VS Code. The reason for this is that I've seen solutions that are completely messed up due to people creating almost arbitrary project references. It's super hard to get them straight after a project has grown to a certain size.
I know that Visual Studio Enterprise offers this out-of-the-box. Unfortunately, in my current company we do not have VS Enterprise. Thus, I want to create that on my own.
So what would be the best way to do it? After doing some research I think leveraging the .NET Compiler Platform ("Roslyn") with its Workspace API might be a good idea? Seems like I could deploy it as a NuGet package which can then be used in any IDE or build-automation. But maybe there's an easier or better way, I'd like to hear your opinion on that before I start digging into it.
Also: if the "Roslyn"-way is the right one is there some good resources on how to create an analyzer that works with the Workspace APIs?
Thanks in advance.
In your analyser, register a compilation start action:
public override void Initialize(AnalysisContext context)
{
context.RegisterCompilationStartAction(Initialize);
}
private void Initialize(CompilationStartAnalysisContext context)
{
var compilation = context.Compilation;
}
From that compilation object, you have various options:
var referencedAssemblyNames = compilation.ReferencedAssemblyNames;
or
var references = compilation.References;
Then do your analysis. To report diagnostics, register an action on the CompilationStartAnalysisContext using context.RegisterCompilationEndAction.
If you don't need to look at actual project content for your analysis, you can simply use RegisterCompilationAction as follows:
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class TooManyReferencesAnalyzer : DiagnosticAnalyzer
{
private static DiagnosticDescriptor TooManyReferences { get; } =
new DiagnosticDescriptor(
"DEMO",
"Don't use too many references",
"The project '{0}' has {1} references",
category: "Maintainability",
defaultSeverity: DiagnosticSeverity.Warning,
isEnabledByDefault: true);
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(TooManyReferences);
public override void Initialize(AnalysisContext context)
{
context.RegisterCompilationAction(AnalyzeCompilation);
}
private void AnalyzeCompilation(CompilationAnalysisContext context)
{
var compilation = context.Compilation;
int referenceCount = compilation.References.Count();
if (referenceCount > 5)
{
context.ReportDiagnostic(
Diagnostic.Create(
TooManyReferences,
null,
compilation.AssemblyName,
referenceCount));
}
}
}
In my solution I have 2 projects.
One is the controller, which in the final product is used to check if a execution is issued from console/non user input and therefore will execute the wanted changes in background based on imput from a xml-file, or if the execution was issued by user input, which will open an interface.
A non user input would maybe a planed task or something like that, but thats is for an other time now and I just write that for some context.
In both cases, sooner or later there is the need to access word-documents and read, write and change document-properties.
To do that I created a VSTO-Word-Addin with the needed functions and up to this point I hardcoded the paths and didn't return the results anywhere else than an other document.
Since I am sure my code in VSTO itself works, I wanted to extend the prototype to the next level and tried adding the connections between console and VSTO.
For testing I am simplifying the process a bit and just try to establish the connection between console and VSTO without any userinput and trying to execute some methods to test functionality of my VSTO.
My approach was to open the console, which then opens Word/the addin, open the file hidden and do the magic.
First thing to do is to set a path for the document to be opened and then call multiple methods with returned values.
In this case my VSTO returns a true for
SetCustomProperty
and a new List of Tuples for
GetCustomProperties
Those are placeholders and will be replaced in developement.
I already tried some possible solutions, but most of them go the other way around to start a WinForms/WPF/Console out of VSTO or try to call an other AddIn from their AddIn.
The approach I had the most success with was this one:
MSDN
Calling Code in VSTO Add-ins from Other Office Solutions
But of course this is for office so I hit the problem of not being able to use
Globals
More info about Globals can be found here in MSDN
So maybe I am missing the point and am just blind, but how can I call a class in a VSTO-project from console?
Here are some codesamples of my current failure:
The class with the used interface I want to access:
[ComVisible(true)]
public interface IPropertyReadWriter
{
bool Open(string Path);
bool SetCustomProperty(String Name, PropertyTypes Type, object Value);
List<Tuple<String, PropertyTypes, object>> GetCustomProperties();
}
[ComVisible(true)]
public class PropertyReaderWriter : IPropertyReadWriter
{
public List<Tuple<string, PropertyTypes, object>> GetCustomProperties()
{
return new List<Tuple<string, PropertyTypes, object>>();
}
public bool Open(string Path)
{
return false;
}
public bool SetCustomProperty(string Name, PropertyTypes Type, object Value)
{
return false;
}
}
The code used in the MSDN article about calling from other office-project:
object addInName = "ExcelImportData";
Office.COMAddIn addIn = Globals.ThisAddIn.Application.COMAddIns.Item(ref addInName);
ExcelImportData.IAddInUtilities utilities = (ExcelImportData.IAddInUtilities)addIn.Object;
utilities.ImportData();
I dont know how to make use of this, because I don't have access to Globals outsite of VSTO?
Somewhat similar question on so with no answer I could use, because lack of context or example:
I don't know what Dan Byström meant with his answer, also Mike Regan's answer lead to prior stated MSDN.
How to call a VSTO AddIn method from a separate C# project?
First, to your Addin that you want to call into add an Interface:
[ComVisible(true)]
public interface IExcelUtilities
{
bool DoSomething();
}
Next, add a class that implements the interface:
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class AddInUtilities :
StandardOleMarshalObject,
IExcelUtilities
{
public bool DoSomething()
{
return true;
}
}
Then override object RequestComAddInAutomationService in ThisAddin.cs:
private AddInUtilities utilities;
protected override object RequestComAddInAutomationService()
{
try
{
if (utilities == null)
{
utilities = new AddInUtilities();
}
return utilities;
}
catch (System.Exception ex)
{
// Catch your ex here
}
}
Now you should be able to call the exposed method from your external application like this:
foreach (COMAddIn comaddin in addins)
{
if (comaddin.ProgId.Equals("YourAddinNameHere", StringComparison.InvariantCultureIgnoreCase) == true)
{
bool returnvalue = comaddin.Object.DoSomething();
break;
}
}
for some more deep info on this subject, also read:
http://blogs.msdn.com/b/andreww/archive/2008/08/11/why-your-comaddin-object-should-derive-from-standardolemarshalobject.aspx
Hope it helps :-)
This isn't an answer exactly, but for others coming across this, document-level solutions cannot expose interfaces to other solutions.
Expose an object in a VSTO Add-in to other Microsoft Office solutions.
VSTO Add-in projects. Call code in VSTO Add-ins from other Office solutions
I am trying to include a custom language support for Visual Studio.
To start with, I need to have GoToDefinition support. And I am struggling to get the context menu to include this command.
I have defined a ContentTypeDefinition and have included the FileExtensionToContentTypeDefinition such as:
internal sealed class GaugeFileContentType
{
[Export]
[Name("Gauge")]
[BaseDefinition("code")]
internal static ContentTypeDefinition GaugeContentTypeDefinition = null;
[Export]
[FileExtension(".spec")]
[ContentType("Gauge")]
internal static FileExtensionToContentTypeDefinition GaugeFileExtensionDefinition = null;
}
Now, despite this, on debugging, I see that DTE.ActiveDocument.Type is text, despite me adding the [BaseDefinition('code')] attribute. What am I missing here?
Are the above definitions enough to tell Visual Studio to bring up Context menu for code?
I am using Visual Studio 2013 Ultimate.
After a few days of head banging, I managed to figure out a way.
I was using the Experimental Instance for debugging, and it did not clean and reinstall the extension, and thus Visual Studio continued to treat the ContentType as 'Plain Text', since that was what I had originally.
When I build a VSIX and installed, opened the same file in a new instance of Visual Studio, it brought up the right context menu.
However, it brought out more than what I wanted (i.e Run Unit Tests from Resharper). So I did some more digging up.
In order to ensure that Visual Studio can handle a command, it checks for it by calling IOleCommandTarget.QueryStatus method.
All I had to do was set the CommandFlag as (uint)OLECMDF.OLECMDF_ENABLED | (uint)OLECMDF.OLECMDF_SUPPORTED and return VSConstants.S_OK when the cmdId is VSConstants.VSStd97CmdID.GotoDefn.
The final method looks like this:
public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText)
{
if ((VSConstants.VSStd97CmdID)prgCmds[0].cmdID == VSConstants.VSStd97CmdID.GotoDefn)
{
prgCmds[0].cmdf = (uint)OLECMDF.OLECMDF_ENABLED | (uint)OLECMDF.OLECMDF_SUPPORTED;
return VSConstants.S_OK;
}
return Next.QueryStatus(pguidCmdGroup, cCmds, prgCmds, pCmdText);
}
There are a similar question Generate a C# delegate method stub and a feature request at microsoft.com Generate method stub should support non-existent method names passed as delegates referenced in its author's blog (No "Add Method Stub" When Passing or Assigning Delegates). In the question they said the feature was going to be available in VS 2010, and the feature request is marked as fixed. However, I don't get any hints for creating delegate handlers in my code (using VS 2010). How is this and what am I doing wrong? Here is an example code:
class BaseEditor { }
delegate void ObjectEditorCreatedDelegate( BaseEditor editor );
class ArrayPropertyEditor
{
public ObjectEditorCreatedDelegate OnObjectEditorCreated;
}
class SomeUserCode
{
public void someMethod()
{
ArrayPropertyEditor ape = new ArrayPropertyEditor();
ape.OnObjectEditorCreated = new ObjectEditorCreatedDelegate( OnEditorCreated );
// -------------------------------------------------------------------------^
}
private void OnEditorCreated( BaseEditor editor ) { }
}
When the cursor is at the pointed place (after OnEditorCreated usage) and OnEditorCreated is not yet created, VS 2010 suggests "generate property stub" and "generate field stub". How do I generate a method?