These are the easiest to start translating, and probably the most wanted in
the case of common controls - mainly because you could take the style
constants such as LVS_EX_FULLROWSELECT and make them do something cool with
the common controls in your projects.
The first thing to note is the // comboex part
of the above line. In C++, two forward slashes (//) are used as a
single-line comment, just like VB's '
character.
#define is actually used to create C++ macros.
VB can't create macros, but when translating from C++ to VB you will usually
translate these lines either to constants or short subroutines. In this case
it's a constant, so you'd translate #define to Public
Const if you were moving this line to a VB module.
Obviously, ICC_USEREX_CLASSES is the name of the constant, so what do we
have so far?
Public Const ICC_USEREX_CLASSES 0x00000200 ' comboex
... But that still won't fly in a Visual Basic project. We've translated the
comment and the #define statement, but what about that strange looking
number? It's got to be the value of the constant, but what would that value
look like in VB?
Answer: It's a hexadecimal number. In C++, all hexadecimals begin
with the characters "0x", followed by the number. If you know how
to specify hexadecimals in VB, then you probably already know what to do
next:
&H00000200
Or, since leading 0's don't display in VB,
&H200
Now the only thing left to do is to insert a "=" character in
between the identifier and the number, and you'll have the final result:
Public Const ICC_USEREX_CLASSES = &H200 ' comboex
As an exercise, see if you can translate the following values:
#define ICC_LISTVIEW_CLASSES 0x00000001 // listview, header
#define ICC_TREEVIEW_CLASSES 0x00000002 // treeview, tooltips
#define ICC_BAR_CLASSES 0x00000004 // toolbar, statusbar, trackbar,
// tooltips
#define ICC_TAB_CLASSES 0x00000008 // tab, tooltips
#define ICC_UPDOWN_CLASS 0x00000010 // updown
#define ICC_PROGRESS_CLASS 0x00000020 // progress
#define ICC_HOTKEY_CLASS 0x00000040 // hotkey
#define ICC_ANIMATE_CLASS 0x00000080 // animate
#define ICC_WIN95_CLASSES 0x000000FF
#define ICC_DATE_CLASSES 0x00000100 // month picker, date picker,
// time picker, updown
#define ICC_USEREX_CLASSES 0x00000200 // comboex
#define ICC_COOL_CLASSES 0x00000400 // rebar (coolbar) control
// IE4 and later controls:
#define ICC_INTERNET_CLASSES 0x00000800
#define ICC_PAGESCROLLER_CLASS 0x00001000 // page scroller
#define ICC_NATIVEFNTCTL_CLASS 0x00002000 // native font control
... That's all fine and good, but what about constants like these?
#define ODT_HEADER 100
#define ODT_TAB 101
#define ODT_LISTVIEW 102
This turns out to be easy, too - it's just that these aren't hexadecimal
numbers, so they would translate directly to:
Public Const ODT_HEADER = 100
Public Const ODT_TAB = 101
Public Const ODT_LISTVIEW = 102
Here is yet another variation on C++ constants:
#define LVN_FIRST (0U-100U) // listview
#define LVN_LAST (0U-199U)
These are unique in that these digits end with a "U". This simply
means that the constant is of type "unsigned long", a data type
that is unsupported by Visual Basic. Does this mean you can't use these
constants? Not at all! An unsigned long and a signed long (which is a Long
in VB) are both stored internally the same way, and the only difference is
the way they are displayed. In this case, all you have to do is remove the
U's from the declaration and add the equal sign:
Public Const LVN_FIRST = (0 - 100) ' listview
Public Const LVN_LAST = (0 - 199)
Sometimes you will also run across numbers with an "L" after them.
They denote a constant of a Long data type, and you can remove these as
well.
How about this set of constants?
#define CDDS_ITEMPREPAINT (CDDS_ITEM | CDDS_PREPAINT)
#define CDDS_ITEMPOSTPAINT (CDDS_ITEM | CDDS_POSTPAINT)
Again, this is another simple translation. The pipe "|" character
is the bitwise OR operator in C++, just like VB's Or
operator is used. Simply change these to read:
Public Const CDDS_ITEMPREPAINT = (CDDS_ITEM Or CDDS_PREPAINT)
Public Const CDDS_ITEMPOSTPAINT = (CDDS_ITEM Or CDDS_POSTPAINT)
You're probably tiring of reading about constants now; don't worry, I'm
done. Let's move on to a more difficult area:
Structures
... Also known as "Types" or "User-defined Types" in
Visual Basic, declared using the Type keyword. Now you're going to have to
do more than insert silly equal signs and delete extraneous letters - you've
got to translate data types as well... and it may be trickier than you think
in some cases.
Let's start with an easy example:
typedef struct tagTBSAVEPARAMSA {
HKEY hkr;
LPCSTR pszSubKey;
LPCSTR pszValueName;
} TBSAVEPARAMSA, FAR* LPTBSAVEPARAMSA;
typedef struct tagTBSAVEPARAMSW {
HKEY hkr;
LPCWSTR pszSubKey;
LPCWSTR pszValueName;
} TBSAVEPARAMSW, FAR *LPTBSAVEPARAMW;
Yow! And this is supposed to be easy? Well, the first thing to take note of
is that we have here two structures that are essentially the same, except
for one point: The first is the TBSAVEPARAMS structure used when compiling
ANSI code, and the second is used for Unicode programs. In fact, all
structures used for ANSI end with the letter A in their names, and
Unicode structures end with a W. Aside from the names, the only
difference is in the type of strings used - ANSI or Unicode. VB won't allow
you to call the Unicode side of the API (although you can accomplish
it if you enjoy making things difficult for yourself), so you don't need the
Unicode versions.
Let's start with the first line:
typedef struct tagTBSAVEPARAMSA {
First of all, convert the typedef struct part
of this line to read Public Type. You're
halfway there already! Now, the name of the structure is buried somewhere in
the rest of the line. Many C++ structures will begin with the word
"tag", but you don't need that part here, so remove it. You can
also get rid of the A at the end of the name, because you don't need
to differentiate between ANSI and Unicode versions of the structure. The
opening curly brace ("{") you can also drop, so the fully
translated line reads:
Public Type TBSAVEPARAMS
The next three lines are the members of this structure:
HKEY hkr;
LPCSTR pszSubKey;
LPCSTR pszValueName;
Unlike Visual Basic, in C++ the data type comes first and the
variable name comes second. So what is the VB equivalent of C++'s
HKEY data type? One thing to always remember when translating is that most
C++ data types that begin with H will be a handle to
something. If you've used the Windows API at all, you know what a handle is;
each Form object (and many controls) in VB have an hWnd property,
which is short for "handle to a Window". The
hWnd property is a Long in VB, and so are all of the variables that you use
to store handles. Therefore, the correct translation for this variable is:
hkr As Long
Similarly, the C++ data type LPSTR and LPCSTR can be
translated to Strings. These types are actually pointers to strings,
but you don't have to worry about that in VB because whenever you send a
structure to the API, VB will convert all Strings in the structure to
pointers to ANSI strings. The other two members of the structure can be
translated as follows:
pszSubKey As String
pszValueName As String
You may have noticed that I didn't mention the LPWSTR type found in the
Unicode version of the structure; these are pointers to Unicode strings, and
since VB converts all strings to ANSI when calling an API function, you
can't very well utilize them without various workarounds. I won't cover
those workarounds in this article, because this is supposed to be a tutorial
for beginners! Other string types you may see are OLECHAR and BSTR. These
are actually what VB's String data type is internally - Unicode - but as
with LPWSTR, they can't be used as ANSI strings can. You'll probably rarely,
if ever, see the LPTSTR data type. You can translate these as you would an
LPCSTR, unless you know that the API call that uses it is only available in
Unicode.
Now we come to the final line in the C++ definition of the structure:
} TBSAVEPARAMSA, FAR* LPTBSAVEPARAMSA;
If you cringe at the thought of deciphering this, then you'll be happy to
know that everything after the closing curly brace ("}") is
meaningless to VB programmers and can be deleted. What about the closing
brace? Simply replace that with the familiar old VB line:
End Type
Congratulations! You've just learned to translate a C++ structure! The
finished product is as follows:
Public Type TBSAVEPARAMS
hkr As Long
pszSubKey As String
pszValueName As String
End Type
All right, you've worked hard. We'll do just one more structure before we
wrap up this section - and it's a big one!
typedef struct {
UINT cbSize;
DWORD dwMask;
int idCommand;
int iImage;
BYTE fsState;
BYTE fsStyle;
WORD cx;
DWORD lParam;
LPSTR pszText;
int cchText;
} TBBUTTONINFOA, *LPTBBUTTONINFOA;
Uh-oh... The structure name isn't up there with the typedef
statement!
Welcome to lesson #2, where things may be slightly different. What you need
to remember is that, as a general rule, when the structure's name isn't at
the top on the first line, it's usually on the bottom line - except that it
doesn't have the word "tag" preceding it. (Although you'll
see that it does have the ANSI A at the end.)
The portion of the bottom line that specifies the structure name you need
is highlighted in red below:
} TBBUTTONINFOA, *LPTBBUTTONINFOA;
Ah, isn't it much better now that you understand? So what does the first
line translate to? ...
Public Type TBBUTTONINFO
Good, I'm glad you got it. Now let's go through a quick table of common C++
data types and their equivalent VB types.
Note: This table applies only to 32-bit compilers. 16-bit compilers,
and even 64-bit compilers, will usually have different translations for
certain data types.
| C++ Data Type |
VB Equivalent |
| short |
Integer |
| WORD |
Integer |
| int |
Long |
| long |
Long |
| UINT |
Long |
| ULONG |
Long |
| DWORD |
Long |
| WPARAM, LPARAM |
Long |
| WMSG, UMSG |
Long |
| HRESULT |
Long |
| BOOL |
Long |
| COLORREF |
Long |
| HWND, HDC, HBRUSH, HKEY, etc. |
Long |
| LPSTR, LPCSTR |
String |
| LPWSTR, OLECHAR, BSTR |
String |
| LPTSTR |
String |
| VARIANT_BOOL |
Boolean |
| unsigned char |
Byte |
| BYTE |
Byte |
| VARIANT |
Variant |
| (Any data type ending with * or **) |
Long |
Using the above table, you can translate just about any C++ structure to
its equivalent VB structure. The last structure we looked at above is fully
translated as:
Public Type TBBUTTONINFO
cbSize As Long
dwMask As Long
idCommand As Long
iImage As Long
fsState As Byte
fsStyle As Byte
cx As Integer
lParam As Long
pszText As String
cchText As Long
End Type
I guess this brings us to the last subject of the article...
Functions
Knowing how to translate C++ function declarations is extremely valuable,
because there are a very large amount of API calls that aren't in VB's API
Viewer add-in.
Here's our first example, a function exported from ComCtl32.dll:
WINCOMMCTRLAPI HWND WINAPI CreateStatusWindowA(LONG style,
LPCSTR lpszText,
HWND hwndParent, UINT wID);
WINCOMMCTRLAPI HWND WINAPI CreateStatusWindowW(LONG style,
LPCWSTR lpszText,
HWND hwndParent, UINT wID);
This call creates a StatusBar control - you won't find the declaration
anywhere around VB! The types you already know how to translate, I'm sure, so
I won't spend a lot of wasted time instructing you on it again.
Once again there are two versions of the declaration: One in ANSI form and one
in Unicode. Let's drop the Unicode one out of the picture and focus on just
ANSI:
WINCOMMCTRLAPI HWND WINAPI CreateStatusWindowA(LONG style,
LPCSTR lpszText,
HWND hwndParent, UINT wID);
First of all what does WINCOMMCTRLAPI mean? Well, it's pretty much just
telling the C++ compiler that the function can be found exported from
ComCtl32.dll. With functions from other DLLs this prefix will be different,
and not all of them make complete sense - sometimes you just have to look up a
function on MSDN to see which DLL it comes from.
Anyway, next comes the return type of the function: HWND. You know by now
that HWND is translated to a Long in VB, and because the return type is HWND
you can be fairly certain that the function will return the hWnd of the
created statusbar, assuming the creation is successful. But I digress... So
what do we have so far?
Public Declare Function CreateStatusWindowA Lib "ComCtl32.dll" _
( ... ) As Long
Now, you ask yourself, how can the arguments (parameters) to this function be
declared on a separate line without a line continuation character? Well, just
in case you didn't know, C++ doesn't need a line continuation character
- the line is automatically continued until the compiler runs across a
semicolon ";" character. Just making sure you know. If you want to
spread this declaration across several lines in VB, you'll need to use VB's
line continuation character, the underscore "_".
Public Declare Function CreateStatusWindowA Lib "ComCtl32.dll" _
(ByVal style As Long, _
ByVal lpszText As String, _
ByVal hwndParent As Long, ByVal wID As Long) As Long
Now maybe you're thinking to yourself, "Hey that's great - now I know how
to translate a C++ function declaration!" Well... not entirely. You see,
there's another gotcha. Do you know the difference between:
WINCOMMCTRLAPI void WINAPI MenuHelp(UINT uMsg,
WPARAM wParam, LPARAM lParam, HMENU hMainMenu,
HINSTANCE hInst, HWND hwndStatus, UINT lpwIDs);
and
WINCOMMCTRLAPI void WINAPI MenuHelp(UINT uMsg,
WPARAM wParam, LPARAM lParam, HMENU hMainMenu,
HINSTANCE hInst, HWND hwndStatus, UINT FAR *lpwIDs);
???
If you said "Yes", then don't be so sure. Both of these declarations
are the same except for the last parameter. The parameter in the second
declaration reads:
UINT FAR *lpwIDs
This second declaration is the correct one, but do you know what should be
changed in your VB translation when a data type is declared as a pointer
("*" or "FAR *")? Unless you've done this
before, probably not. But here's a hint: in VB, you know of pointers as being
a reference to a variable. Get it yet?
Answer: Most parameters you run across should be declared ByVal -
but you need to leave out ByVal if the parameter is a pointer. (In other
words, it's ByRef.)
BUT! As always, there is an exception to the rule. That exception comes
in when the pointer parameter is a string. A string pointer should always
be declared as ByVal, unless it is defined as a "pointer to a
pointer", or using two asterisks ** instead of just one *. In the case of
two asterisks, the translation should be a ByRef ... As Long parameter,
and the Long value passed should equal StrPtr(MyStringVar).
(For those of you that don't know, StrPtr is an undocumented VB function which
returns the pointer to the beginning of a string.)
Another thing to point out is that MenuHelp's return data type is declared as void.
This means that the call has no return value, in other words, it is a Sub
instead of a Function.
Therefore, the MenuHelp sub (not function) should be translated thusly:
Public Declare Sub MenuHelp Lib "ComCtl32.dll" _
(ByVal uMsg As Long, _
ByVal wParam As Long, _
ByVal lParam As Long, _
ByVal hMainMenu As Long, _
ByVal hInst As Long, _
ByVal hwndStatus As Long, _
lpwIDs As Long)
WHEW! Are your eyes getting tired from reading all of this? Or maybe you took
a break or two. In any case, I certainly hope you've learned a lot from this
tutorial, and it's been fun writing it.
There's still a lot left to learn about C++, but this will definitely get
you on your way. Maybe someday you'll even want to learn how to write programs
in C++... but that feat is left to another tutorial at another time.
Ben Baird, Webmaster, Visual Basic Thunder