How do I get a branches parent from a path in TFS - c#

Lets say the following is my TFS structure:
Branches (Folder)
Module1 (Folder)
Branch1 (Branch with parent Dev)
Branch2 (Branch with parent Branch1)
Branch3 (Branch with parent Branch1)
Dev (Branch)
In code, I have access to my local workspace, as well as a VersionControlServer object.
I want a method such as string GetParentPath(string path)
that would act like the following:
GetParentPath("$/Branches/Module1/Branch1"); // $/Dev
GetParentPath("$/Branches/Module1/Branch2"); // $/Branches/Module1/Branch1
GetParentPath("$/Branches/Module1/Branch3"); // $/Branches/Module1/Branch1
GetParentPath("$/Dev"); // throws an exception since there is no parent
I currently have the following, thought it worked, but it doesn't (and I didn't honestly expect it to work either)
private string GetParentPath(string path)
{
return versionControlServer.QueryMergeRelationships(path)?.LastOrDefault()?.Item;
}

You can get all branch hierarchies (Parent/Child) using below code: (Install the Nuget package Microsoft.TeamFoundationServer.ExtendedClient)
using System;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
namespace DisplayAllBranches
{
class Program
{
static void Main(string[] args)
{
string serverName = #"http://ictfs2015:8080/tfs/DefaultCollection";
//1.Construct the server object
TfsTeamProjectCollection tfs = new TfsTeamProjectCollection(new Uri(serverName));
VersionControlServer vcs = tfs.GetService<VersionControlServer>();
//2.Query all root branches
BranchObject[] bos = vcs.QueryRootBranchObjects(RecursionType.OneLevel);
//3.Display all the root branches
Array.ForEach(bos, (bo) => DisplayAllBranches(bo, vcs));
Console.ReadKey();
}
private static void DisplayAllBranches(BranchObject bo, VersionControlServer vcs)
{
//0.Prepare display indentation
for (int tabcounter = 0; tabcounter < recursionlevel; tabcounter++)
Console.Write("\t");
//1.Display the current branch
Console.WriteLine(string.Format("{0}", bo.Properties.RootItem.Item));
//2.Query all child branches (one level deep)
BranchObject[] childBos = vcs.QueryBranchObjects(bo.Properties.RootItem, RecursionType.OneLevel);
//3.Display all children recursively
recursionlevel++;
foreach (BranchObject child in childBos)
{
if (child.Properties.RootItem.Item == bo.Properties.RootItem.Item)
continue;
DisplayAllBranches(child, vcs);
}
recursionlevel--;
}
private static int recursionlevel = 0;
}
}

Figured it out (Thanks to Andy Li-MSFT for poking my brain with the BranchObject class):
string GetParentPath(string path)
{
BranchObject branchObject = versionControlServer.QueryBranchObjects(new ItemIdentifier(path), RecursionType.None).Single();
if (branchObject.Properties.ParentBranch != null)
return branchObject.Properties.ParentBranch.Item;
else
throw new Exception($"Branch '{path}' does not have a parent");
}
In addition, if you want to get the parent branch of a file/folder located within that branch, you could use the following code to get that functionality:
private string GetParentPath(string path)
{
string modifyingPath = path;
BranchObject branchObject = versionControlServer.QueryBranchObjects(new ItemIdentifier(modifyingPath), RecursionType.None).FirstOrDefault();
while (branchObject == null && !string.IsNullOrWhiteSpace(modifyingPath))
{
modifyingPath = modifyingPath.Substring(0, modifyingPath.LastIndexOf("/"));
branchObject = versionControlServer.QueryBranchObjects(new ItemIdentifier(modifyingPath), RecursionType.None).FirstOrDefault();
}
string root = branchObject?.Properties?.ParentBranch?.Item;
return root == null ? null : $"{root}{path.Replace(modifyingPath, "")}";
}

Related

C#: Get Product Version using the Product Name using the WindowsInstaller and not WMI

