牵着老婆满街逛

严以律己,宽以待人. 三思而后行.
GMail/GTalk: yanglinbo#google.com;
MSN/Email: tx7do#yahoo.com.cn;
QQ: 3 0 3 3 9 6 9 2 0 .

VSHelper - Visual Studio IDE enhancements

from:http://www.codeproject.com/macro/VSHelper.asp

Introduction

When I had to switch from Visual C++ 6 to .NET, I discovered that things were not working the same way as they used to. I had to adapt to the new IDE. I thought, why can't the IDE adapt to me? That's how the VSHelper add-in was born. It fixed some of the pet peeves I had with .NET IDE. Over time I continued to add more functionality. Hopefully, other people will find some of these features useful.

The add-in registers 11 new commands, which you can map to key combinations or access from the toolbar.

Add-in commands

Cut and copy (use plain text, do nothing if no text is selected)

These are replacements for the standard Edit.Cut and Edit.Copy commands. The difference is that all special formatting is stripped out from the clipboard. What does that mean?

Imagine, you copy this text from your C++ source to your email:

The text is copied in Rich Text Format, which preserves the fonts and colors of the source window. Not all applications interpret the RTF content correctly. Even if they do, you may still prefer plain text.

The text pasted in Outlook Express (the font and the background colors are not preserved):

The text pasted in Word (the font is not preserved properly):

The text pasted in WordPad (technically correct, but still not the same as the original):

Another feature of the new Cut and Copy is that they do nothing if no text is selected. The original Edit.Cut and Edit.Copy put the current line in the clipboard even if nothing is selected. If you press Ctrl+C when you actually wanted to press Ctrl+V, the editor will copy the current line and mess up the clipboard contents.

If for some reason you want to preserve the full RTF content, in that case, you can access the Edit.Copy command from the main menu, or from the right-click menu.

Esc (close all secondary windows)

Do you remember how you could press Esc in VC6 and hide all the unnecessary windows? It is an incredibly useful way to quickly maximize your work area. Sadly, .NET doesn't support that. And there is an even greater need for it because there are more modeless windows that pop up and they don't close on their own - Properties, Find window, Toolbox, etc.

The new Esc command can help in this situation. Pressing Esc several times will progressively:

  • focus the main document,
  • clear the text selection,
  • close the unnecessary windows.

The list of affected windows can be customized from the settings.

The initial selection includes: Output window, Find Results windows, Properties, Task List, Toolbox, Find Symbol, Find and Replace, Visual Assist's Find References and the Incredibuild window.

The list of all available windows is retrieved from the HKLM\SOFTWARE\Microsoft\VisualStudio\7.1\ToolWindows registry key. For VS 2005 replace 7.1 with 8.0.

FindNext and FindPrev (Find the text and close the Find window)

In VS 2003, the Find window is no longer modal. This is not a big deal in itself; the problem is that it loses focus very easily. For example, press Ctrl+F, type some text, and then press F3. The Find window stays open, but loses focus. Now, you have this modeless, focus-less window obscuring your editor until you press Cltr+F and Esc, or reach for the mouse to close it.

The new FindNext and FindPrev commands first invoke the original Edit.FindNext and Edit.FindPrevious commands and then close the Find window. It works like this:

ExecuteCommand(next?"Edit.FindNext":"Edit.FindPrevious");
CloseWindow(wins,vsWindowKindFindReplace);

Output (show the Output and Find Results windows)

In VC6, the Output and Find Results are part of the same window. You can have a single key combination to go to them. In .NET, they are separate windows and you'll need different shortcuts for each of them. The new Output command will show all the three with a single key combination. If the windows are docked together the Output window will be on top, then the Find Results 1, and then the Find Results 2. Here's how it's done:

CComPtr<Window> output=FindWindow(wins,vsWindowKindOutput);
CComPtr<Window> find1=FindWindow(wins,vsWindowKindFindResults1);
CComPtr<Window> find2=FindWindow(wins,vsWindowKindFindResults2);
find2->Activate();
find1->Activate();
output->Activate();

