Add VSIX features to C# Class Library
Asked Answered
C

2

10

I have an existing Single-File Generator (housed in a C# Class Library). How do you add the VSIX project-level features to this project? The end goal is to compile my class library project and get a VSIX.

(I'm actually answering my own question. This is in relation to SIngle-file generator changes in Visual Studio 2017 - but that question wasn't asking what I'm answering here.)

Cloister answered 16/3, 2017 at 18:44 Comment(0)
C
29

First off,

your Single-File Generator class needs to have the appropriate class-level attributes:

using System.Runtime.InteropServices;
using Microsoft.VisualStudio.Shell;
using VSLangProj80;

[ComVisible(true)]
[Guid("your-unique-identifier")]
[CodeGeneratorRegistration(
    typeof(MyCustomTool),
    "MyCustomTool",
    vsContextGuids.vsContextGuidVCSProject,
    GeneratesDesignTimeSource = true,
    GeneratorRegKeyName = "MyCustomTool"
)]
[ProvideObject(
    typeof(MyCustomTool),
    RegisterUsing = RegistrationMethod.CodeBase
)]
public sealed class MyCustomTool : IVsSingleFileGenerator {

All of these attributes will ensure that a .pkgdef file is correctly generated within your VSIX. The .pkgdef file contains the registry entries that are used to register your single-file generator with Visual Studio.

Second,

add a text file "source.extension.vsixmanifest" to your project. Its "Build Action" should be "None." Give it some default text of:

<?xml version="1.0" encoding="utf-8"?>
<PackageManifest Version="2.0.0" xmlns="http://schemas.microsoft.com/developer/vsx-schema/2011" xmlns:d="http://schemas.microsoft.com/developer/vsx-schema-design/2011">
  <Metadata>
    <Identity Id="MyCustomTool.MyCompany.another-random-guid" Version="1.0" Language="en-US" Publisher="MyCompany" />
    <DisplayName>MyCustomTool</DisplayName>
    <Description>Helpful information</Description>
  </Metadata>
  <Installation>
    <InstallationTarget Id="Microsoft.VisualStudio.Community" Version="[15.0]" />
  </Installation>
  <Dependencies>
    <Dependency Id="Microsoft.Framework.NDP" DisplayName="Microsoft .NET Framework" d:Source="Manual" Version="[4.5,)" />
  </Dependencies>
  <Prerequisites>
    <Prerequisite Id="Microsoft.VisualStudio.Component.CoreEditor" Version="[15.0,16.0)" DisplayName="Visual Studio core editor" />
  </Prerequisites>
</PackageManifest>

Most of this stuff is pretty esoteric. In the next step, we'll make it so you can edit this file with a designer.

Third

(and to answer the original question), you need to manhandle the .csproj file (your C# Class Library file). Specifically, you need to add the following:

<PropertyGroup>
  <VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
  <ProjectTypeGuids>{82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
</PropertyGroup>
<ItemGroup>
  <None Include="source.extension.vsixmanifest">
    <SubType>Designer</SubType>
  </None>
</ItemGroup>
<PropertyGroup>
  <UseCodebase>true</UseCodebase>
</PropertyGroup>
<Import Project="$(VSToolsPath)\VSSDK\Microsoft.VsSDK.targets" Condition="'$(VSToolsPath)' != ''" />

So, what have we done here? Let's break it down.

First, we setup a path to the location of the Visual Studio toolset. Afterall, this is now an "extensions project." So, it needs to know where the VS-SDK is located.

Then, we changed the "ProjectTypeGuids" (which probably wasn't in your project file to begin with). Originally, it just included the guid for C# (which is the "FAE04EC0-..." guid). Now, it also includes the guid for VSIX (which is the "82b43b9b-..." guid).

We also made sure the "source.extension.vsixmanifest" file uses its new, fancy designer (instead of editing the file by hand).

The "UseCodeBase" element is important. This element prevents you from being forced to register your Generator with the system's COM registry. Instead, Visual Studio will simply load up your Generator from its installation location.

At the bottom, we import the MsBuild stuff for the VS-SDK.

Fourth,

Load your project back up. Go to the Project Properties screen and you'll see a new "VSIX" section at the bottom. Open that section and check the "Create VSIX Container during build" checkbox.

At this point, you can also double-check the "source.extension.vsixmanifest" file. Depending on how fancy your Generator is, you can change whatever you need. (The contents of the file that I pasted above is pretty much exactly what I used for my project.)

And finally,

you can compile your project. In the bin folder, you'll find MyCustomTool.dll and MyCustomTool.vsix. The .vsix file is simply a zip file. Inside the .vsix, you'll find MyCustomTool.pkgdef.

If we've done everything correctly, the .pkgdef file should look something like this:

[$RootKey$\Generators\{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}\MyCustomTool]
@="MyCustomTool"
"CLSID"="{your-unique-identifier}"
"GeneratesDesignTimeSource"=dword:00000001
[$RootKey$\CLSID\{your-unique-identifier}]
@="MyCustomTool"
"InprocServer32"="$WinDir$\SYSTEM32\MSCOREE.DLL"
"Class"="MyCustomTool"
"CodeBase"="$PackageFolder$\MyCustomTool.dll"
"ThreadingModel"="Both"

And, I think this is the longest SO answer I've written. And probably, only 5 people will ever read this :)

Cloister answered 16/3, 2017 at 18:44 Comment(13)
Don't worry, I'm the 6thBohrer
I've tried to migrate an old ISingleFileGenerator project to a vsix extension project. Where are the types CodeGeneratorRegistrationAttribute and ProvideObjectAttribute defined that you have used?Wastrel
They're both in the Microsoft.VisualStudio.Shell namespace. Depending on which version of VisualStudio you're using the assembly is Microsoft.VisualStudio.Shell.14.0.Cloister
In which Assembly is vsContextGuids.vsContextGuidVCSProject defined?Wastrel
msdn.microsoft.com/en-us/library/…Cloister
This helped a lot in getting a single file generator running. Thanks. The msdn documentation about the subject is a mess :(Fyke
One can also define it by hand: public const string vsContextGuidVCSProject = "{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}"; public const string vsContextGuidVBProject = "{164B10B9-B200-11D0-8C61-00A0C91E29D5}";Alfie
And here is the 7th one. Thank you for your effort.Subtitle
I'm confused.. i followed this step by step but don't get a pkgdef file in my vsixGules
I needed two more things to make it work: (1) the csproj must contain <GeneratePkgDefFile>true</GeneratePkgDefFile>, or simply click the project in Visual Studio and make sure that the "Generate .pkgdef File" and "Include Assembly in VSIX" options are True in the Properties pane. (2) my SFG had two base classes; each needed the [ComVisible(true)] attribute (Eliseu's suggestion of [ClassInterface(ClassInterfaceType.None)] didn't work for me).Alfie
Oh, and you might need to add an "asset" to the vsix manifest.Alfie
This answer enabled me to fix a huge problem we have been having with a SFG and our build process. Many, many thanks for taking the time to post this.Chromatology
VSX was/is a sort of painful area :) Thanks for putting this all together @Cloister I am getting weird error 'error MSB6003: The specified task executable "CreatePkgDef.exe" could not be run. The filename or extension is too long' I remember I had this 10 years ago, but can't remember how I fixed that. If anyone remembers please point me to the right direction. Thanks!Beitnes
H
3

If, instead of implementing the IVsSingleFileGenerator on your class, your class inherits from the abstract class BaseCodeGeneratorWithSite that inherits from the abstract class BaseCodeGenerator that implements IVsSingleFileGenerator you need to add the following atribute to your class, to avoid the error message "Cannot find custom tool '...' on this system":

[ClassInterface(ClassInterfaceType.None)]

The reason is that the abstract class BaseCodeGeneratorWithSite is not COM visible.

Handset answered 18/5, 2017 at 10:15 Comment(2)
The classinterface attribute should replace the [ComVisible(true)]Nameplate
That saved me. I was struggling for three days why my older VSIX SFG works and new one does not. Adding that attribute to the new one helped (but the old one does not have it, strange).Lest

© 2022 - 2024 — McMap. All rights reserved.