WTL Helper
WTL Helper
Introduction
When I started using WTL, I didn't find any tool that could help me add message handlers like the Class Wizard. So, I decided to develop my own add-in for Microsoft Visual Studio (MS VS) that does that. Sorry for my English, I don't know it very well.
Using the Add-in
First, you should download WTL Helper Setup and run it to install the add-in. The add-in automatically registers the shortcut CTRL+ALT+W to call the dialogue. However, if you use one of the predefined keyboard mapping schemes then the registration of the shortcut will fail. Instead, you should create a copy of the current keyboard mapping scheme. To do this, go to the menu Tools->Options. In the Environment section, select Keyboard, press the Save As button and select a name for the new keyboard scheme. Then you can manually set a shortcut for WTL Helper (its full name is WtlHelper.Connect.WtlHelper).
Message Map
If the add-in is installed properly, the WTL Helper sub-menu appears in the menu Tools. Click on WTL Helper and you will get the dialogue shown above. The structure of the left TreeView panel is described in the WTL files. These WTL files are unpacked into the {installation_path}\Messages directory, if you use the setup file, or they are in the \wtl subdirectory in the source archive. The description of these files is as follows. I've grouped these messages by categories for faster search. If you think that this order is inconvenient, you can change the WTL files to have your own view of the message tree.
DDX Map
On the left panel of the dialogue, you can see the control IDs for the selected dialogue. By default, there are only dialogue classes in the combo box. On the right panel, there are variables that correspond to these ID controls.
Options
Other menu item Options allow you to choose the following options:
Message Options
- The style of the message handler is used by default (new or old)
- Sorting of the message handlers (on/off)
- Function-handlers (inline or in CPP)
- Which
CRect
,CPoint
,CSize
classes will be used: WTL or ATL - Handler colours
DDX Options
- Default protection for the DDX variables
- Show only dialogue classes or all classes on the Variables tab
- Use type prefix for DDX variable
More explanation on these options is given below.
Message Options Discussion
- WTL 3.1 and above have a file called atlcrack.h. In this file, there are many message handlers that unpack (crack)
wParam
andlParam
. So, the message handler functions become more laconic. However, there are some troubles because crackers must be used with a message map starting not withBEGIN_MSG_MAP
, but withBEGIN_MSG_MAP_EX
. TheBEGIN_MSG_MAP
macro and cracked handlers are only possible for ATL 7.0 and above, and only for classes inherited fromCWindowIml
orCDialogImpl
. - You can use sorting of message handlers, which is enabled by default. Sorting means that all the new message handlers in the message map will be inserted in the following order:
- Windows message handlers like
WM_CREATE
,WM_DESTROY
, etc - Command message handlers: messages that are part of the
WM_COMMAND
message - Notify message handlers: messages that are part of the
WM_NOTIFY
message - Other message handlers (user-defined messages)
CHAIN_MSG_MAP
- Windows message handlers like
- If it's true, the function is inserted into the header file. Otherwise, the function is inserted into the CPP file.
- ATL 7.0 and above have a file atltypes.h. In this file, classes like
CRect
,CPoint
andCSize
are declared. You can also use the WTL classesCRect
,CPoint
andCSize
. If you check this option, you will use the WTL ones. Otherwise, you will use the ATL ones. If you choose the WTL classes, the file <atlmisc.h> will be included for each header file where the message handler that usesCRect
,CPoint
,CSize
is declared. If you choose ATL classes, the file <atltypes.h> will be included in the same way. - Since version 0.9.9.0, you also have the ability to set up highlighting colours for the handlers. Just press the button and you will see a dialog like this:
Select the colours you like the most and the handlers will be highlighted in that way.
DDX Options Discussion
- Protection type of a variable which will be default for a dialogue that asks for parameters of the new DDX entry.
- If this option is checked, then only dialogue-based classes will be shown in the Variables tab. In other cases, all the classes will be shown.
- If this flag is set, every auto-generated variable name will have a type prefix such as
i
(m_iVar1
) orstr
(m_strVar2
).
Context Menus
Since version 0.9.9.0, I've added a few context menu handlers to make the calling of WTL Helper easy. In Visual Studio 2003, the context menus are MS Office Command Bars. So, I need to know their names to add my menu items. Here are command bar names and where they are called from:
- Class View Item: Class View window
- Resource Editors: all resource editors except the menu editor
- Menu Designer: Menu editor
After loading the add-in, my menu items appear in these context menus.
Class View
I've added two menu items that show the main window of WTL Helper. The only difference is that the first one sets the handler/function page while the second one sets the DDX variable page as default.
Resource Editors
For the accelerator or toolbar button, you can simply add a command handler.
In the above picture, you can see a context menu for a dialogue. The first two items are equal to the Class View ones. To use the last one, you should have WTL Wizards at least version 1.1. WTL Wizards are a set of dialogues for adding WTL classes.
You can simply add a handler or a variable for a control without showing the main window of WTL Helper.
Menu Editor
The same as for accelerator or toolbar buttons, you can simply add a command handler for the menu item.
Dialog Resource View
The same goes for the dialog editor.
Getting Selected Resource Items
I had some troubles when I tried to get the current resource ID and the selected resource item. The Visual Studio add-in framework has no documented ways to get such information. The most difficult was to determine the resource that is currently being edited and its ID. I've done this not in the correct way (I don't know the correct one), but I have found a way out. You can see that the resource editing window has a caption that consists of the resource file name, ID of the currently edited resource and the type of the resource. So, I use this caption to get all that I need.
The next question is how to retrieve the ID of the currently edited resource item (menu item, control on the dialogue and so on). You can get the EnvDTE::SelectionContainer
object from the EnvDTE::SelectedItems
object and then you can get an object for the selected item. However, for resource items there is no such documentation. These objects are defined in ResEditPKGLib
. To use them, you should import TLB with GUID {7365C6FE-4191-476B-A3FE-1CB6A7B1C119} as follows:
#import "libid:7365C6FE-4191-476B-A3FE-1CB6A7B1C119"
version("7.1") lcid("0") no_implementation
Every resource item object has a property ID, but there is no common interface for the objects. So, I use the Invoke
method of the IDispatch
interface to retrieve the current resource item ID.
Override Functions
Since version 0.9.9.0, I've added the possibility to override some of the functions of the parent classes. In the picture above, you can see the menu for a child of the CScrollWindowImpl
class. The first two functions are available for all of the classes except dialogue classes. The others are available only for the specified classes. For example, DoScroll
and DoPaint
are available only for the children of CScrollImpl
.
Problems while Working with EnvDTE
The problem is that there is no exact definition of the map. I think that in Microsoft, maps are called objects which start from one of the predefined macros, such as BEGIN_MSG_MAP
, BEGIN_MESSAGE_MAP
and so on. It causes some problems while working with this object of EnvDTE
.
The macro BEGIN_MSG_MAP_EX
is not recognized as the beginning of the map; it is recognized as a function. All of the macros that are used in the declaration of the classes are recognized as functions, as well. But it isn't the main issue. The main problem is that when you create a message map by the macro BEGIN_MSG_MAP_EX
or another map (such as DDX map) and try to insert the function into the source document, it returns an error.
The second problem is the recognition of some of the map entries and the end of the map. For example:
CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>) CHAIN_MSG_MAP(CUpdateUI<CMainFrame>) CHAIN_MSG_MAP(CMessageFilter) CHAIN_MSG_MAP(CIdleHandler<CMainFrame>)
...is recognized as:
CHAIN_MSG_MAP(CFrameWindowImpl<CMainFrame>) CHAIN_MSG_MAP(CMessageFilter) CHAIN_MSG_MAP(CIdleHandler<CMainFrame>)
When I tried to understand this behavior, I found out that the second CHAIN_MSG_MAP
with the parameter templatization class is not recognized (in the example, CUpdateUI<CMainFrame>
). However, if the second CHAIN_MSG_MAP
is CMessageFilter
and the third is CUpdateUI<CMainFrame>
, then CMessageFilter
is recognized properly, but CUpdateUI<CMainFrame>
is not recognized.
When some map entries are not recognized in the map EnvDTE
, the end of the map will not be recognized either. There is one more problem with BEGIN_MSG_MAP_EX
. If the map starts with BEGIN_MSG_MAP_EX
and ends with END_MSG_MAP
, then EnvDTE
cannot recognize any CodeElement
after the map. I've solved this problem in the following way: I've added a macro in stdafx.h.
#define END_MSG_MAP_EX END_MSG_MAP
When I added this macro in the same header as the map, I got the same result as without the macro. However, it works well in stdafx.h.
Using the Code
The main functionality is in the files VSElements.h or VSElements.cpp. It's my superstructure of EnvDTE
concerning CodeElement
interfaces. There are some classes that wrap functions, variables, maps, classes and parameters of functions.
The most important of these wrappers is the map wrapper, VSMap
. I've remade this class to get map entries, since using EnvDTE::CodeElement
(with kind of EnvDTE::vsCMElementMap
) or VCCodeModelLibrary::VCCodeMap
does not give the exact result that we want. These problems have been described above.
I've created a special class, VSMessageMap
, for the message map. I am sorry about the Russian comments in the files, but I don't have enough time to add English ones. All the dialogues that you can see while working are in the \dialog subdirectory. Classes for parsing resources are in the \resources subdirectory.