Opposite (switch between C/CPP and H files)

This command simply switches between the source and header files:

// Retrieve the current document's name
CComPtr<Document> doc;
m_pDTE->get_ActiveDocument(&doc);
BSTR name;
doc->get_FullName(&name);
wchar_t name2[_MAX_PATH+_MAX_EXT];
memcpy(name2,name,wcslen(name)*2+2);
// modify name2 according to the settings
// .....
// Open the new file name
BSTR name3=SysAllocString(name2);
Window *win;
BSTR kind=_com_util::ConvertStringToBSTR(vsViewKindTextView);
CComPtr<ItemOperations> operations;
m_pDTE->get_ItemOperations(&operations);
operations->OpenFile(name3,kind,&win);

The Opposite command can cycle between a list of extensions. By default it is .h -> .inl -> .cpp -> .c -> .h. This can be changed from the settings.

Also if the current file starts with // ## <filename> the command will switch to the specified file name. For example to toggle between File.inc and File.hpp you do:

Put this in the beginning of File.inc:

// ## File.hpp
..........

Put this in the beginning of File.hpp:

// File.inc
..........

The "// ##" prefix is configurable from the settings.

Locate (find document in the Solution Explorer)

Visual Studio has an option "Track Active Item", which will always expand the tree in Solution Explorer to show the current document. This can be very annoying when working with large projects. After some time, all the projects and their folders become expanded, and it takes lot of scrolling to find the files you are looking for. In VS2002, this was the only behavior. In VS2003, it is optional, and turned off by default (thanks, MS :)).

But occasionally you do need to find the current file in the Solution Explorer. That's when the Locate command comes in. It will locate the current document in the Solution Explorer. It will switch focus to Solution Explorer and will scroll the selected item into the view. The code uses a combination of VS automation API and Win32 API:

Collapse
// Find the ProjectItem for the current document
CComPtr<Document> doc;
m_pDTE->get_ActiveDocument(&doc);
CComPtr<ProjectItem> pitem;
doc->get_ProjectItem(&pitem);
// Find the Solution Explorer object
CComPtr<Windows> wins;
m_pDTE->get_Windows(&wins);
CComPtr<Window> se=FindWindow(wins,vsWindowKindSolutionExplorer);
// Get the UIHierarchyItems
CComPtr<UIHierarchy> uih;
se->get_Object((IDispatch**)&uih);
CComPtr<UIHierarchyItems> items;
uih->get_UIHierarchyItems(&items);
// Convert the ProjectItem into UIHierarchyItem
CComPtr<UIHierarchyItem> hitem=FindProjectItem(items,pitem);
if (hitem) {
// Select the item in the Explorer
hitem->Select(vsUISelectionTypeSelect);
if (g_SolutionTree) {
// Scroll the item into view
HTREEITEM h=TreeView_GetSelection(g_SolutionTree);
TreeView_EnsureVisible(g_SolutionTree,h);
}
// Activate the Solution Explorer window
se->Activate();
}

Home (go to the first column, in VS 2003 version only)

Another thing missing in VS 2003 is the "Go to the first column of the line" command. The new Home command does exactly that – jumps to the first column of the current line. That command exists only in VS 2003 version of the add-in because in VS 2005 that feature is back - the command is called Edit.LineFirstColumn.

CComPtr<Window> w;
m_pDTE->get_ActiveWindow(&w);
// Get the selection
CComPtr<TextSelection> sel;
w->get_Selection((IDispatch **)&sel);
// Jump to the beginning of the line
long top;
sel->get_TopLine(&top);
sel->GotoLine(top,FALSE);

ClearBookmarks (in VS2005 version only)

Starting with VS2005 if you try to clear all bookmarks in the document you will be asked "Are you sure you want to delete all of the bookmark(s)?". It would be great if there was a way to turn this question off after the first time. But there isn't. You can use the ClearBookmarks command instead:

