I am using a setup project to publish my projects. I want the version of each project to be the same as the setup version.
I want to change my setup version property in Visual Studio and after building, for all project versions to be updated from this property, is this possible?
Projects have Assembly & File version numbers: (not setup versions I edited your question accordingly)
Answer 1:
If you want to make the Setup projects version number set the Assembly & File version numbers you need to do it with a script/exe that gets triggered by the build.
This article on How To Update Assembly Version Number Automatically shows half the solution...
From the research I did it is not possible to use the SetupVersion in a PreBuildEvent. There isn't a $SetupVersion command for it: http://msdn.microsoft.com/en-us/library/42x5kfw4(v=vs.80).aspx
Having to change the PreBuildEvent each build as shown in this comment in the Code Project article using the -set: command is not ideal.
The solution we need is a PreBuildEvent to call the AssemblyInfoUtil.exe and have it read the "ProductVersion" from the vdproj project file. And then update the Assembly version number(s).
I have modified the code from the article to show you how to read the product version from the Setup.vdproj and this is how it can be called from a PreBuildEvent:
AssemblyInfoUtil.exe -setup:"C:\Program Files\MyProject1\Setup1\Setup1.vdproj" -ass:"C:\Program Files\MyProject1\AssemblyInfo.cs"
This is the modified code:
using System;
using System.IO;
using System.Text;
namespace AssemblyInfoUtil
{
class AssemblyInfoUtil
{
private static int incParamNum = 0;
private static string fileName = "";
private static string setupfileName = "";
private static string versionStr = null;
private static bool isVB = false;
[STAThread]
static void Main(string[] args)
{
for (int i = 0; i < args.Length; i++) {
if (args[i].StartsWith("-setup:")) {
string s = args[i].Substring("-setup:".Length);
setupfileName = int.Parse(s);
}
else if (args[i].StartsWith("-ass:")) {
fileName = args[i].Substring("-ass:".Length);
}
}
//Jeremy Thompson showing how to detect "ProductVersion" = "8:1.0.0" in vdproj
string setupproj = System.IO.File.ReadAllText(setupfileName);
int startPosOfProductVersion = setupproj.IndexOf("\"ProductVersion\" = \"") +20;
int endPosOfProductVersion = setupproj.IndexOf(Environment.NewLine, startPosOfProductVersion) - startPosOfProductVersion;
string versionStr = setupproj.Substring(startPosOfProductVersion, endPosOfProductVersion);
versionStr = versionStr.Replace("\"", string.Empty).Replace("8:",string.Empty);
if (Path.GetExtension(fileName).ToLower() == ".vb")
isVB = true;
if (fileName == "") {
System.Console.WriteLine("Usage: AssemblyInfoUtil
<path to :Setup.vdproj file> and <path to AssemblyInfo.cs or AssemblyInfo.vb file> [options]");
System.Console.WriteLine("Options: ");
System.Console.WriteLine(" -setup:Setup.vdproj file path");
System.Console.WriteLine(" -ass:Assembly file path");
return;
}
if (!File.Exists(fileName)) {
System.Console.WriteLine
("Error: Can not find file \"" + fileName + "\"");
return;
}
System.Console.Write("Processing \"" + fileName + "\"...");
StreamReader reader = new StreamReader(fileName);
StreamWriter writer = new StreamWriter(fileName + ".out");
String line;
while ((line = reader.ReadLine()) != null) {
line = ProcessLine(line);
writer.WriteLine(line);
}
reader.Close();
writer.Close();
File.Delete(fileName);
File.Move(fileName + ".out", fileName);
System.Console.WriteLine("Done!");
}
private static string ProcessLine(string line) {
if (isVB) {
line = ProcessLinePart(line, "<Assembly: AssemblyVersion(\"");
line = ProcessLinePart(line, "<Assembly: AssemblyFileVersion(\"");
}
else {
line = ProcessLinePart(line, "[assembly: AssemblyVersion(\"");
line = ProcessLinePart(line, "[assembly: AssemblyFileVersion(\"");
}
return line;
}
private static string ProcessLinePart(string line, string part) {
int spos = line.IndexOf(part);
if (spos >= 0) {
spos += part.Length;
int epos = line.IndexOf('"', spos);
string oldVersion = line.Substring(spos, epos - spos);
string newVersion = "";
bool performChange = false;
if (incParamNum > 0) {
string[] nums = oldVersion.Split('.');
if (nums.Length >= incParamNum && nums[incParamNum - 1] != "*") {
Int64 val = Int64.Parse(nums[incParamNum - 1]);
val++;
nums[incParamNum - 1] = val.ToString();
newVersion = nums[0];
for (int i = 1; i < nums.Length; i++) {
newVersion += "." + nums[i];
}
performChange = true;
}
}
else if (versionStr != null) {
newVersion = versionStr;
performChange = true;
}
if (performChange) {
StringBuilder str = new StringBuilder(line);
str.Remove(spos, epos - spos);
str.Insert(spos, newVersion);
line = str.ToString();
}
}
return line;
}
}
}
Answer 2:
To my way of thinking a better way is to use a Shared Assembly Info class rather than individual AssemblyInfo class files.
To implement this, create a file in the solution folder named SharedAssemblyInfo.cs and then add a link in each project to SharedAssemblyInfo.cs. You can also move the linked SharedAssemblyInfo.cs into the Properties folder so that it sits side-by-side with the AssemblyInfo.cs that is specific to each project in the solution, as shown below.
Here is a sample SharedAssemblyInfo.cs file:
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyCompany("Saint Bart Technologies")]
[assembly: AssemblyProduct("Demo")]
[assembly: AssemblyCopyright("Copyright ? Saint Bart 2013")]
[assembly: AssemblyTrademark("")]
// Make it easy to distinguish Debug and Release (i.e. Retail) builds;
// for example, through the file properties window.
#if DEBUG
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("Flavor=Debug")] // a.k.a. "Comments"
#else
[assembly: AssemblyConfiguration("Retail")]
[assembly: AssemblyDescription("Flavor=Retail")] // a.k.a. "Comments"
#endif
[assembly: CLSCompliant(true)]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// Note that the assembly version does not get incremented for every build
// to avoid problems with assembly binding (or requiring a policy or
// <bindingRedirect> in the config file).
//
// The AssemblyFileVersionAttribute is incremented with every build in order
// to distinguish one build from another. AssemblyFileVersion is specified
// in AssemblyVersionInfo.cs so that it can be easily incremented by the
// automated build process.
[assembly: AssemblyVersion("1.0.0.0")]
// By default, the "Product version" shown in the file properties window is
// the same as the value specified for AssemblyFileVersionAttribute.
// Set AssemblyInformationalVersionAttribute to be the same as
// AssemblyVersionAttribute so that the "Product version" in the file
// properties window matches the version displayed in the GAC shell extension.
[assembly: AssemblyInformationalVersion("1.0.0.0")] // a.k.a. "Product version"
Here is a sample AssemblyInfo.cs file:
// Note: Shared assembly information is specified in SharedAssemblyInfo.cs
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("WindowsFormsApplication2")]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("ffded14d-6c95-440b-a45d-e1f502476539")]
So each time you want to change all projects Assembly info you can do it in one spot. I assume you would want to set the MSI Setup Version the same as the Assembly version number, one manual step.
Answer 3:
Consider switching to use MSBuild it has all these kinds of benefits but I'm not sure if you have the time to pick it up right now.
Answer 4:
Assemblies can auto-increment their build numbers using the following asterisk syntax within AssemblyInfo.cs:
[assembly: AssemblyVersion("1.0.0.*")]
This is a good method because the point of tracking a build number is
to be able to recognize different builds. Having a pre-build changing
build numbers defeats this purpose as the build has not yet occurred.
Answer 5:
The other CodeProject answer here assumes you want to update the ProductVersion, ProductCode, PackageCode in the Setup MSI Project file. I didn't interpret your question that way and according to this thread there are problems:
pre-build event to change setup project's ProductVersion doesn't take effect until after the build
Answer 6 (new):
There is a few TFS Build plugins to set "Assembly Info": https://marketplace.visualstudio.com/items?itemName=bleddynrichards.Assembly-Info-Task
https://marketplace.visualstudio.com/items?itemName=bool.update-assembly-info
https://marketplace.visualstudio.com/items?itemName=ggarbuglia.setassemblyversion-task
I don't know if this solves your problem perfectly but you could implement a common class with all the configmanagment informations like:
public class VersionInfo{
public const string cProductVersion = "1.0.0"
//other version info
}
After you can update all your AssemblyInfo.cs with the new class:
[assembly: AssemblyVersion(VersionInfo.cProductVersion)]
I hope this helps.
Related
I have some code which must be able to generated a console application at runtime (Codegeneration with System.CodeDom). I did this already a lot, but in NET 6 now I am struggling with that and the new API. In the code below I try to compile simply from a string. See below the static class with method Start() which then should generates the application.
The compilations seems fine, no errors at the end. But when starting the generated AppCodegenerated.exe, it shows some reference exception with System.Runtime.
Please help, any Idea? Already researched a lot but could not find any useful solution..
//-
I used the Visual Studio 2022 / NET 6 and theses Nuget's:
using Basic.Reference.Assemblies;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using System.Text;
namespace CompilerSimplified
{
public static class Compiler
{
public static bool Start()
{
string FileName = "AppCodegenerated";
string ExePath = AppDomain.CurrentDomain.BaseDirectory + #"\" + FileName + ".exe";
string code = #"using System; Console.WriteLine(""Hello.""); Console.ReadLine(); ";
// ------- References -------------
// .net platform references
List<MetadataReference> References = new List<MetadataReference>();
foreach (var item in ReferenceAssemblies.Net60) // ReferenceAssemblies from Nuget: Basic.Reference.Assemblies;
References.Add(item);
// or tried this: loop manually through system platform
//string[] fileEntries = Directory.GetFiles(#"C:\Program Files\dotnet\packs\Microsoft.NETCore.App.Ref\6.0.0\ref\net6.0\", "*.dll");
//foreach (string fileName in fileEntries)
// references.Add(MetadataReference.CreateFromFile(fileName));MetadataReference.CreateFromFile(fileName));
// ------- References END -------------
// delete existing file
if (File.Exists(ExePath))
File.Delete(ExePath);
// compiler options
CSharpCompilationOptions DefaultCompilationOptions =
new CSharpCompilationOptions(outputKind: OutputKind.ConsoleApplication, platform: Platform.AnyCpu)
.WithOverflowChecks(true).WithOptimizationLevel(OptimizationLevel.Release);
// encode soucre code
string sourceCode = SourceText.From(code, Encoding.UTF8).ToString();
// CSharp options
var parsedSyntaxTree = Parse(sourceCode, "", CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp10));
// compilation
var compilation = CSharpCompilation.Create(FileName, new SyntaxTree[] { parsedSyntaxTree }, references: References, DefaultCompilationOptions);
var result = compilation.Emit(ExePath);
// return
if (result.Success)
return true;
else
return false;
}
private static SyntaxTree Parse(string text, string filename = "", CSharpParseOptions options = null)
{
var stringText = SourceText.From(text, Encoding.UTF8);
return SyntaxFactory.ParseSyntaxTree(stringText, options, filename);
}
}
}
Above code runs fine without error and exports the AppCodegenerated.exe into the project /bin folder.
Execution of this generated AppCodegenerated.exe shows following on the output console:
Unhandled exception: System.IO.FileNotFoundException:
The file or assembly "System.Runtime, Version = 6.0.0.0, Culture = neutral,
PublicKeyToken = b03f5f7f11d50a3a" or a dependency on it was not found.
The system can not find the stated file.
It is not possible to codegenerate directly a console application like the initial approach above. One possible solution is to generate first a dll (what I mentioned above in the example code is working fine), and from there include that .dll into a .exe, from where the functionality can run.
I have a simple console application and it has auto increment build version. When I build project, I’d like to append build version to end of the output exe file name with using post-build event. There is no macro for build version.
You will need a small utility to rename the target:
using System;
using System.Diagnostics;
using System.IO;
namespace RenameWithVersion
{
public class Program
{
public static void Main(String[] args)
{
if (args.Length != 1)
throw new Exception("There should only be one command line parameter - the file to be renamed.");
var oldFilename = Path.GetFullPath(args[0]);
if (!File.Exists(oldFilename))
throw new Exception($"File '{oldFilename}' does not exist.");
var oldFilenameWithoutExt = Path.ChangeExtension(oldFilename, null);
var fileVersion = FileVersionInfo.GetVersionInfo(oldFilename).FileVersion;
var ext = Path.GetExtension(oldFilename);
var newFilename = $"{oldFilenameWithoutExt}{fileVersion}{ext}";
File.Delete(newFilename);
File.Move(oldFilename, newFilename);
}
}
}
Compile it and put the executable somewhere on your path.
In the post-build event, call the utility like this:
RenameWithVersion "$(TargetPath)"
(Remember to put $(TargetPath) in double quotes in case the path contains spaces.)
I am trying to code a command line interface, the beginning has a scan for DLLs in a relevant folder (/dlls) but keeps throwing this error: Could not find a part of the path '/Users/danielshroff/Projects/Meh-Rewrite/Meh-Rewrite/bin/Debug/dlls)'. The path exists and I have copied and followed it exactly using finder goto folder
edit: i also tried using the full path but no luck
using System.IO;
namespace MehRewrite
{
class MainClass
{
public static void Main(string[] args)
{
Console.WriteLine("MEH System Version 1.0");
Console.WriteLine("Loading Core System");
Console.WriteLine("Declaring Startup Variables");
String dlls = null;
Console.WriteLine("Startup Variables Declared");
Console.WriteLine("Searching for dlls");
foreach (var file in Directory.GetFiles(#"./dlls)", "*.dll"))
{
dlls = dlls + file;
Console.WriteLine(file); }
int present = dlls.IndexOf("tinyMath.dll", StringComparison.Ordinal);
Console.Write("Enter Secure Mode? ");
variables.secure = Console.ReadLine();
The problem is you have a ')' at the end of the path string you are passing to Directory.GetFiles.
The line should be:
foreach (var file in Directory.GetFiles(#"./dlls", "*.dll"))
How can I programmatically get the current product version in C#?
My code:
VersionNumber = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
I am getting VersionNumber=1.0.0.0, but the current version is 1.0.0.12.
There are three versions: assembly, file, and product. To get the product version:
using System.Reflection;
using System.Diagnostics;
Assembly assembly = Assembly.GetExecutingAssembly();
FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(assembly.Location);
string version = fileVersionInfo.ProductVersion;
I got the answer to my question its Just give the reference to System.Deployment.Application and though it wont work in developement of the visual studio but it will work once the application is deployed.
//using System.Deployment.Application;
//using System.Reflection;
public string CurrentVersion
{
get
{
return ApplicationDeployment.IsNetworkDeployed
? ApplicationDeployment.CurrentDeployment.CurrentVersion.ToString()
: Assembly.GetExecutingAssembly().GetName().Version.ToString();
}
}
System.Reflection.Assembly.GetEntryAssembly().GetName().Version
Another approach to getting the product version (which is specified using the AssemblyInformationalVersionAttribute) is
private static string AssemblyProductVersion
{
get
{
object[] attributes = Assembly.GetExecutingAssembly()
.GetCustomAttributes(typeof(AssemblyInformationalVersionAttribute), false);
return attributes.Length == 0 ?
"" :
((AssemblyInformationalVersionAttribute)attributes[0]).InformationalVersion;
}
}
Try this:
var thisApp = Assembly.GetExecutingAssembly();
AssemblyName name = new AssemblyName(thisApp.FullName);
VersionNumber = "v. " + name.Version;
Also, see this Microsoft Doc on the AssemblyName.Version property.
In C# you need to use reflection and diagnostics
Assembly assembly = Assembly.GetExecutingAssembly();
FileVersionInfo fileVersionInfo = FileVersionInfo.GetVersionInfo(assembly.Location);
string version = fileVersionInfo.ProductVersion;
All these answers ask for the assembly with .GetExecutingAssembly().
If you have this code in a dll, it will return the dll version number.
Swap that call for GetCallingAssembly() to get the place in your code that wanted to know.
/// <summary>
/// Returns version like 2.1.15
/// </summary>
public static String ProductVersion
{
get
{
return new Version(FileVersionInfo.GetVersionInfo(Assembly.GetCallingAssembly().Location).ProductVersion).ToString();
}
}
var productVersion = FileVersionInfo.GetVersionInfo(typeof(SomeClassFromDesiredAssembly).Assembly.Location).ProductVersion;
I had the same issue as most of you. It would always show 1.0.0.0 unless you manually went in and updated assemblyInfo.cs to the version you wanted to display. I think we wanted to display the publish version-revision number under the project properties but that doesn't seem to be an option (from what I've read).
I'm not sure if back when these comments were made this existed, but now in the assemblyinfo.cs there is a way to do this automatically. I too was not content with having to manually update these with every publish.
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.*")]
That * auto-increments with each publish. It won't be the same as the publish number you see under the project properties, but it definitely increments and is definitely better than doing it by hand.
You then have a couple options to display it as mentioned above. I personally used this which I found on another site
Version version = Assembly.GetExecutingAssembly().GetName().Version;
lblRevision.Text = String.Format("{0}.{1}.{2}.{3}", version.Major, version.Minor, version.Build, version.Revision);
I have my own custom tool for Visual Studio 2008 SP1. It consists of 5 assemblies: 3 assemblies with code that are used heavily in my other projects, 1 assembly-wrapper above VS2008 SDK and an assembly with the tool.
If I'd debug my tool from visual studio, using "Run external program" option with command line "C:\Program Files (x86)\Microsoft Visual Studio 9.0\Common7\IDE\devenv.exe" and arguments "/ranu /rootsuffix Exp" all works perfectly.
After that I'm trying to deploy it to my working VS copy, not to experimental hive, doing: gacutil /i Asm1.dll for all my assemblies and doing RegAsm Asm1.dll only for assembly with custom tool. Neither of utils prints any error, all work as planned, even registry keys appear. But my tool doesn't work (error occurred "Cannot find custom tool 'TransportGeneratorTool' on this system") even after PC restart. What did I do wrong?
Wrapper looks like that:
[ComVisible(true)]
public abstract class CustomToolBase : IVsSingleFileGenerator, IObjectWithSite
{
#region IVsSingleFileGenerator Members
int IVsSingleFileGenerator.DefaultExtension(out string pbstrDefaultExtension)
{
pbstrDefaultExtension = ".cs";
return 0;
}
int IVsSingleFileGenerator.Generate(string wszInputFilePath, string bstrInputFileContents, string wszDefaultNamespace, IntPtr[] rgbOutputFileContents, out uint pcbOutput, IVsGeneratorProgress pGenerateProgress)
{
GenerationEventArgs gea = new GenerationEventArgs(
bstrInputFileContents,
wszInputFilePath,
wszDefaultNamespace,
new ServiceProvider(Site as Microsoft.VisualStudio.OLE.Interop.IServiceProvider)
.GetService(typeof(ProjectItem)) as ProjectItem,
new GenerationProgressFacade(pGenerateProgress)
);
if (OnGenerateCode != null)
{
OnGenerateCode(this, gea);
}
byte[] bytes = gea.GetOutputCodeBytes();
int outputLength = bytes.Length;
rgbOutputFileContents[0] = Marshal.AllocCoTaskMem(outputLength);
Marshal.Copy(bytes, 0, rgbOutputFileContents[0], outputLength);
pcbOutput = (uint)outputLength;
return VSConstants.S_OK;
}
#endregion
#region IObjectWithSite Members
void IObjectWithSite.GetSite(ref Guid riid, out IntPtr ppvSite)
{
IntPtr pUnk = Marshal.GetIUnknownForObject(Site);
IntPtr intPointer = IntPtr.Zero;
Marshal.QueryInterface(pUnk, ref riid, out intPointer);
ppvSite = intPointer;
}
void IObjectWithSite.SetSite(object pUnkSite)
{
Site = pUnkSite;
}
#endregion
#region Public Members
public object Site { get; private set; }
public event EventHandler<GenerationEventArgs> OnGenerateCode;
[ComRegisterFunction]
public static void Register(Type type)
{
using (var parent = Registry.LocalMachine.OpenSubKey(#"Software\Microsoft\VisualStudio\9.0", true))
foreach (CustomToolRegistrationAttribute ourData in type.GetCustomAttributes(typeof(CustomToolRegistrationAttribute), false))
ourData.Register(x => parent.CreateSubKey(x), (x, name, value) => x.SetValue(name, value));
}
[ComUnregisterFunction]
public static void Unregister(Type type)
{
using (var parent = Registry.LocalMachine.OpenSubKey(#"Software\Microsoft\VisualStudio\9.0", true))
foreach (CustomToolRegistrationAttribute ourData in type.GetCustomAttributes(typeof(CustomToolRegistrationAttribute), false))
ourData.Unregister(x => parent.DeleteSubKey(x, false));
}
#endregion
}
My tool code:
[ComVisible(true)]
[Guid("55A6C192-D29F-4e22-84DA-DBAF314ED5C3")]
[CustomToolRegistration(ToolName, typeof(TransportGeneratorTool))]
[ProvideObject(typeof(TransportGeneratorTool))]
public class TransportGeneratorTool : CustomToolBase
{
private const string ToolName = "TransportGeneratorTool";
public TransportGeneratorTool()
{
OnGenerateCode += GenerateCode;
}
private static void GenerateCode(object s, GenerationEventArgs e)
{
try
{
var serializer = new XmlSerializer(typeof (Parser.System));
using (var reader = new StringReader(e.InputText))
using (var writer = new StringWriter(e.OutputCode))
{
Generator.System = (Parser.System) serializer.Deserialize(reader);
Generator.System.Namespace = e.Namespace;
Generator.GenerateSource(writer);
}
}
catch (Exception ex)
{
e.Progress.GenerateError(ex.ToString());
}
}
}
Resulting registry keys:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Generators]
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Generators\{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}]
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\Generators\{FAE04EC1-301F-11D3-BF4B-00C04F79EFBC}\TransportGeneratorTool]
#="TransportGeneratorTool"
"CLSID"="{55a6c192-d29f-4e22-84da-dbaf314ed5c3}"
"GeneratesDesignTimeSource"=dword:00000001
"GeneratesSharedDesignTimeSource"=dword:00000001
Here is the code of my custom attribute (it is in wrapper assembly):
[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class CustomToolRegistrationAttribute : RegistrationAttribute
{
public CustomToolRegistrationAttribute(string name, Type customToolType)
{
Name = name;
CustomToolType = customToolType;
}
/// <summary>
/// The type that implements the custom tool. This starts
/// as MyCustomTool by default in the template.
/// </summary>
public Type CustomToolType { get; set; }
public string Name { get; set; }
#region RegistrationAttribute abstract member implementations
public override void Register(RegistrationContext context)
{
Register(x => context.CreateKey(x), (x, key, value) => x.SetValue(key, value));
}
public void Register<T>(Func<string, T> keyCreator, Action<T, string, object> valueCreator)
{
var keyName = CreateKeyName(Name);
var key = keyCreator(keyName);
valueCreator(key, string.Empty, Name);
valueCreator(key, "CLSID", CustomToolType.GUID.ToString("B"));
valueCreator(key, "GeneratesDesignTimeSource", 1);
valueCreator(key, "GeneratesSharedDesignTimeSource", 1);
var disposable = key as IDisposable;
if (disposable != null)
disposable.Dispose();
}
private static string CreateKeyName(string name)
{
return string.Format(#"Generators\{0}\{1}", vsContextGuids.vsContextGuidVCSProject, name);
}
public override void Unregister(RegistrationContext context)
{
Unregister(context.RemoveKey);
}
public void Unregister(Action<string> keyRemover)
{
keyRemover(CreateKeyName(Name));
}
#endregion
}
My solution is to make a setup project. I get the registry settings from the pkgdef file by adding the following to the csproj file of the package:
<Target Name="GeneratePackageRegistryFiles">
<Exec Command=""$(VSSDK90Install)VisualStudioIntegration\Tools\Bin\RegPkg.exe" /root:Software\Microsoft\VisualStudio\9.0 /codebase "$(TargetPath)" /regfile:"$(OutDir)$(TargetName).reg"" />
</Target>
<PropertyGroup>
<BuildDependsOn>$(BuildDependsOn);GeneratePackageRegistryFiles;</BuildDependsOn>
</PropertyGroup>
When building look in the output directory you should find a .reg file which you can import in the setup project.
Obviously you can run the regpkg.exe from the command-line if modifying the project is not an option.
This is what I ended up with last time when I struggled to get my custom tool registered.
I hope this instruction is detailed enough and covers everything so you won't spend much time fighting it. The following MSDN article was used as a starting point. http://msdn.microsoft.com/en-US/library/bb166527(v=vs.80).aspx Unfortunately you cannot use it alone. What you really need to do is:
Make sure the assembly is signed. Why? Because otherwise you won't be able to put it into GAC at step 6 below.
To sign your assembly follow these steps:
1.1. Go to the Properties screen of the project.
1.2. Once there go to the Signing tab.
1.3. Once there check the Sign the assembly checkbox.
Make sure you know the version number of your assembly. You will need this number to specify the ASSEMBLY_VERSION parameter later.
In order to get this number open the AssemblyInfo.cs file in the Properties folder of your project and look for the line starting with: [assembly: AssemblyVersion(
Make sure you know the GUID of the generator class. You will need it to specify the GENERATOR_GUID parameter later.
In order to get this GUID open the file with the generator class and look for the Guid class-attribute that decorates this class, something like: [Guid("17799E85-421B-4684-B59E-650E34ECC718")]
Build the project
Get the public token key of the assembly. In order to do that you will have to run the following command:
sn.exe -T ASSEMBLY_FILE
You will need this information later when for PUBLIC_TOKEN_KEY.
The sn.exe file can be found in C:\Program Files\Microsoft SDKs\Windows\v8.0A\bin\sn.exe
Pay attention to the version number of the framework (v8.0A) in the filepath above. It needs to be consistent with the version of the framework used to compile the project.
Put the assembly to the GAC using the following command:
gacutil.exe /i ASSEMBLY_FILE /f
Getting registered in GAC requires administrative permissions.
The gacutil.exe file can be found in C:\Program Files\Microsoft SDKs\Windows\v8.0A\bin\NETFX 4.0 Tools\gacutil.exe
Pay attention to the version number of the framework (v8.0A) in the filepath above. It needs to be consistent with the version of the framework used to compile the project.
Make the following changes to the .REG (see below) file. PLEASE NOTE: that both GENERATOR_GUID and PROJECT_TYPE_GUID need to be supplied WITH curly braces: {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}
7.1. Fix version number of Visual Studio is used (for example: 10.0 or 9.0): VS_VERSION
7.2. Fix the GUID of the generator: GENERATOR_GUID
7.3. Fix the namespace of the assembly: NAMESPACE_NAME
7.4. Fix the generator class name: GENERATOR_TYPE_NAME
7.5. In order to register the generator the Visual Studio needs to know to which project types this generator can be applied to. So you need to get GUID's of proper project types (C#, VB.NET, etc.).
To figure out the GUID's of the project types you need to open a visual studio project file (*.csproj) in a text editor and look for GUID's in the ProjectTypeGuids XML element.
For each of these GUIDs repeat the block of last 3 entries in the .REG file replacing the PROJECT_TYPE_GUID with the a GUID just found.
7.6. Fix the extension of the file associated with the custom tool: FILE_EXTENSTION
Run the .REG file. You may need to have administrative permissions for doing this.
.REG file:
Windows Registry Editor Version 5.00
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\VS_VERSION\CLSID\GENERATOR_GUID]
#="COM+ class: NAMESPACE_NAME.GENERATOR_TYPE_NAME"
"InprocServer32"="C:\\WINDOWS\\system32\\mscoree.dll"
"ThreadingModel"="Both"
"Class"="NAMESPACE_NAME.GENERATOR_TYPE_NAME"
"Assembly"="NAMESPACE_NAME, Version=ASSEMBLY_VERSION, Culture=Neutral, PublicKeyToken=PUBLIC_TOKEN_KEY"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\VS_VERSION\Generators]
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\VS_VERSION\Generators\PROJECT_TYPE_GUID]
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\VS_VERSION\Generators\PROJECT_TYPE_GUID\\.FILE_EXTENSTION]
#="GENERATOR_TYPE_NAME"
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\VS_VERSION\Generators\PROJECT_TYPE_GUID\GENERATOR_TYPE_NAME]
#="Code generator for whatever you like"
"CLSID"="GENERATOR_GUID"
"GeneratesDesignTimeSource"=dword:00000001
PS.
Sorry for not being able to make placehoders in the REG file distinct, unfortunately the text editor that StackOverflow uses cannot distinguish its markup elements from the content.