I've noticed that a lot of VB developers are asking questions about how to convert the declares they're used to using into ones they can use in RB. But this article isn't helpful for just VB users -- it can help someone who's never used a declare in their life (or they've found some VB declares online and want to understand how to convert them).
Let's start out by discussing the syntax of an RB declare.
Declare Sub|Function FunctionCallName Lib "LibraryName" [Alias "AliasName"] ( [[ByRef|ByVal]param1 as Type], [[ByRef|ByVal]param2 as Type], [...] ) as ReturnType
The things in the [] are all optional, so you don't need them. So let's break this apart piece by piece.
Declare is the way the RB compiler knows that this is a special piece of code that lives somewhere on the end-user's system. The Sub/Function specifier tells the compiler what type of method call this is. If it doesn't return a value, then it's a Sub. If it does return one, then it's a Function. Then comes the function's name. This is the name serves a dual purpose. It tells the compiler what function needs to be loaded when your application first starts up, and it tells the compiler what name you will use to call the function by. The FunctionCallName needs to be the exact same as what the system knows the function by if you are not using the Alias specifier. This is important, and I'll get into it more later. The Lib keyword followed by the LibraryName specifier tells the compiler what library (in the case of Windows, the librarys are all DLLs) the FunctionCallName is exported from. You can optionally include an Alias specifier in your declares, which is useful when you need to "rename" a declared function. Let's say your class has a method named Foobar already, and you want to call a declare also named Foobar. Well, in that case, you use "Foobar" as the Alias (since that's the function name for the API call), and use MyFoobar as the FunctionCallName. This allows you to distinguish between your class method "Foobar" and your declare "MyFoobar" (meaning that when you want to call into the declare, you use the FunctionCallName: MyFoobar). Finally, after all that jazz comes the parameter list and the return type. If you have a return type, then you need to remember to use Declare Function instead of Declare Sub.
Phew! That was a lot of information just about the structure of a declare. So at what point does an RB declare differ from a VB declare? Let's run down a list of common differences you will run into.
1) In VB, every parameter is passed ByRef by default. RB works differently in that every parameter is passed ByVal by default. This means that when you look at a VB declare you find somewhere and see parameters like ByVal foo as Integer, you can leave the ByVal off when writing the RB declare. Of course, leaving it on doesn't hurt anything, but it's just something to keep in mind. More important is the inverse -- if you see a VB declare with the ByVal left off, then you will need to remember to put ByRef in front of your parameter (this is assuming the VB declare is correctly using ByRef and not just forgetting to put ByVal in front of the parameter).
2) Many VB declares use parameters of type Long. REALbasic doesn't have the Long data type -- it uses Integer. So any time you see Long, just replace it with Integer and you're fine.
3) VB declares have the concept of Public and Private which doesn't translate into RB. If you want to make a declare public, you place a method in a module, give it global scope, and put the declare inside of it. Basically, in RB, declares are scoped like variables -- they are only valid inside the method they are declared in.
4) Any time you are going to pass a String variable into a declare, it needs to be declared properly (it must have the proper Type). Typically, on Windows, that type will be CString. So, for the function FindWindow, which takes two strings and returns an Integer, you would pass in CString as the Type for the parameters. The only time when you won't be passing in a CString is when you use the Wide Char (W version) of an API call (more on this in a second), in which case you need to pass in a UTF-16 string to the function. In this case, you declare the parameters to be type Ptr, and pass in a UTF-16 string stuffed into a MemoryBlock.
5) RB does not allow some constructs that VB allows. You cannot use a declare with ParamArray or arrays (tho you can accomplish the latter using MemoryBlocks).
6) In VB declares, you'll sometimes find weird suffixes to the function name or parameter names. These suffixes take the place of the As Type specifier in REALbasic. If you see Foobar&, then that translates into Foobar as Integer. The other suffixes to types are as follows:
& --> Integer
$ --> String
% --> Short
# --> Double
! --> Single
Remember how I told you that FunctionCallNames are important and we'd talk about them more later. Well, it's later, so let's talk about this a bit. One very important thing to keep in mind is that the FunctionCallName is case-sensitive. You can't just type in any-old random case for the function name -- it must match what's being exported from the DLL exactly. The other "gotcha" is because of the MS convention of splitting Unicode-savvy and non-Unicode-savvy functions up and designating which is which by the name. Any function that takes a string (including ones that have strings in structures) will typically have two variations of the function. So MSDN will call the function FindWindow (for example), but the exported FunctionCallName is either FindWindowA or FindWindowW (for the ANSI or Wide Char version, respectively). So you need to make sure your FunctionCallName matches, meaning you'll need to append an A or a W onto the function as appropriate.
So let's take a VB declare and form it into an RB declare.
Private Declare Function FindWindow Lib "User32" Alias "FindWindowA" (ByVal lpClassName as String, ByVal lpWindowName as String) as Long
Breaking this apart... we don't need to use the Private keyword, all Longs become Integers and we need to specify what type of strings are being used. So that turns this into:
Declare Function FindWindow Lib "User32" Alias "FindWindowA" ( lpClassName as CString, lpWindowName as CString ) as Integer
You'll notice how we use Alias to declare what the _actual_ function's link name is (what's exported from the DLL), but use FindWindow as the function name that we will actually call.
There are other difference between VB declares and RB declares, but this article covers the most common differences. If you run into other differences that you think should be included in this white paper, then please email me at aaron@realsoftware.com
VB To RB Declare Converter If you are interested in downloading a small project I wrote that will convert most VB declares into an RB declare, then you should check out the following:The Win32 version of the application
The Carbon version of the application
The source code
Note that this software comes with no guarantees and no support. It is an open source project; you are welcome to use it to whatever end you'd like. If you improve the application, I would appreciate it if you sent me your changes so that everyone can benefit. This project is not supported by REAL Software, it's just a side project I decided to take on.
Version History
Version 1.0 -- Aug 18 2004Initial check-in. This version should support most VB declares, but it's entirely possible that there are things that should work which don't, or things that shouldn't work but do.
