ReSharper 9 Adding menu item action not working - c#

Trying to update my resharper extension to work for 9.0, before I was just moving the dll into the plugins directory but now I need to figure out how to get nuget to work... I've been able to package the files, dll gets included in the nupkg but I think I have some namespace\id something something issues(not very familiar with .net) and it doesn't seem as if my actions.xml is even being read by resharper when I import the nuget package. The menu item isn't being added. Anwyays if anyone can give me any sort of advice on how to debug a nuget package or what might be going wrong would really really appreciate as I've been stuck on this for a few days now.
Actions.xml
<?xml version="1.0" encoding="utf-8" ?>
<actions>
<action id="yuval" text="L10N"></action>
<insert group-id="ReSharper" position="last">
<action-ref id="yuval" text="About Localization Helper"/>
</insert>
</actions>
AboutAction.cs
namespace JetBrains.Resharper.L10N
{
[Action(Id)]
public class AboutAction : IExecutableAction
{
public const string Id = "yuval";
public bool Update(IDataContext context, ActionPresentation presentation, DelegateUpdate nextUpdate)
{
return true;
}
public void Execute(IDataContext context, DelegateExecute nextExecute)
{
MessageBox.ShowMessageBox(
"Localization Helper\nYuval\n\nHelps Localize",
"About Localization Helper",
MbButton.MB_OK,
MbIcon.MB_ICONASTERISK);
}
}
}
nuget spec
<?xml version="1.0"?>
<package >
<metadata>
<id>JetBrains.Resharper.L10N</id>
<version>1.0.0.7</version>
<title>L10N</title>
<authors>Yuval</authors>
<owners>UW</owners>
<licenseUrl>https://myurl.com</licenseUrl>
<projectUrl>https://myurl.com</projectUrl>
<iconUrl>https://myurl.com/logo.png</iconUrl>
<requireLicenseAcceptance>true</requireLicenseAcceptance>
<description>Tool to help localize</description>
<releaseNotes>Summary of changes made in this release of the package.</releaseNotes>
<copyright>Copyright 2015</copyright>
<tags></tags>
<dependencies>
<dependency id="Wave" version="[1.0]" />
</dependencies>
</metadata>
<files>
<file src="..\bin\Debug\JetBrains.Resharper.L10N.dll"
target="dotFiles\"/>
</files>
</package>

The way actions are registered has changed in ReSharper 9. It's no longer done with actions.xml, but with interfaces on your action class. For example, to add an action to the ReSharper → Tools menu, you would do:
[Action(ActionId, Id = 1)]
public class AboutAction : IExecutableAction, IInsertLast<ToolsMenu>
{
public const string ActionId = "yuval";
// …
}
You also need to specify a unique value for Id. As of 9.1, this needs to be unique within your own extension (9.0 required it to be unique across the whole installation, including ReSharper itself and any other extensions).
Whenever you change the attributes or interfaces of an action, the extension needs to be reinstalled via nupkg (the actions are statically registered with Visual Studio, in the same way as a standard VS extension), but if just the implementation has changed, you can copy the dlls to the install folder, either manually, or via a small change to the .csproj.
You also need to make sure you've defined a ZoneMarker class. This declares that your action belongs to a zone, which is used to enable/disable functionality based on installed features and the current host (e.g. so Visual Studio specific extensions only work in VS and don't get loaded into dotPeek, etc.). You can find out more about Zones in the devguide, with this page providing useful info for defining a zone marker.
This thread should help, too.
Also, it's probably a good idea to name you dll and nupkg something other than JetBrains.ReSharper.(Whatever) to prevent any potential clashes with official dlls, and to prevent confusion as to where the dll comes from. The first part of the name is supposed to be your company's name (or personal name).

Related

How to disable predeployment and postdeployment scripts in DacServices.Deploy()