I need some help in retrieving the ProductVersion of a program in C#.
I did find a way to retrieve it using WMI but it is really slow.
Previously I had the same issue when using vbscript and the solution was to use the
Windows Installer (which is really fast)
Set objInstaller = CreateObject("WindowsInstaller.Installer")
Set colProducts = objInstaller.Products
For Each objProduct In colProducts
strProductName = objInstaller.ProductInfo(objProduct, "ProductName")
If strProductName = "MyProductName1" Then
var1 = objInstaller.ProductInfo(objProduct, "VersionString")
End If
If strProductName = "MyProductName2" Then
var2 = objInstaller.ProductInfo(objProduct, "VersionString")
End If
Next
The question is how do I do the same in C#? I'm currently using Avalonia as UI.
I also tried all the other methods in the search (Wix DTF, COM with P/Invoke), so please don't redirect to google....
Edit: I do not have a path for the msi or exe. I need to search in the registry or installer, that is why getfileversion or dtf is not working.
Thanks!
Edit1:
Here is my code after reading all the comments and resources.
public class Program
{
public static string getInstalledVersion(string findByName)
{
string info = null;
string registryKey = #"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryKey key32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32);
RegistryKey key = key32.OpenSubKey(registryKey);
if (key != null)
{
foreach (RegistryKey subkey in key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName)))
{
string displayName = subkey.GetValue("DisplayName") as string;
if (displayName != null && displayName.Contains(findByName))
info = subkey.GetValue("Version").ToString();
break;
else
info = "Not found";
}
key.Close();
}
return info;
}
}
Get the result in a variable:
public string MyVar => Program.getInstalledVersion("Microsoft Edge");
Edit2:
So the edit with the break in my latest version somewhat works. It finds Microsoft Edge but still doesn't find other programs (i will try other paths as fallbacks)
# Stein Asmul i did try your version but doesn't find anything, maybe Im doing something wrong?
public class ProgramX
{
public static string getPVersion(string findByName)
{
string info = null;
foreach (var p in ProductInstallation.AllProducts)
{
if (p.ProductName == findByName)
{
info = p.ProductVersion.ToString();
break;
}
else
info = "Not Found";
}
return info;
}
}
Call:
public string MyProgram => ProgramX.getPVersion("Microsoft Edge");
Edit3: Great success!
I managed to get it to work. The problem was subkey getvalue is "DisplayVersion" not "Version". I only need 64 for my programs.
public class ProgramI
{
public static string getInstalledVersion(string findByName)
{
string info = null;
string registryKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
RegistryKey key64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
RegistryKey key = key64.OpenSubKey(registryKey);
if (key != null)
{
foreach (RegistryKey subkey in key.GetSubKeyNames().Select(keyName => key.OpenSubKey(keyName)))
{
string displayName = subkey.GetValue("DisplayName") as string;
if (displayName != null && displayName.Contains(findByName))
{
info = subkey.GetValue("DisplayVersion").ToString();
break;
}
else
info = "Not found";
}
key.Close();
}
return info;
}
}
Thanks everyone!
There are many ways to do this. Using WiX DTF should be relatively easy, but you might not want the dependency on the DTF dlls? If so you can use interop and call the C++ API directly (no custom assembly dependencies).
For the DTF scenario (too late to add the C++ api call version tonight) you create a new project, then you add project references to the WiX DTF assemblies (standard path is: "C:\Program Files (x86)\WiX Toolset v3.11\bin"). The files you need are:
Microsoft.Deployment.WindowsInstaller.dll
Microsoft.Deployment.WindowsInstaller.Package.dll
Then you can try this code snippet - consult the DTF help file for more details - the help file you will find in the WiX installation folder ("doc" sub-folder):
using System;
using Microsoft.Deployment.WindowsInstaller;
using Microsoft.Deployment.WindowsInstaller.Package;
namespace DTFTestApplication
{
class Program
{
static void Main(string[] args)
{
InstallPackage pkg = new InstallPackage(#"e:\MySetup.msi", DatabaseOpenMode.ReadOnly);
var version = pkg.Property["ProductVersion"];
Console.WriteLine(version);
}
}
}
Links:
Another answer on WiX DTF and MSI API from serverfault
You need to put a break in if statement.
if (displayName != null && displayName.Contains(findByName)){
info = subkey.GetValue("Version").ToString();
break;
}
else
info = "Not found";
In your case, Display Name is found but the next iteration of for loop setting it to "not found". Also, you need to add fallback mechanism in your code so that if it doesn't found name in 64-bit node then it should search in 32-bit one.
Product GUID Lookup: If you know the product GUID for the product (How can I find the product GUID of an installed MSI setup?), you can simply get the version and other properties for this product GUID:
using System;
using Microsoft.Deployment.WindowsInstaller;
using Microsoft.Deployment.WindowsInstaller.Package;
namespace DTFTestApplication
{
class Program
{
static void Main(string[] args)
{
ProductInstallation i = new ProductInstallation("{8D6DECA5-17F9-42AF-A62B-8D7C5C11069B}");
Console.WriteLine(i.ProductVersion);
}
}
}
Product Name Lookup: If you need to search for your product by name I guess this could do:
using System;
using Microsoft.Deployment.WindowsInstaller;
namespace DTFTestApplication
{
class Program
{
static void Main(string[] args)
{
foreach (var p in ProductInstallation.AllProducts)
{
if (p.ProductName == "Inkscape")
{
Console.Write("Inkspace found in version: " + p.ProductVersion + "\n");
}
}
}
}
}
You can see this code snippet for more terse, advanced constructs using Linq. For example something like this:
var myproduct = ProductInstallation.AllProducts.FirstOrDefault(p => p.ProductName == "MyProductName");
MSI Package Estate: The link below takes you to a VBScript which will create a HTML-report of the MSI package estate: https://github.com/glytzhkof/all/blob/master/MsiHtmlReport-Mini-V4.vbs
Sample screenshot:
Links:
How to enumerate installed MSI Products and their MSP Patches using C#
Uninstalling program

Can't retrieve type information via Roslyn

I am attempting to retrieve the type of a class syntax node in Roslyn so I can get the enclosing namespace by following along with #slaks answer: Roslyn : How to get the Namespace of a DeclarationSyntax with Roslyn C#
I have the following:
static async Task MainAsync(string[] args)
{
string projectPath = #"C:\Projects\ertp\Ertp.Mobile.csproj";
var msWorkspace = MSBuildWorkspace.Create();
var project = msWorkspace.OpenProjectAsync(projectPath).Result;
foreach (var document in project.Documents)
{
Console.WriteLine(project.Name + "\t\t\t" + document.Name);
SemanticModel model = await document.GetSemanticModelAsync();
var classes = document.GetSyntaxRootAsync().Result.DescendantNodes().OfType<ClassDeclarationSyntax>();
foreach (var klass in classes)
{
var info = model.GetTypeInfo(klass);
var isNull = info.Type == null; //TRUE
}
}
If I can't get the type I can't the namespace - any idea how I can retrieve the details I require?
For decelerators you need to call model.GetDeclaredSymbol(node) and then for the namespace, ContainingNamespace.
model.GetTypeInfo(node).Type will work for expression node.

Unity Add Default Namespace to Script Template?

I just found Unity's script template for C# scripts. To get the script name you write #SCRIPTNAME# so it looks like this:
using UnityEngine;
using System.Collections;
public class #SCRIPTNAME# : MonoBehaviour
{
void Start ()
{
}
void Update ()
{
}
}
Then it would create the script with the right name, but is there something like #FOLDERNAME# so that I can put it in the right namespace directly when creating the script?
There is no built-in template variables like #FOLDERNAME#.
According to this post, there are only 3 magic variables.
"#NAME#"
"#SCRIPTNAME#"
"#SCRIPTNAME_LOWER#"
But you can always hook into the creation process of a script and append the namespace yourself using AssetModificationProcessor.
Here is an example that adds some custom data to the created script.
//Assets/Editor/KeywordReplace.cs
using UnityEngine;
using UnityEditor;
using System.Collections;
public class KeywordReplace : UnityEditor.AssetModificationProcessor
{
public static void OnWillCreateAsset ( string path )
{
path = path.Replace( ".meta", "" );
int index = path.LastIndexOf( "." );
string file = path.Substring( index );
if ( file != ".cs" && file != ".js" && file != ".boo" ) return;
index = Application.dataPath.LastIndexOf( "Assets" );
path = Application.dataPath.Substring( 0, index ) + path;
file = System.IO.File.ReadAllText( path );
file = file.Replace( "#CREATIONDATE#", System.DateTime.Now + "" );
file = file.Replace( "#PROJECTNAME#", PlayerSettings.productName );
file = file.Replace( "#SMARTDEVELOPERS#", PlayerSettings.companyName );
System.IO.File.WriteAllText( path, file );
AssetDatabase.Refresh();
}
}
I know it's and old question but in newer versions of Unity you can define a root namespace to be used in the project.
You can define the namespace in Edit > Project Settings > Editor > Root Namespace
Doing this will add the defined namespace on newly created scripts.
Using zwcloud's answer and some other resources I was able to generate a namespace on my script files:
First step, navigate:
Unity's default templates can be found under your Unity installation's directory in Editor\Data\Resources\ScriptTemplates for Windows and /Contents/Resources/ScriptTemplates for OSX.
And open the file 81-C# Script-NewBehaviourScript.cs.txt
And make the following change:
namespace #NAMESPACE# {
At the top and
}
At the bottom. Indent the rest so that the whitespace is as desired. Don't save this just yet. If you wish, you can make other changes to the template, such as removing the default comments, making Update() and Start() private, or even removing them entirely.
Again, do not save this file yet or Unity will throw an error on the next step. If you saved, just hit ctrl-Z to undo and then resave, then ctrl-Y to re-apply the changes.
Now create a new script inside an Editor folder inside your Unity Assets directory and call it AddNameSpace. Replace the contents with this:
using UnityEngine;
using UnityEditor;
public class AddNameSpace : UnityEditor.AssetModificationProcessor {
public static void OnWillCreateAsset(string path) {
path = path.Replace(".meta", "");
int index = path.LastIndexOf(".");
if(index < 0) return;
string file = path.Substring(index);
if(file != ".cs" && file != ".js" && file != ".boo") return;
index = Application.dataPath.LastIndexOf("Assets");
path = Application.dataPath.Substring(0, index) + path;
file = System.IO.File.ReadAllText(path);
string lastPart = path.Substring(path.IndexOf("Assets"));
string _namespace = lastPart.Substring(0, lastPart.LastIndexOf('/'));
_namespace = _namespace.Replace('/', '.');
file = file.Replace("#NAMESPACE#", _namespace);
System.IO.File.WriteAllText(path, file);
AssetDatabase.Refresh();
}
}
Save this script as well as saving the changes to 81-C# Script-NewBehaviourScript.cs.txt
And you're done! You can test it by creating a new C# script inside any series of folders inside Assets and it will generate the new namespace definition we created.
I'm well aware that this question has been answered by the awesome people (zwcloud, Darco18 and Alexey).
However, since my namespace organization follows the folder structure in the project, I've put together in a jiffy some minor modification to the code, and I'm sharing it here in case someone needs it and has the same organizational methodology which I'm following.
Please keep in mind that you need to set the root namespace in your project settings in the C# project generation section.
EDIT: I've adjusted the code a bit work with placement in the root folders of Scripts, Editor, etc..
public class NamespaceResolver : UnityEditor.AssetModificationProcessor
{
public static void OnWillCreateAsset(string metaFilePath)
{
var fileName = Path.GetFileNameWithoutExtension(metaFilePath);
if (!fileName.EndsWith(".cs"))
return;
var actualFile = $"{Path.GetDirectoryName(metaFilePath)}\\{fileName}";
var segmentedPath = $"{Path.GetDirectoryName(metaFilePath)}".Split(new[] { '\\' }, StringSplitOptions.None);
var generatedNamespace = "";
var finalNamespace = "";
// In case of placing the class at the root of a folder such as (Editor, Scripts, etc...)
if (segmentedPath.Length <= 2)
finalNamespace = EditorSettings.projectGenerationRootNamespace;
else
{
// Skipping the Assets folder and a single subfolder (i.e. Scripts, Editor, Plugins, etc...)
for (var i = 2; i < segmentedPath.Length; i++)
{
generatedNamespace +=
i == segmentedPath.Length - 1
? segmentedPath[i]
: segmentedPath[i] + "."; // Don't add '.' at the end of the namespace
}
finalNamespace = EditorSettings.projectGenerationRootNamespace + "." + generatedNamespace;
}
var content = File.ReadAllText(actualFile);
var newContent = content.Replace("#NAMESPACE#", finalNamespace);
if (content != newContent)
{
File.WriteAllText(actualFile, newContent);
AssetDatabase.Refresh();
}
}
}
OK, so this question was already answered by two wonderful people, zwcloud and Draco18s, and their solution works, I'm just showing another version of the same code that, I hope, will be a little more clear in terms of what exactly happening.
Quick notes:
Yes, in this method we are getting not the actual file path,
but the path of its meta file as a parameter
No, you can not use AssetModificationProcessor without UnityEditor prefix, it is deprecated
OnWillCreateAsset method is not shown via Ctrl+Shift+M, 'override' typing or base class metadata
_
using UnityEditor;
using System.IO;
public class ScriptTemplateKeywordReplacer : UnityEditor.AssetModificationProcessor
{
//If there would be more than one keyword to replace, add a Dictionary
public static void OnWillCreateAsset(string metaFilePath)
{
string fileName = Path.GetFileNameWithoutExtension(metaFilePath);
if (!fileName.EndsWith(".cs"))
return;
string actualFilePath = $"{Path.GetDirectoryName(metaFilePath)}{Path.DirectorySeparatorChar}{fileName}";
string content = File.ReadAllText(actualFilePath);
string newcontent = content.Replace("#PROJECTNAME#", PlayerSettings.productName);
if (content != newcontent)
{
File.WriteAllText(actualFilePath, newcontent);
AssetDatabase.Refresh();
}
}
}
And this is the contents of my file c:\Program Files\Unity\Editor\Data\Resources\ScriptTemplates\81-C# Script-NewBehaviourScript.cs.txt
using UnityEngine;
namespace #PROJECTNAME#
{
public class #SCRIPTNAME# : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
}
Here is my solution, in my case Unity added the root namespace right before OnWillCreateAsset(), so I had to replace the root namespace in the script with the one I want.
Here is the code
public class AddNameSpace : UnityEditor.AssetModificationProcessor
{
public static void OnWillCreateAsset(string path)
{
var rootNamespace =
string.IsNullOrWhiteSpace(EditorSettings.projectGenerationRootNamespace) ?
string.Empty : $"{EditorSettings.projectGenerationRootNamespace}";
path = path.Replace(".meta", "");
int index = path.LastIndexOf(".");
if (index < 0)
return;
string file = path.Substring(index);
if (file != ".cs" && file != ".js" && file != ".boo")
return;
string formattedNamespace = GetNamespace(path, rootNamespace);
file = System.IO.File.ReadAllText(path);
if (file.Contains(formattedNamespace))
return;
file = file.Replace(rootNamespace, formattedNamespace);
System.IO.File.WriteAllText(path, file);
AssetDatabase.Refresh();
}
private static string GetNamespace(string filePath, string rootNamespace)
{
filePath = filePath.Replace("Assets/Scripts/", "/")
.Replace('/', '.')
.Replace(' '.ToString(), string.Empty);
var splitPath = filePath.Split('.');
string pathWithoutFileName = string.Empty;
for (int i = 1; i < splitPath.Length - 2; i++)
{
if (i == splitPath.Length - 3)
{
pathWithoutFileName += splitPath[i];
}
else
{
pathWithoutFileName += splitPath[i] + '.';
}
}
return $"{rootNamespace}.{pathWithoutFileName}";
}
}

SSMS Extensibility Project - howto research/debug

In the vein of this answer regarding creation of an SSMS Extension:
namespace SSMSAddin
{
using System;
using System.IO;
using Extensibility;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.CommandBars;
using Microsoft.SqlServer.Management.UI.VSIntegration;
using System.Windows.Forms;
public class Connect : IDTExtensibility2, IDTCommandTarget
{
private DTE2 applicationObject;
private CommandEvents executeSqlEvents;
private AddIn addInInstance;
public Connect() { }
public void OnConnection(object application, ext_ConnectMode connectMode, object addInInst, ref Array custom)
{
this.applicationObject = (DTE2)application;
this.addInInstance = (AddIn)addInInst;
this.applicationObject = (DTE2)application;
this.executeSqlEvents = this.applicationObject.Events.CommandEvents["{52692960-56BC-4989-B5D3-94C47A513E8D}", 1];
this.executeSqlEvents.BeforeExecute += this.ExecuteSqlEventsBeforeExecute;
if (connectMode == ext_ConnectMode.ext_cm_UISetup)
{
var contextGUIDS = new object[] { };
var commands = (Commands2)this.applicationObject.Commands;
string toolsMenuName = "Tools";
//Place the command on the tools menu.
//Find the MenuBar command bar, which is the top-level command bar holding all the main menu items:
CommandBar menuBarCommandBar = ((CommandBars)this.applicationObject.CommandBars)["MenuBar"];
//Find the Tools command bar on the MenuBar command bar:
CommandBarControl toolsControl = menuBarCommandBar.Controls[toolsMenuName];
CommandBarPopup toolsPopup = (CommandBarPopup)toolsControl;
//This try/catch block can be duplicated if you wish to add multiple commands to be handled by your Add-in,
// just make sure you also update the QueryStatus/Exec method to include the new command names.
try
{
//Add a command to the Commands collection:
Command command = commands.AddNamedCommand2(this.addInInstance, "SSMSAddin", "SSMSAddin", "Executes the command for SSMSAddin", true, 59, ref contextGUIDS, (int)vsCommandStatus.vsCommandStatusSupported + (int)vsCommandStatus.vsCommandStatusEnabled, (int)vsCommandStyle.vsCommandStylePictAndText, vsCommandControlType.vsCommandControlTypeButton);
//Add a control for the command to the tools menu:
if ((command != null) && (toolsPopup != null))
{
command.AddControl(toolsPopup.CommandBar, 1);
}
}
catch (ArgumentException)
{
//If we are here, then the exception is probably because a command with that name
// already exists. If so there is no need to recreate the command and we can
// safely ignore the exception.
}
}
}
private void ExecuteSqlEventsBeforeExecute(string guid, int id, object customin, object customout, ref bool canceldefault)
{
try
{
Document document = ((DTE2)ServiceCache.ExtensibilityModel).ActiveDocument;
var textDocument = (TextDocument)document.Object("TextDocument");
string queryText = textDocument.Selection.Text;
if (string.IsNullOrEmpty(queryText))
{
EditPoint startPoint = textDocument.StartPoint.CreateEditPoint();
queryText = startPoint.GetText(textDocument.EndPoint);
}
DateTime now = DateTime.Now;
// string server =
string folderPath = string.Format(#"B:\SSMS Queries\{0}", now.ToString("yyyyMMdd"));
string fileName = now.ToString("yyyyMMdd-HHmmss") + ".sql";
Directory.CreateDirectory(folderPath);
string fullPath = Path.Combine(folderPath, fileName);
File.WriteAllText(fullPath, queryText);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
public void OnDisconnection(ext_DisconnectMode disconnectMode, ref Array custom) { }
public void OnAddInsUpdate(ref Array custom) { }
public void OnStartupComplete(ref Array custom) { }
public void OnBeginShutdown(ref Array custom) { }
public void QueryStatus(string commandName, vsCommandStatusTextWanted neededText, ref vsCommandStatus status, ref object commandText)
{
if (neededText == vsCommandStatusTextWanted.vsCommandStatusTextWantedNone)
{
if (commandName == "SSMSAddin.Connect.SSMSAddin")
{
status = (vsCommandStatus)vsCommandStatus.vsCommandStatusSupported | vsCommandStatus.vsCommandStatusEnabled;
return;
}
}
}
public void Exec(string commandName, vsCommandExecOption executeOption, ref object varIn, ref object varOut, ref bool handled)
{
handled = false;
if (executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
{
if (commandName == "SSMSAddin.Connect.SSMSAddin")
{
var document = ((DTE2)ServiceCache.ExtensibilityModel).ActiveDocument;
if (document != null)
{
//replace currently selected text
var selection = (TextSelection)document.Selection;
selection.Insert(
#"Welcome to SSMS. This sample is brought to you by
SSMSBoost add-in team
Check www.ssmsboost.com for updates.",
(Int32)EnvDTE.vsInsertFlags.vsInsertFlagsContainNewText);
}
handled = true;
return;
}
}
}
}
}
The code adds an event that fires before each SQL Execute in SSMS 2012... I hit F5, the sql query runs, but before it runs it saves a copy of the query to B:\SSMS Queries\20130225\083000.sql.
What's missing from this? I want to add options for the Connection/Databse used, say for example B:\SSMS Queries\Localhost\Northwind\20130225\083000.sql (Just an example).
What I would normally do... Breakpoint, step through, inspect objects, etc... This is a addon though. Class library. You can't breakpoint/step through a library...
How do I put a breakpoint into a class library that gets loaded into SSMS/Visual Studio so that I can research? Or what would be a good resource for this kind of tinkering? Somewhere in object customin, object customout is the information I want to tinker with.
The second part of the question to find the connection to the current database ..
Add reference to Microsoft.SqlServer.RegSrvrEnum.dll and SqlWorkBench.Interfaces (located somewhere in your C:\ProgramFiles..\SQL Server.. -). Make sure you have installed the SDK for the tools.
Then the below code should do the trick (your welcome!)
IScriptFactory scriptFactory = ServiceCache.ScriptFactory;
CurrentlyActiveWndConnectionInfo connectionIfno = scriptFactory.CurrentlyActiveWndConnectionInfo;
UIConnectionInfo conn = connectionIfno.UIConnectionInfo;
Debug.WriteLine("{0}::{1}", conn.ServerName, conn.AdvancedOptions["DATABASE"]);
Actually documenting my answer (after forgetting multiple times). Found my answer in a combination of SSMSBoost and TSQLTidy.Blogspot (and Martin Smith comment)
1) Set SSMS as the startup project inside Debug Profile. File location for SSMS2012:
C:\Program Files (x86)\Microsoft SQL Server\110\Tools\Binn\ManagementStudio\SSMS.exe
2) I've created 2 Addin files:
MyAddin.Debug.Addin
MyAddin.Release.Addin
(Contents updated as listed below)
3) Add postbuild event to create directory if not exists
4) Add postbuild event to copy Addin from ProjectDir to MSeventShared
5) Turn off P-Invoke warnings. Press CRLT + ALT + E - In Managed Debugging Assistants, find PInvokeStackImbalance, untick it.
Addin Files (Release changes the DLL location from Project directory to MSEnvShared\Admin folder):
<?xml version="1.0" encoding="UTF-16" standalone="no"?>
<Extensibility xmlns="http://schemas.microsoft.com/AutomationExtensibility">
<HostApplication>
<Name>Microsoft SQL Server Management Studio</Name>
<Version>*</Version>
</HostApplication>
<Addin>
<FriendlyName>MyAddin.</FriendlyName>
<Description>MyAddin Description.</Description>
<Assembly>C:\Projects\MyAddin\bin\Debug\MyAddin.dll</Assembly>
<FullClassName>SSMSAddin.Connect</FullClassName>
<LoadBehavior>0</LoadBehavior>
<CommandPreload>1</CommandPreload>
<CommandLineSafe>0</CommandLineSafe>
</Addin>
</Extensibility>
Post Build Event:
cmd /x /c mkdir "C:\ProgramData\Microsoft\MSEnvShared\Addins\"
cmd /C copy "$(ProjectDir)MyAddin.$(ConfigurationName).Addin" "C:\C:\ProgramData\Microsoft\MSEnvShared\Addins\MyAddin.Addin"