CComPtr<Document> doc;
m_pDTE->get_ActiveDocument(&doc);
// get the text document
CComPtr<IDispatch> spTextDocumentDisp;
doc->Object(CComBSTR("TextDocument"),&spTextDocumentDisp);
CComQIPtr<TextDocument> textDoc=spTextDocumentDisp;
textDoc->ClearBookmarks();

Settings

The Settings command opens a dialog box to edit the settings:

Evaluate g_DebugData - Evaluates g_DebugData when the debugger stops. See below. The change will take effect the next time Visual Studio is started.

Add new files to Perforce - When new files are added to the project they will be added to Perforce automatically. See below. The change will take effect the next time Visual Studio is started.

Always show selection in Solution Explorer - Shows the selection in the Solution Explorer even when it is inactive. See below. This option is only available in Visual Studio 2005. The change will take effect the next time Visual Studio is started.

Support for Numpad Clear key - Translates VK_CLEAR to VK_NUMPAD5. See below. This option is only available in Visual Studio 2005. Turning this option ON requires restarting of Visual Studio.

Opposite order - lists the order of extensions for the Opposite command. You can add or remove extensions and change their order.

Opposite prefix - the prefix for the Opposite command.

Windows to close - shows a list of all tool windows in Visual Studio. The selected windows will be closed with the Esc command.

Additional features

Subclassing the Solution Explorer (in VS 2005 version only)

The developers of VS 2005 decided that the selection in the Solution Explorer should be hidden if the window doesn't have focus and if "Track Active Item" is off. I have that option off (see the Locate command). Now imagine the following situation: You select multiple projects and open their settings. The Solution Explorer loses focus (because the settings dialog gets the focus), and now you can't see which projects you are editing! I'm sure there must be a good reason behind that behavior, I just can't find it.

Fortunately, the Solution Explorer is using a standard TreeView control. The add-in subclasses the Solution Explorer to force the TVS_SHOWSELALWAYS style:

LRESULT CALLBACK CConnect::SolutionProc( HWND hWnd,
UINT uMsg, WPARAM wParam, LPARAM lParam )
{
// Force the TVS_SHOWSELALWAYS style
if (uMsg==WM_STYLECHANGING && wParam==GWL_STYLE)
((STYLESTRUCT*)lParam)->styleNew|=TVS_SHOWSELALWAYS;
return CallWindowProc(g_OldSolutionProc,hWnd,uMsg,wParam,lParam);
}

Support for Numpad Clear key (in VS2005 version only)

In VC6 IDE you can create shortcuts using the key in the middle of the Numpad (where the digit 5 is). The shortcut can be used with the NumLock turned off. VC6 calls this key "Numpad Clear" - for example "Ctrl+Numpad Clear". VS 2003 still supports such shortcuts, but doesn't have a name for the key - the shortcuts show up as "Ctrl+".

VS 2005 completely ignores the Clear key and it can't be used in shortcuts. On the other hand it accepts the "Num 5" key, which is the same but with NumLock turned on. Since I keep my NumLock turned off, I have a whole key on my keyboard that I can't use.

The VSHelper addin solves that problem by installing a keyboard hook and translating VK_CLEAR to VK_NUMPAD5. When the hook is in effect you can press the Clear key with or without NumLock and Visual Studio will accept it as the "Num 5" key:

LRESULT CALLBACK CConnect::NumKeyboardProc( int code,
WPARAM wParam, LPARAM lParam )
{
if (code<0) return CallNextHookEx(g_KbdHook,code,wParam,lParam);
if (CallNextHookEx(g_KbdHook,code,wParam,lParam)) return TRUE;
if (!g_bTranslateNum5 || wParam!=VK_CLEAR) return FALSE;
if (GetKeyState(VK_CONTROL)>=0 && GetKeyState(VK_SHIFT)>=0
&& GetKeyState(VK_MENU)>=0) return FALSE;
keybd_event(VK_NUMPAD5,0,(lParam&0x80000000)?KEYEVENTF_KEYUP:0,0);
return TRUE;
}