We have some automated dacpac deployment code which correctly handles both a CreateNewDatabase and straightforward Update database scenarios in C# using Microsoft.SqlServer.Dac
Now in the CreateNewDatabase case we want to be able to run the DacServices.Deploy() with both the Pre and Post Deployment scripts disabled. I.e. they should not be executed in this scenario.
I have tried to find a suitable place in the DacDeployOptions and DacServices objects but cannot find anything that will do this.Ideally
Question 1: I would like something like DacDeployOptions.IgnorePreDeploymentScript = true Is there any means by which I could achieve this at runtime?
As an alternative, some time ago I remember seeing example code which showed how to traverse a dacpac and create a new dacpac in run time. I think this approach would allow me to simply create a new dacpac which I could pass to the Deploy and which would exclude the Pre and Post Deployment scripts. I don't like this solution but it would allow me to achieve what I need.
Question 2: Can anyone point me to some examples for this please?
My code:
var dacService = new DacServices(ConstDefaultConnectionString);
using (var dacPackage = DacPackage.Load(dacPacFilePath))
{
var deployOptions = new DacDeployOptions
{
CreateNewDatabase = true,
IncludeTransactionalScripts = false
};
dacService.Deploy(dacPackage, TestDatabaseName, true, deployOptions);
}
The question is related to: Create LocalDB for testing from Visual Studio SQL project
There are a number of approaches you can take for this, this is a bit of a brain dump (hey the clocks went back last night and I'm not even sure if the current time):
1) create an empty project that references your main project using a same database reference - when you deploy without the scripts deploy the empty one using IncludeCompositeObjects - pre/post deploy scripts are only run from the dacpac you deploy not from any referenced dacpacs but obviously the code and scheme are deployed. This describes it:
https://the.agilesql.club/blog/Ed-Elliott/2016-03-03/Post-Deploy-Scripts-In-Composite-Dacpac-not-deploying
2) use SQLCMD variables to wrap the data setups and pass in the value to the deploy.
3) make your scripts check for whether they should setup data like only insert if the table rowcount is zero
4) for reference data use merge scripts - I'm not clear if the point of this is for reference data or setting up test data
5) Use .net packaging api to remove the pre/post deploy scripts from the dacpac, this shows you how to write the scripts so you should be able to do a GetPart rather than WritePart:
https://github.com/GoEddie/Dir2Dac/blob/master/src/Dir2Dac/DacCreator.cs
On the whole I would guess that there is probably a simpler solution- if this is for testing then maybe make the data setup part of the test setup? If you are unit testing tSQLt helps you avoid all this by using FakeTable.
Hope it helps :)
Ed
Two things to try:
First, doing this type of thing is quite easy if you are using MSBuild since you can tailor a particular Configuration to include one or more pieces of the Project. In your .sqlproj file there is an <ItemGroup> section that should look similar to the following:
<ItemGroup>
<PreDeploy Include="Script.PreDeployment1.sql" />
<PostDeploy Include="Script.PostDeployment1.sql" />
</ItemGroup>
You can simply add a "Condition" that will determine if that ItemGroup is used or not. You can see these "Condition" attributes throughout the .sqlproj file (usually). So the result should look similar to:
<ItemGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PreDeploy Include="Script.PreDeployment1.sql" />
<PostDeploy Include="Script.PostDeployment1.sql" />
</ItemGroup>
Then you just flip between "Release" or "Debug" in the "Active Configuration" drop-down and the pre and post deploy scripts will be included or excluded accordingly.
The other thought was to somehow reset the pre and post deployment scripts. Since you are loading the DacPac into dacPackage, you will have access to the PreDeploymentScript and PostDeploymentScript properties. I am not able to test, but it might be possible to "erase" what is there (assuming that the streams already point to the stored scripts).
DACPACs are ZIP files. Use the functionality of the System.IO.Packaging namespace to remove pre- and post-deployment scripts from an existing package.
using System.IO.Packaging;
// [...]
using (var dacPac = Package.Open(dacPacFile))
{
var preDeploy = new Uri("/predeploy.sql", UriKind.Relative);
if (dacPac.PartExists(preDeploy))
{
dacPac.DeletePart(preDeploy);
}
var postDeploy = new Uri("/postdeploy.sql", UriKind.Relative);
if (dacPac.PartExists(postDeploy))
{
dacPac.DeletePart(postDeploy);
}
dacPac.Close();
}
The file is simply overwritten after Close, so consider copying it away first, in case you want the original unchanged.
(this is partly covered by the internet resource linked in item 5) in the accepted answer; however the code shown above is all you need)