Are there any tools that allow me to change all C# built-in types to their .NET Framework types?

One of the things that I find hard to keep consistent is the use of int vs Int32 and bool vs Boolean etcetera.
I find it simpler to identify all types by their case and color syntax highlighting...
List<int>
vs
List<Int32>
The latter is cleaner and upholds consistency. A lot of code is littered with both and I'm looking for a refactoring tool to change them all.
Are there any tools that allow me to change all C# built-in types to their .NET Framework types?
If you look at StyleCop, rule SA1121 actually enforces the opposite of what you want (asks you to change Int32 to int). It's fairly trivial to decompile that rule and create your own StyleCop rule to enforce the opposite.
This isn't automatic, but after you do your initial conversion, you can incorporate it into your builds and then flag any new uses as errors.
Using the Roslyn CTP, the following appears to work in practice:
static SyntaxTree UpdatePredefinedTypes(this SyntaxTree tree)
{
PredefinedTypeSyntax node;
var root = tree.Root;
while (null != (node = root.DescendentNodes()
.OfType<PredefinedTypeSyntax>()
.FirstOrDefault(
syn => redefineMap.ContainsKey(syn.PlainName))))
{
var ident = Syntax.IdentifierName(redefineMap[node.PlainName]);
root = root.ReplaceNode<SyntaxNode, SyntaxNode>(
node,
ident.WithLeadingTrivia(node.GetLeadingTrivia())
.WithTrailingTrivia(node.GetTrailingTrivia()));
}
return SyntaxTree.Create(
tree.FileName,
(CompilationUnitSyntax)root,
tree.Options);
}
When using a proper redefineMap (e.g. {"int","Int32"}, {"double","Double"}) the following program was converted successfully:
using System;
namespace HelloWorld {
class Program {
static void Main(string[] args) {
int x = Int32.Parse("11");
double y = x;
Console.WriteLine("Hello, World! {0}", y);
}
}
}
Output:
using System;
namespace HelloWorld {
class Program {
static void Main(String[] args) {
Int32 x = Int32.Parse("11");
Double y = x;
Console.WriteLine("Hello, World! {0}", y);
}
}
}
When compiling:
var mscorlib = new AssemblyFileReference(
typeof(object).Assembly.Location);
var newTree = UpdatePredefinedTypes(tree);
var compilation = Compilation.Create("HelloWorld")
.AddReferences(mscorlib)
.AddSyntaxTrees(new[] { newTree });
var results = compilation.Emit(File.Create("helloworld.exe"));
Console.WriteLine("Success: {0}", results.Success);
foreach (var message in results.Diagnostics)
{
Console.WriteLine("{0}", message);
}
// C:\tmp\cs>roslyn-test.exe
// Success: True
//
// C:\tmp\cs>dir /b *.exe
// roslyn-test.exe
// helloworld.exe
//
// C:\tmp\cs>helloworld.exe
// Hello, World! 11
//
You can even utilize the Workspace features to update an entire solution:
var workspace = Workspace.LoadSolution(info.FullName);
var solution = workspace.CurrentSolution;
foreach (var project in solution.Projects
.Where(prj => prj.LanguageServices.Language == "C#"))
{
foreach (var doc in project.Documents
.Where(d => d.SourceCodeKind == SourceCodeKind.Regular
&& d.LanguageServices.Language == "C#"))
{
var tree = SyntaxTree.ParseCompilationUnit(
doc.GetText(),
doc.DisplayName);
var newTree = UpdatePredefinedTypes(tree);
solution = solution.UpdateDocument(doc.Id, newTree.Text);
}
}
workspace.ApplyChanges(workspace.CurrentSolution, solution);
// when running this in VS on itself it correctly updates the project!
I do not know any tools except the search and replace function of VS.
I usually use the c# alias for type declarations and the .NET type when I call static members
int i = Int32.Parse(s);
It is just a personal preference.
I eventually wrote a macro to do this
Option Strict Off
Option Explicit Off
Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports EnvDTE90a
Imports EnvDTE100
Imports System.Diagnostics
Public Module ReplaceCSharpBuiltInTypesWithTheirFrameworkTypes
Sub ReplaceCSharpBuiltInTypesWithTheirFrameworkTypes()
Dim dictionary As New Collections.Generic.Dictionary(Of String, String)
dictionary.Add("bool", "Boolean")
dictionary.Add("byte", "Byte")
dictionary.Add("sbyte", "SByte")
dictionary.Add("char", "Char")
dictionary.Add("decimal", "Decimal")
dictionary.Add("double", "Double")
dictionary.Add("float", "Single")
dictionary.Add("int", "Int32")
dictionary.Add("uint", "UInt32")
dictionary.Add("long", "Int64")
dictionary.Add("ulong", "UInt64")
dictionary.Add("object", "Object")
dictionary.Add("short", "Int16")
dictionary.Add("ushort", "UInt16")
dictionary.Add("string", "String")
For Each key In dictionary.Keys
DTE.Find.FindWhat = key
DTE.Find.ReplaceWith = dictionary(key)
DTE.Find.Target = vsFindTarget.vsFindTargetCurrentDocument
DTE.Find.MatchCase = True
DTE.Find.MatchWholeWord = True
DTE.Find.MatchInHiddenText = False
DTE.Find.PatternSyntax = vsFindPatternSyntax.vsFindPatternSyntaxLiteral
DTE.Find.ResultsLocation = vsFindResultsLocation.vsFindResultsNone
DTE.Find.Action = vsFindAction.vsFindActionReplaceAll
DTE.Find.Execute()
Next
End Sub
End Module

Categories