Login | Register
 
Contents

  About...
  Articles
  Source Code (coming)
  Software
 
  Contact
 
 

How to Read C++


Remember when the Rebar control was first discovered by the VB world? Remember how everyone wanted that awesome look that Internet Explorer boasted over all other applications?
And even more... Remember how a few resourceful VB programmers grabbed the C++ definitions for this cool new control, translated them to VB, and made you really envious because you couldn't do the same?
If so... isn't it about time you did something about it? You don't have to learn C++ to be able to translate header files to VB! (Although it can help in some situations.) This tutorial will not teach you how to program in C++, but what it will do is give you the tools you need to start translating from C++ to VB.

Well, enough introduction. If you're not interested then by all means, go and browse another section of the site. But if you've ever wished that you didn't have to wait for someone else to do the translating for you, then read on! Now, to our first lesson:


Note: C++ examples for this tutorial are excerpts from CommCtrl.h, the C++ header file for the Windows Common Controls API.

Constants

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.

Our first example is here:


#define ICC_USEREX_CLASSES   0x00000200 // comboex

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


Copyright © 1997-2005, Ben Baird
All Rights Reserved.