The hook is controlled by the "Support for Numpad Clear key" option. If the option is on when Visual Studio starts the hook will be installed. If you really need to enter the Clear key you can temporarily disable the VK_CLEAR -> VK_NUMPAD5 translation by turning off the option. There is only one place in VS where this might be necessary - when creating an accelerator in your resources that uses the Clear key. Even then you can simply type VK_CLEAR in the accelerator table.

Evaluate debug data

The add-in registers an OnEnterBreakMode event handler. Every time the debugger breaks the handler will calculate the expression ((int)&g_DebugData),d and will store it in a variable. You can retrieve the value by calling the GetDebugData function. The purpose of that value will be revealed in another article [1]. :)

Collapse
static char g_DebugData[128];
// OnEnterBreakMode handler
STDMETHODIMP CConnect::OnEnterBreakMode(EnvDTE::dbgEventReason Reason,
EnvDTE::dbgExecutionAction* ExecutionAction)
{
*ExecutionAction=dbgExecutionActionDefault;
Expression *exp=NULL;
memcpy(g_DebugData,"err",4);
if (g_pDebugger) {
// Evaluate the expression and store the 
// result in g_DebugData
g_pDebugger->GetExpression(CComBSTR("((int)&g_DebugData),d"),
VARIANT_FALSE,-1,&exp);
if (!exp) return S_OK;
BSTR val=NULL;
exp->get_Value(&val);
exp->Release();
if (!val) return S_OK;
WideCharToMultiByte(CP_ACP,0,val,-1,
g_DebugData,sizeof(g_DebugData),NULL,NULL);
SysFreeString(val);
}
return S_OK;
}

Perforce helper

When working with Perforce I prefer to use the P4Win client to submit my changes instead of the Visual Studio integration. But sometimes I add files to the project and forget to add them to Perforce. So the next day when somebody tries to build my code they complain about missing files.

For VS 2005 the addin registers OnItemAdded event handler that fires when a new file is added to the project. For VS 2003 there is no such universal event. Instead, the addin first registers an OnOpened event handler that fires when a new solution is opened. Then it goes through all projects of the solution and if it finds a C++ project it registers OnItemAdded handler for that project only. For more details on how to hook project events see here: [2].

Once the OnItemAdded event is detected the add-in checks if the new file is under Perforce control by issuing the "fstat" command. If it is not, the file is added to default changelist (but not submitted) with the "add" command.

To compile the Perforce code you will need the multithreaded DLL version of the P4API. The latest version can be found here: [3]. To disable the Perforce functionality comment out #define ADD_TO_PERFORCE at the top of the Connect.inc file. This will allow you to compile VSHelper without the P4API.

Documentation for the Perforce API can be found here: [4].

Custom toolbar

The add-in creates a new custom toolbar that contains buttons for the most commands:

A tip for using custom images in add-ins: To use custom images for the new commands the add-in must provide a DLL with bitmap resources and provide two values in the registry under the HKLM\Software\Microsoft\VisualStudio\7.1\AddIns\<addin name>.Connect key (for VS 2005 use 8.0 instead of 7.1). The values are SatelliteDLLPath and SatelliteDLLName. The full path to the DLL is constructed like this - <SatelliteDLLPath>\<language code>\<SatelliteDLLName>. The language code is the numeric value of the current language (1033 is English).

Then when you register the commands call:

pCommands->AddNamedCommand(m_pAddInInstance, CComBSTR("FindNext"),
CComBSTR("FindNext"), CComBSTR("Find next text"),
VARIANT_FALSE, // FALSE means to get the bitmap from the DLL
    IDB_FINDNEXT, // IDB_FINDNEXT is the bitmap's resource ID
NULL, vsCommandStatusSupported+vsCommandStatusEnabled,&pFindNext);

In this case we only need bitmaps and they are supposed to be the same in all languages. So instead of providing an additional DLL for the resources we can use the original add-in DLL - VS2003Helper. To do that use these keys in your setup project:

SatelliteDLLPath=[TARGETDIR]    <- this gets expanded to the installation path
SatelliteDLLName='..\VS2003Helper.DLL'

