|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
Note: This is an unedited contribution. If this article is inappropriate,
needs attention or copies someone else's work without reference then please
Report This Article
IntroductionVisual Studio 2005 / 2008 both lack a good way to define assembly references per solution configuration (‘Debug’ or ‘Release’). BackgroundThere are some ways to define assembly references which change if the solution configuration changes, but they are not sufficient for larger projects. The following features of Visual Studio 2005 / 2008 are fine for small projects:
The Lexware Assembly Reference Tool fills this gap, by providing a new tool window in Visual Studio 2005 / 2008, which allows you to change ‘hard-coded’ assembly reference paths to flexible reference paths which change depending on the solution configuration. The tool detects ‘Debug’ or ‘Release’ in the assembly reference path and marks the assembly in red to show you the potential problem you have, when you build the project in another configuration. You only need to press a button and all these paths will be converted to paths which depend on the configuration of the project. Additionally the tool provides the following features:
After installing and starting the Add-In you can open the assembly reference tool via the Visual Studio 2005 / 2008 tools menu. How it worksThe general idea behind itThe tool allows flexible assembly reference paths by replacing “\Debug\” or “\Release\” in the reference path against “\$Configuration\”, which is a placeholder for the solution configuration in Visual Studio 2005 / 2008. Visual Basic and CSharp projects are able to replace the placeholder back to “\Debug\” or “\Relase\” when trying to resolve an assembly path. When the tool saves the project file, Visual Studio notices that change and asks you to reload the project. For some features the tool holds a reference to the internal Visual Studio representation of an assembly reference (VSLangProj80.Reference3), which allows a manipulation of properties like ‘CopyLocal’, ‘SpecificVersion’ or deleting a reference. Changes to these properties are directly reflected by the original property window of the assembly reference in Visual Studio. The Tool solution itselfThe solution contains a CSharp and a setup project. The setup project was created with the Setup Project template. The CSharp project is originally created with a Visual Studio Add-In project template of Visual Studio 2005. Visual Studio automatically creates a ‘Tool’ menu item for the add-in, if you choose so in the project wizard. If you select ‘I would like my Add-In to load, …’ Visual Studio loads your Add-In directly when it starts. In the new project Visual Studio creates two files with the extension ‘AddIn’. One is lying in the project folder (e.g. ‘Lexware.Tools.AssemblyReferences.AddIn’) and this is used when the Add-In is deployed. The other one (e.g. ‘Lexware.Tools.AssemblyReferences - For Testing.AddIn’) is placed in the Visual Studio Add-In folder. This is the one which is used while debugging the Add-In. The Visual Studio Add-In folder is contained in your documents folder. For Windows Vista it should be found here: C:\Users\’Your User Name’\Documents\Visual Studio 2005\Addins. An Add-In file contains a full description of the Add-In. It provides a friendly name, the load behavior and other information, necessary for Visual Studio to display the Add-In in the ‘Add-in Manager’. The Add-InThe Add-In project contains the file ‘connect.cs’, which contains the code connecting to Visual Studio. The class ‘Connect’ is called, when the Add-In starts. In fact it is defined in the ‘FullClassName’ tag of the ‘.Addin’ file described below. The class ConnectThe class public void OnConnection(object application, ext_ConnectMode connectMode,
object addInInst, ref Array custom)
{
_applicationObject = (DTE2)application;
_addInInstance = (AddIn)addInInst;
CreateToolWindow();
if(connectMode == ext_ConnectMode.ext_cm_UISetup)
{
object[] contextGUIDS = new object[] {};
Commands2 commands = (Commands2)_applicationObject.Commands;
string toolsMenuName;
try
{
//If you would like to move the command to a different menu, change the
//word "Tools" to the English version of the menu. This code will take
// the culture, append on the name of the menu then add the command to
// that menu. You can find a list of all the top-level menus in the file
// CommandBar.resx.
ResourceManager resourceManager = new ResourceManager(
"Lexware.Tools.AssemblyReferences.CommandBar",
Assembly.GetExecutingAssembly());
CultureInfo cultureInfo = new CultureInfo(_applicationObject.LocaleID);
string resourceName = String.Concat(cultureInfo.TwoLetterISOLanguageName,
LocalResources.ToolbarName);
toolsMenuName = resourceManager.GetString(resourceName);
}
catch
{
//We tried to find a localized version of the word Tools, but one was
// not found.
// Default to the en-US word, which may work for the current culture.
toolsMenuName = LocalResources.ToolbarName;
}
//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:
Microsoft.VisualStudio.CommandBars.CommandBar menuBarCommandBar = (
(CommandBars)_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(_addInInstance,
"AssemblyReferences", "Assembly Reference Tool",
"Checks and fixes assembly references. Uses placeholder for debug and release directory.",
true, 0, ref contextGUIDS,
(int)vsCommandStatus.vsCommandStatusSupported +
(int)vsCommandStatus.vsCommandStatusEnabled,
(int)vsCommandStyle.vsCommandStyleText,
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.
}
}
}
When the user clicks your menu item, the method public void Exec(string commandName, vsCommandExecOption executeOption,
ref object varIn, ref object varOut, ref bool handled)
{
handled = false;
if(executeOption == vsCommandExecOption.vsCommandExecOptionDoDefault)
{
if(commandName == "Lexware.Tools.AssemblyReferences.Connect.AssemblyReferences")
{
// Open Toolwindow
CreateToolWindow();
handled = true;
return;
}
}
}
The tool window, which contains all the important code in this Add-In, is a UserControl. To create it, you can call private void CreateToolWindow()
{
if(_toolWindow != null)
{
_toolWindow.Activate();
}
else
{
//This guid must be unique for each different tool window,
// but you may use the same guid for the same tool window.
//This guid can be used for indexing the windows collection,
// for example: applicationObject.Windows.Item(guidstr)
Windows2 windows2 = (Windows2)_applicationObject.Windows;
Assembly asm = Assembly.GetExecutingAssembly();
object customControl = null;
string className = "Lexware.Tools.AssemblyReferences.ToolWindowControl";
string caption = "Assembly References";
_toolWindow = windows2.CreateToolWindow2(_addInInstance, asm.Location, className,
caption, _toolWindowGuid,
ref customControl);
//Set the picture displayed when the window is tab docked (this causes
//problems in Visual Studio 2008)
try
{
_toolWindow.SetTabPicture(LocalResources.LexwareBmp.GetHbitmap());
}
catch
{
}
//When using the hosting control, you must set visible to true before calling
// HostUserControl, otherwise the UserControl cannot be hosted properly.
_toolWindow.Visible = true;
if (customControl != null)
{
_toolWindowControl = (ToolWindowControl)customControl;
_toolWindowControl.ApplicationObject = _applicationObject;
_toolWindowControl.ParentToolWindow = _toolWindow;
}
}
}
The ToolWindowThe tool window registers some events of the Visual Studio solution, document and the command object, so that changes in the solution will be noticed by the Add-In. private void RegisterEvents()
{
if (_solutionEvents != null)
{
UnregisterEvents();
}
_solutionEvents = _applicationObject.Events.SolutionEvents;
// register new events
_solutionEvents.Opened += new _dispSolutionEvents_OpenedEventHandler(
_solutionEvents_Opened);
_solutionEvents.ProjectAdded += new _dispSolutionEvents_ProjectAddedEventHandler(
_solutionEvents_ProjectAdded);
_solutionEvents.ProjectRemoved += new _dispSolutionEvents_ProjectRemovedEventHandler(
_solutionEvents_ProjectRemoved);
_solutionEvents.ProjectRenamed += new _dispSolutionEvents_ProjectRenamedEventHandler(
_solutionEvents_ProjectRenamed);
_solutionEvents.AfterClosing += new _dispSolutionEvents_AfterClosingEventHandler(
_solutionEvents_AfterClosing);
_documentEvents.DocumentSaved += new _dispDocumentEvents_DocumentSavedEventHandler(
_documentEvents_DocumentSaved);
_commandEvents.AfterExecute += new _dispCommandEvents_AfterExecuteEventHandler(
_commandEvents_AfterExecute);
}
private void _commandEvents_AfterExecute(string Guid, int ID, object CustomIn,
object CustomOut)
{
//Command name: File.SaveSelectedItems
//Command GUID/ID: {5EFC7975-14BC-11CF-9B2B-00AA00573819}, 331
//Command name: File.SaveAll
//Command GUID/ID: {5EFC7975-14BC-11CF-9B2B-00AA00573819}, 224
//Command name: File.SaveSelectedItemsAs
//Command GUID/ID: {5EFC7975-14BC-11CF-9B2B-00AA00573819}, 226
//Command name: Build.SolutionConfigurations
//Command GUID/ID: {5EFC7975-14BC-11CF-9B2B-00AA00573819}, 684
//Command name: Project.Addreference
//Command GUID/ID: {1496A755-94DE-11D0-8C3F-00C04FC2AAE2}, 1113
if (((Guid == "{5EFC7975-14BC-11CF-9B2B-00AA00573819}") && (ID == 331)) ||
((Guid == "{5EFC7975-14BC-11CF-9B2B-00AA00573819}") && (ID == 224)) ||
((Guid == "{5EFC7975-14BC-11CF-9B2B-00AA00573819}") && (ID == 226)))
{
ReadAllReferences();
}
else if ((Guid == "{1496A755-94DE-11D0-8C3F-00C04FC2AAE2}") && (ID == 1113))
{
ParentToolWindow.Activate();
}
}
When the solution changes, the Add-In iterates the projects contained in the solution and reads in the references of each project. It adds each reference to the list view and puts an instance of the class private void ReadAllReferences()
{
ClearHintLists();
if ((_applicationObject != null) && (_applicationObject.Solution != null))
{
// Walk through the projects of the solution and search for assembly references
foreach (Project currentProject in _applicationObject.Solution.Projects)
{
ReadProjectReferences(currentProject);
}
}
}
private void ReadProjectReferences(Project currentProject)
{
try
{
if (currentProject != null)
{
VSProject2 visualStudioProject = currentProject.Object as VSProject2;
// The current project can be a 'real' project, but it can also be a
// folder (see else if)
if (visualStudioProject != null)
{
string projectFullName = currentProject.FullName;
if (!string.IsNullOrEmpty(projectFullName))
{
FileInfo projectFileInfo = new FileInfo(projectFullName);
// If it is a csproj or a vbproj, add it the the list view
if (projectFileInfo.Exists &&
((projectFileInfo.Extension == _csProjectFileExtension) ||
(projectFileInfo.Extension == _vbProjectFileExtension)))
{
// Add a group for this project
ListViewGroup projectGroup = GetProjectGroup(currentProject);
AddAssemblyHintsToListView(currentProject, projectFullName,
projectGroup, visualStudioProject);
}
}
}
else if ((currentProject.ProjectItems != null) && (
currentProject.ProjectItems.Count > 0))
{
// Project Item Type GUID
// Physical File {6BB5F8EE-4483-11D3-8BCF-00C04F8EC28C}
// Physical Folder {6BB5F8EF-4483-11D3-8BCF-00C04F8EC28C}
// Virtual Folder {6BB5F8F0-4483-11D3-8BCF-00C04F8EC28C}
// Subproject {EA6618E8-6E24-4528-94BE-6889FE16485C}
// The projects contains a sub folder -> search for projects in
// these folders
foreach (ProjectItem currentProjectItem in currentProject.ProjectItems)
{
if (currentProjectItem.SubProject != null)
{
ReadProjectReferences(currentProjectItem.SubProject);
}
}
}
}
// Enable fixit button, if there is something to fix
toolStripButtonFixIt.Enabled = (_needsToBeSaved.Count > 0);
}
catch (Exception ex)
{
ShowMessage(ex);
}
}
When the user hits the 'FixIt' button, the tool changes the underlying project file. It save all projects which needs to be saved, due to an incorrect assembly path. private void SaveDirtyProjects()
{
try
{
foreach (KeyValuePair
Visual Studio notices that the tool changed the project file and asks you to reload the project. The setup projectThe setup project deploys the Add-In to the AddIns folders of Visual Studio 2005 and Visual Studio 2008. Requirements to build the solutionTo build the solution, you need Visual Studio 2008. The Add-In is tested on Visual Studio 2005 and 2008 Team Developer and Team Suite. Historyno changes
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||