c# autocad dllimport by autocad version

i have been using the the program from "Detecting entities under cursor while selection is running" By Philippe Leefsma located here1. it worked in ACAD2014 however now we are using ACAD2016. it will not work because of the DLLImport of acdb19.dll and autocad2016 needs acdb20.dll. Is there a way to make the program load eighter dll version? i tried using netload and assembly.loadfrom and neither worked.
Point Monitor callback
The DLL import is set at compile time so the best bet is to compile two versions for each release of AutoCAD.
Once the project is compiled, you could set up an autoloader by creating an application.bundle folder. this folder can be placed in c:\programdata\Autodesk\applicationPlugins. In the application.bundle folder, create a subfolder called Application and place the compiled .DLL file in there.
The loader is controlled with an XML file which should be named PackageContents.xml. Here's some example code for the xml file:-
<?xml version="1.0" encoding="utf-8"?>
<ApplicationPackage xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
Name="My AutoCAD App"
Description="Does something in AutoCAD"
Icon="./Application/MyIcon.ico"
Author="Paul Nelson">
<CompanyDetails Name="Paul Nelson"
Url="http://www.stackoverflow.com"
Email="myemail#email.com">
</CompanyDetails>
<Components>
<!-- define the min and max versions of AutoCA in the next line -->
<RuntimeRequirements OS="Win64" Platform="AutoCAD" SeriesMin="R19.0" SeriesMax="R22.0" />
<ComponentEntry
ModuleName=".\Application\MyApp.dll"
LoadOnAutoCADStartup="true"
LoadOnCommandInvocation="false"
AppDescription="This is assembly MyApp."
AppName="My AutoCAD App"
AppType=".NET">
<Commands GroupName="My Apps">
<Command Local="MYAPP" Global="MYAPP" />
</Commands>
</ComponentEntry>
</Components>
</ApplicationPackage>
One last tip - make sure the .dll filename does not contain spaces.

Checking Visual Studio projects for consistency