When combined, the full path will be <install path>\1033\..\VS2003Helper.DLL, which when optimized points exactly to the add-in's own DLL.

Another trick to use during development: Since you will be running the DLL from the Debug or Release folder of your project, it will be hard to update the registry with the correct path. Instead, in the .rgs file use:

val SatelliteDLLPath = s '%MODULE%\..'    <- this gets expanded
to <project path>\Debug\VS2003Helper.DLL
val SatelliteDLLName = s '..\VS2003Helper.DLL'

When combined, the full path will be <project path>\Debug\VS2003Helper.DLL\..\1033\..\VS2003Helper.DLL, which when optimized points to the DLL in the Debug folder.

Installation

  • VS 2003 - Download VS2003Helper.zip and run Setup.exe.
  • VS 2005 - Download VS2005Helper.zip and run Setup.exe.
  • The add-in will not automatically bind any of the commands to key combinations. You must do so manually in Visual Studio from Tools\Options\Keyboard. All commands are prefixed with VS2003Helper.Connect or VS2005Helper.Connect.

Building the code

The source code contains projects for VS 2003 and 2005. All the magic happens in the Connect.inc file, which is shared between the two projects. The bitmaps for the toolbar buttons are also shared.

If you want to use the Perforce functionality you need to download P4API from here: [3]. In the project settings replace D:\Work\Libs\p4api with actual path to the API.

Licensing

The source code, the binaries and this article are owned by Pandemic Studios. They can be freely used for commercial and non-commercial purposes under the terms of the MIT license. A copy of the license is included in the readme.rtf file in VSHelperSrc.zip.

Known problems and future development

The Output command always shows the Output window on top. There may be a way to preserve the Z-order between the Output window and the Find Results windows. Possibly the add-in can subclass these windows and keep track of which is activated last.

The Perforce functionality uses only the default settings - depot, workspace, user, password. Instead, they can be added to the Settings dialog. Even better, the settings can probably be retrieved from solution properties. Also in VS 2003 only C++ projects are supported. A future version may support different project types - C#, VB, etc.

Links

[1] Debugger support for CRC hash values

[2] HOWTO: Getting Project and ProjectItem events from a Visual Studio .NET add-in

[3] Download latest multithreaded DLL version of the P4API

[4] Perforce API documentation

[5] HOWTO: Removing commands and UI elements during Visual Studio .NET add-in uninstallation

History

  • Jan, 2006 - First version
    • The add-in compiles and runs for VS 2003 and VS 2005 Beta 2. Binaries provided for VS 2003 only
  • Mar, 2006 - Few fixes
    • Fixed a compatibility issue with Visual Assist. The text from the Copy and Cut commands wasn't added to the Visual Assist's Paste Menu
    • Optimized the Locate command for large solutions
  • Oct, 2006 - Version 2.0: new features and bug fixes
    • Additions to the Opposite command
    • Settings dialog to enable some of the features and to configure the Opposite command
    • Perforce helper - automatically add new files to Perforce
    • Toolbar with icons for most commands
    • Fixed a bug in the Copy and Cut commands that corrupts the clipboard when they are used in the image editor
    • Added uninstall script to remove the commands and the toolbar from Visual Studio. For more details on uninstall scripts see here: [5]
    • Provided binaries for VS 2005
  • Feb, 2007 - Version 2.2: new features and bug fixes
    • Added ClearBookmarks command for VS 2005
    • Added support for the Numpad Clear key for VS 2005
    • Added a list to pick which windows to close with the Esc command
    • Fixed a crash when opening some corrupted .vcproj files
    • Published under the MIT license

About Ivo Beltchev


Ivo started programming in 1985 on an Apple ][ clone. He graduated from Sofia University, Bulgaria with a MSCS degree. Ivo has been working as a professional programmer for over 10 years, and as a professional game programmer for over 8. He is currently employed in Pandemic Studios, a video game company in Los Angeles, California.

Click here to view Ivo Beltchev's online profile.


 

 

posted on 2007-04-04 10:57 杨粼波 阅读(1068) 评论(0)  编辑 收藏 引用


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理