You have a large Visual Studio Solution with dozens of project files in it. How would you verify that all the projects follow certain rules in their property settings, and enforce these rules if a new project is added. For example check that all projects have:
TargetFrameworkVersion = "v4.5"
Platform = "AnyCPU"
WarningLevel = 4
TreatWarningsAsErrors = true
OutputPath = $(SolutionDir)bin
SignAssembly = true
AssemblyName = $(ProjectFolderName)
I know two methods myself that I will add in an answer below, but I was wondering how people go about doing this type of project test. I'm especially interested to learn about available solutions such as libraries or build tasks for this rather than having to have to invent something new or write it from scratch.
*.sln files are plain text and easily parsable, and *.*proj files are xml.
You can add a dummy project with a prebuild step that parses the sln to retrieve all of the project files, validate their settings, print a report, and fail the build if necessary.
Also, you should check this post to ensure the prebuild step is always executed. Essentially, you specify a blank output in the custom build step to force a rebuild.
The following list identifies the key file types that are automatically added to VSS when a solution is added to source control by using the Visual Studio .NET integrated development environment (IDE):
Solution files (.sln). The key items maintained within these files include a list of constituent projects, dependency information, build configuration details, and source control provider details.
Project files (.csproj or *.vbproj). The key items maintained within these files include assembly build settings, referenced assemblies (by name and path), and a file inventory.
Application configuration files. These are configuration files based on Extensible Markup Language (XML) used to control various aspects of your project's run time behavior.
Use a Single Solution Model Whenever Possible an
Also see : https://msdn.microsoft.com/en-us/library/ee817677.aspx,
https://msdn.microsoft.com/en-us/library/ee817675.aspx
AND For CONTINUOUS INTEGRATION :
there are many tools available like MSBuild, Jenkins, Apache's Continuum, Cruise Control (CC), and Hudson(plugin can be extended to c#)
This is what I have myself:
One way to do this is to create an MSBuild target with error conditions:
<Error Condition="'$(TreatWarningsAsErrors)'!='true'" Text="Invalid project setting" />
I like this approach because it is integrated with MSBuild and gives you early errors, however, you have to modify every project to import it in them or get all your team members to use a special command prompt with environment variables that will inject custom pre-build steps into your projects during the build, which is a pain.
The second approach I know is to use some library like VSUnitTest which provides an API to project properties that you can test against. VSUnitTest is currently not open source and unlisted from the NuGet service.
You could write some code to open the the solution as a text file to identify all of the csproj files referenced, in turn opening each of these as xml files, and then writing unit tests to ensure specific nodes of the project match what you expect.
It's a quick and dirty solution, but works for CI and gives you the flexibility to ignore nodes you don't care about. It actually sounds kinda useful. I have a solution with 35 projects I'd like to scan too.
Let's try something completely different: you could ensure that they are consistent by construction by generating them from a template or by using a build generation tool such as CMake. This might be simpler than attempting to make them consistent after the fact.
In our work we use a powershell script that checks project settings and modified them if they are incorrect. For example, we remove Debug configuration this way, disable C++ optimization and SSE2 support. We run it manually, but definitely it is possible to run it automatically, e.g. as pre\post build step.
Below the example:
`function Prepare-Solution {
param (
[string]$SolutionFolder
)
$files = gci -Recurse -Path $SolutionFolder -file *.vcxproj | select - ExpandProperty fullname
$files | %{
$file = $_
[xml]$xml = get-content $file
#Deleting Debug configurations...
$xml.Project.ItemGroup.ProjectConfiguration | ?{$_.Configuration -eq "Debug"} | %{$_.ParentNode.RemoveChild($_)} | Out-Null
$xml.SelectNodes("//*[contains(#Condition,'Debug')]") |%{$_.ParentNode.RemoveChild($_)} | Out-Null
if($xml.Project.ItemDefinitionGroup.ClCompile) {
$xml.Project.ItemDefinitionGroup.ClCompile | %{
#Disable SSE2
if (-not($_.EnableEnhancedInstructionSet)){
$_.AppendChild($xml.CreateElement("EnableEnhancedInstructionSet", $xml.DocumentElement.NamespaceURI)) | Out-Null
}
if($_.ParentNode.Condition.Contains("Win32")){
$_.EnableEnhancedInstructionSet = "StreamingSIMDExtensions"
}
elseif($_.ParentNode.Condition.Contains("x64")) {
$_.EnableEnhancedInstructionSet = "NotSet"
} else {
Write-Host "Neither x86 nor x64 config. Very strange!!"
}
#Disable Optimization
if (-not($_.Optimization)){
$_.AppendChild($xml.CreateElement("Optimization", $xml.DocumentElement.NamespaceURI)) | Out-Null
}
$_.Optimization = "Disabled"
}
}
$xml.Save($file);
} }`
A file is an assembly if and only if it is managed, and contains an assembly entry in its metadata. For more information on assemblies and metadata, see the topic Assembly Manifest.
How to manually determine if a file is an assembly
Start the Ildasm.exe (IL Disassembler).
Load the file you wish to test.
If ILDASM reports that the file is not a portable executable (PE) file, then it is not an assembly. For more information, see the topic How to: View Assembly Contents.
How to programmatically determine if a file is an assembly
Call the GetAssemblyName method, passing the full file path and name of the file you are testing.
If a BadImageFormatException exception is thrown, the file is not an assembly.
This example tests a DLL to see if it is an assembly.
class TestAssembly
{
static void Main()
{
try
{
System.Reflection.AssemblyName testAssembly = System.Reflection.AssemblyName.GetAssemblyName(#"C:\Windows\Microsoft.NET\Framework\v3.5\System.Net.dll");
System.Console.WriteLine("Yes, the file is an assembly.");
}
catch (System.IO.FileNotFoundException)
{
System.Console.WriteLine("The file cannot be found.");
}
catch (System.BadImageFormatException)
{
System.Console.WriteLine("The file is not an assembly.");
}
catch (System.IO.FileLoadException)
{
System.Console.WriteLine("The assembly has already been loaded.");
}
}
}
// Output (with .NET Framework 3.5 installed):
// Yes, the file is an assembly.
Framework is the highest installed version, SP is the service pack for that version.
RegistryKey installed_versions = Registry.LocalMachine.OpenSubKey(#"SOFTWARE\Microsoft\NET Framework Setup\NDP");
string[] version_names = installed_versions.GetSubKeyNames();
//version names start with 'v', eg, 'v3.5' which needs to be trimmed off before conversion
double Framework = Convert.ToDouble(version_names[version_names.Length - 1].Remove(0, 1), CultureInfo.InvariantCulture);
int SP = Convert.ToInt32(installed_versions.OpenSubKey(version_names[version_names.Length - 1]).GetValue("SP", 0));
For .Net 4.5
using System;
using Microsoft.Win32;
...
private static void Get45or451FromRegistry()
{
using (RegistryKey ndpKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey("SOFTWARE\\Microsoft\\NET Framework Setup\\NDP\\v4\\Full\\")) {
int releaseKey = Convert.ToInt32(ndpKey.GetValue("Release"));
if (true) {
Console.WriteLine("Version: " + CheckFor45DotVersion(releaseKey));
}
}
}
...
// Checking the version using >= will enable forward compatibility,
// however you should always compile your code on newer versions of
// the framework to ensure your app works the same.
private static string CheckFor45DotVersion(int releaseKey)
{
if (releaseKey >= 393273) {
return "4.6 RC or later";
}
if ((releaseKey >= 379893)) {
return "4.5.2 or later";
}
if ((releaseKey >= 378675)) {
return "4.5.1 or later";
}
if ((releaseKey >= 378389)) {
return "4.5 or later";
}
// This line should never execute. A non-null release key should mean
// that 4.5 or later is installed.
return "No 4.5 or later version detected";
}
For similar purposes we use custom MSBuild fragments with common properties that we want to share between the projects, like this (build.common.props file):
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<TargetFrameworkVersion>v2.0</TargetFrameworkVersion>
<PlatformToolset>v90</PlatformToolset>
<OutputPath>$(SolutionDir)..\bin\$(PlatformPath)\$(Configuration)\</OutputPath>
<!-- whatever you need here -->
</PropertyGroup>
</Project>
And then we just include this fragment to real VS projects we want to apply these properties to:
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<CommonProps>$(SolutionDir)..\Build\build.common.props</CommonProps>
</PropertyGroup>
<Import Project="$(CommonProps)" />
<!-- the rest of the project -->
</Project>
We handle a lot of things using this approach:
common properties, as you mentioned
static analysis (FxCop, StyleCop)
digital sign of assemblies
etc.
The only disadvantage that you need to include these MSBuild fragments into each project file, but once you do that, you have all the benefits of modular build system that is easy to manage and update.
You could go the search & replace Regex way with a handwritten C#, Script, powershell or similar. But it has the following problems:
Difficult to read (Read your pretty regex in three or more months)
Difficult to enhance(New regex for new search/replace/check feature)
Easy to break (a new release/format of ms build project or a not forecast tag may not work)
Harder to test (you must check that no unintended match occurs)
Difficult to maintain (because of the above)
and the following advantages:
Not doing any extra validation which (may) let it work on any kind of project (mono or visual).
Doesn't care about \r :)
The best could be to use the Microsoft.Build.Evaluation
and build a C# tool which does all your testing/checking/fix and so on.
I've done a command line tool that use a sourcefile list (used by Mono) and update sources of csproj and another which dumps on console the csproj content. It was easy to do, pretty straightforward and easy to test also.
However, it may fail (as I've experienced it) on projects modified by "non" Ms tool (like Mono Studio) or because of missing \r....
Anyway, you can always handle it with an exception catch and a good message.
Here a sample by using Microsoft.Build.dll (don't use Microsof.Build.Engine as it is obsolete):
using System;
using Microsoft.Build.Evaluation;
internal class Program
{
private static void Main(string[] args)
{
var project = new Project("PathToYourProject.csproj");
Console.WriteLine(project.GetProperty("TargetFrameworkVersion", true, string.Empty));
Console.WriteLine(project.GetProperty("Platform", true, string.Empty));
Console.WriteLine(project.GetProperty("WarningLevel", true, string.Empty));
Console.WriteLine(project.GetProperty("TreatWarningsAsErrors", true, "false"));
Console.WriteLine(project.GetProperty("OutputPath", false, string.Empty));
Console.WriteLine(project.GetProperty("SignAssembly", true, "false"));
Console.WriteLine(project.GetProperty("AssemblyName", false, string.Empty));
Console.ReadLine();
}
}
public static class ProjectExtensions
{
public static string GetProperty(this Project project, string propertyName, bool afterEvaluation, string defaultValue)
{
var property = project.GetProperty(propertyName);
if (property != null)
{
if (afterEvaluation)
return property.EvaluatedValue;
return property.UnevaluatedValue;
}
return defaultValue;
}
}
I also faced this issue and created a small solution that creates a csv file with details to identifies the inconsistences. You can look at it in this url
https://github.com/gdlmanuv/VSProjectConsistencyChecker

Making WPF navigation dynamic - using an XML file?

I'm working on the navigation part of a desktop application, and have a bit of a problem. The request is that the navigation should be dynamic, so that you can, for instance, switch orders of the views without having to recompile (and ideally also adding a view without recompiling).
Currently I'm using an XML to define which windows to display, which header it should have and how the footer should look like. Here's how the XML looks now:
<?xml version="1.0" encoding="utf-8" ?>
<ArrayOfViewState xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<ViewState ViewName="WelcomeView" Header="Welcome to the Application" FooterButton1="Quit" FooterButton2="Back" FooterButton3="Next" />
<ViewState ViewName="LicenseView" Header="Licence Agreement" FooterButton1="Quit" FooterButton2="Back" FooterButton3="Next" />
<ViewState ViewName="LoginView" Header="Log in" FooterButton1="Quit" FooterButton2="Back" FooterButton3="Next" />
<ViewState ViewName="InstallationView" Header="Installing..." FooterButton1="Cancel" FooterButton2="None" FooterButton3="Next" />
<ViewState ViewName="UpdateView" Header="Updating..." FooterButton1="Cancel" FooterButton2="None" FooterButton3="Next" />
<ViewState ViewName="FinishedView" Header="Finished!" FooterButton1="None" FooterButton2="None" FooterButton3="Finish" />
</ArrayOfViewState>
And when I match this in the code it looks like this (viewState.View is of type UserControl):
...
case "WelcomeView":
viewState.View = new WelcomeView();
...
As you can see I use the ViewName property in the XML to match and create my views (they also have a ViewModel, but that is taken care through XAML and the MVVM Light ViewModel Locator).
This solution technically allows the navigation to be changed somewhat without recompiling (for example you can shuffle the order any way you like), but there must be a better way to handle this than matching a string property. I've tried looking into serializing the User Control so that I could just load it along with the other properties, but so far I've had no luck. Any ideas on how to go about and improve/change this?
Thanks!
Indeed, there is a better way. :-)
Have a look at the Microsoft Extensibility Framework (MEF). It works very well with WPF and MVVM.
It allows you to easiliy compose application parts on-the-fly during runtine.
In short, you mark a class you want to be loaded somewhere else with an Attribute [Export]:
[Export(typeof(ViewContainer))]
public class ViewContainer
{
public string ViewName = "WelcomeView";
public string Header="Welcome to the Application"
// Do more stuff here
}
And in the class or assembly that should use the exported class you can load it with the [Import] Attribute:
public class ClassInOtherAssembly
{
[ImportMany]
internal ObservableCollection<ViewContainer> m_MyViews { get; set; }
// Do other stuff here
}
Depending on the architecture that you implement it might even be sufficient to use a 1-liner (!) to assemble all imported classes (this uses a different approach than the following referenced tutorial):
CompositionInitializer.SatisfyImports(this);
And that's it!
(Do not take these examples as is, I just wanted to get to the point. I recommend using
Properties instead of the strings and interfaces instead of the class export. You'll find plenty of more elegant snippets on the net. :-) )
Here you can find a tutorial to get you started:
Getting started with Managed Extensibility Framework (MEF)
Why not use reflection?
You could use Activator.CreateInstance and pass in your View's string:
string asmName = "YourAssembly";
string typeName = "YourViewName";
object obj = Activator.CreateInstance(asmName, typeName).Unwrap() as UserControl;

Automatically update version number

I would like the version property of my application to be incremented for each build but I'm not sure on how to enable this functionality in Visual Studio (2005/2008). I have tried to specify the AssemblyVersion as 1.0.* but it doesn't get me exactly what I want.
I'm also using a settings file and in earlier attempts when the assembly version changed my settings got reset to the default since the application looked for the settings file in another directory.
I would like to be able to display a version number in the form of 1.1.38 so when a user finds a problem I can log the version they are using as well as tell them to upgrade if they have an old release.
A short explanation of how the versioning works would also be appreciated. When does the build and revision number get incremented?
With the "Built in" stuff, you can't, as using 1.0.* or 1.0.0.* will replace the revision and build numbers with a coded date/timestamp, which is usually also a good way.
For more info, see the Assembly Linker Documentation in the /v tag.
As for automatically incrementing numbers, use the AssemblyInfo Task:
AssemblyInfo Task
This can be configured to automatically increment the build number.
There are 2 Gotchas:
Each of the 4 numbers in the Version string is limited to 65535. This is a Windows Limitation and unlikely to get fixed.
Why are build numbers limited to 65535?
Using with with Subversion requires a small change:
Using MSBuild to generate assembly version info at build time (including SubVersion fix)
Retrieving the Version number is then quite easy:
Version v = Assembly.GetExecutingAssembly().GetName().Version;
string About = string.Format(CultureInfo.InvariantCulture, #"YourApp Version {0}.{1}.{2} (r{3})", v.Major, v.Minor, v.Build, v.Revision);
And, to clarify: In .net or at least in C#, the build is actually the THIRD number, not the fourth one as some people (for example Delphi Developers who are used to Major.Minor.Release.Build) might expect.
In .net, it's Major.Minor.Build.Revision.
VS.NET defaults the Assembly version to 1.0.* and uses the following logic when auto-incrementing: it sets the build part to the number of days since January 1st, 2000, and sets the revision part to the number of seconds since midnight, local time, divided by two. See this MSDN article.
Assembly version is located in an assemblyinfo.vb or assemblyinfo.cs file. From the file:
' Version information for an assembly consists of the following four values:
'
' Major Version
' Minor Version
' Build Number
' Revision
'
' 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.0.0")>
<Assembly: AssemblyFileVersion("1.0.0.0")>
I have found that it works well to simply display the date of the last build using the following wherever a product version is needed:
System.IO.File.GetLastWriteTime(System.Reflection.Assembly.GetExecutingAssembly().Location).ToString("yyyy.MM.dd.HH.mm.ss")
Rather than attempting to get the version from something like the following:
System.Reflection.Assembly assembly = System.Reflection.Assembly.GetExecutingAssembly();
object[] attributes = assembly.GetCustomAttributes(typeof(System.Reflection.AssemblyFileVersionAttribute), false);
object attribute = null;
if (attributes.Length > 0)
{
attribute = attributes[0] as System.Reflection.AssemblyFileVersionAttribute;
}
[Visual Studio 2017, .csproj properties]
To automatically update your PackageVersion/Version/AssemblyVersion property (or any other property), first, create a new Microsoft.Build.Utilities.Task class that will get your current build number and send back the updated number (I recommend to create a separate project just for that class).
I manually update the major.minor numbers, but let MSBuild to automatically update the build number (1.1.1, 1.1.2, 1.1.3, etc. :)
using Microsoft.Build.Framework;
using System;
using System.Collections.Generic;
using System.Text;
public class RefreshVersion : Microsoft.Build.Utilities.Task
{
[Output]
public string NewVersionString { get; set; }
public string CurrentVersionString { get; set; }
public override bool Execute()
{
Version currentVersion = new Version(CurrentVersionString ?? "1.0.0");
DateTime d = DateTime.Now;
NewVersionString = new Version(currentVersion.Major,
currentVersion.Minor, currentVersion.Build+1).ToString();
return true;
}
}
Then call your recently created Task on MSBuild process adding the next code on your .csproj file:
<Project Sdk="Microsoft.NET.Sdk">
...
<UsingTask TaskName="RefreshVersion" AssemblyFile="$(MSBuildThisFileFullPath)\..\..\<dll path>\BuildTasks.dll" />
<Target Name="RefreshVersionBuildTask" BeforeTargets="Pack" Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<RefreshVersion CurrentVersionString="$(PackageVersion)">
<Output TaskParameter="NewVersionString" PropertyName="NewVersionString" />
</RefreshVersion>
<Message Text="Updating package version number to $(NewVersionString)..." Importance="high" />
<XmlPoke XmlInputPath="$(MSBuildProjectDirectory)\mustache.website.sdk.dotNET.csproj" Query="/Project/PropertyGroup/PackageVersion" Value="$(NewVersionString)" />
</Target>
...
<PropertyGroup>
..
<PackageVersion>1.1.4</PackageVersion>
..
When picking Visual Studio Pack project option (just change to BeforeTargets="Build" for executing the task before Build) the RefreshVersion code will be triggered to calculate the new version number, and XmlPoke task will update your .csproj property accordingly (yes, it will modify the file).
When working with NuGet libraries, I also send the package to NuGet repository by just adding the next build task to the previous example.
<Message Text="Uploading package to NuGet..." Importance="high" />
<Exec WorkingDirectory="$(MSBuildProjectDirectory)\bin\release" Command="c:\nuget\nuget push *.nupkg -Source https://www.nuget.org/api/v2/package" IgnoreExitCode="true" />
c:\nuget\nuget is where I have the NuGet client (remember to save your NuGet API key by calling nuget SetApiKey <my-api-key> or to include the key on the NuGet push call).
Just in case it helps someone ^_^.
What source control system are you using?
Almost all of them have some form of $ Id $ tag that gets expanded when the file is checked in.
I usually use some form of hackery to display this as the version number.
The other alternative is use to use the date as the build number: 080803-1448
Some time ago I wrote a quick and dirty exe that would update the version #'s in an assemblyinfo.{cs/vb} - I also have used rxfind.exe (a simple and powerful regex-based search replace tool) to do the update from a command line as part of the build process. A couple of other helpfule hints:
separate the assemblyinfo into product parts (company name, version, etc.) and assembly specific parts (assembly name etc.). See here
Also - i use subversion, so I found it helpful to set the build number to subversion revision number thereby making it really easy to always get back to the codebase that generated the assembly (e.g. 1.4.100.1502 was built from revision 1502).
If you want an auto incrementing number that updates each time a compilation is done, you can use VersionUpdater from a pre-build event. Your pre-build event can check the build configuration if you prefer so that the version number will only increment for a Release build (for example).
Here is a handcranked alternative option: This is a quick-and-dirty PowerShell snippet I wrote that gets called from a pre-build step on our Jenkins build system.
It sets the last digit of the AssemblyVersion and AssemblyFileVersion to the value of the BUILD_NUMBER environment variable which is automatically set by the build system.
if (Test-Path env:BUILD_NUMBER) {
Write-Host "Updating AssemblyVersion to $env:BUILD_NUMBER"
# Get the AssemblyInfo.cs
$assemblyInfo = Get-Content -Path .\MyShinyApplication\Properties\AssemblyInfo.cs
# Replace last digit of AssemblyVersion
$assemblyInfo = $assemblyInfo -replace
"^\[assembly: AssemblyVersion\(`"([0-9]+)\.([0-9]+)\.([0-9]+)\.[0-9]+`"\)]",
('[assembly: AssemblyVersion("$1.$2.$3.' + $env:BUILD_NUMBER + '")]')
Write-Host ($assemblyInfo -match '^\[assembly: AssemblyVersion')
# Replace last digit of AssemblyFileVersion
$assemblyInfo = $assemblyInfo -replace
"^\[assembly: AssemblyFileVersion\(`"([0-9]+)\.([0-9]+)\.([0-9]+)\.[0-9]+`"\)]",
('[assembly: AssemblyFileVersion("$1.$2.$3.' + $env:BUILD_NUMBER + '")]')
Write-Host ($assemblyInfo -match '^\[assembly: AssemblyFileVersion')
$assemblyInfo | Set-Content -Path .\MyShinyApplication\Properties\AssemblyInfo.cs -Encoding UTF8
} else {
Write-Warning "BUILD_NUMBER is not set."
}

Categories

Resources