423,131 Members | 2,110 Online
Bytes IT Community
Submit an Article
Got Smarts?
Share your bits of IT knowledge by writing an article on Bytes.

VBA to C Crossover

NeoPa
Expert Mod 15k+
P: 30,910
Overview

At their hearts, Visual Basic type languages and c type languages are quite different. In visual Basic type languages you deal with data at a step removed, whereas c type languages leave you down and dirty with the data and how it's referenced. Let me try to explain what I mean by that. To simplify the explanations below I'll refer to Visual Basic type languages simply as VB, and equally the c type languages simply as C. I appreciate that c type languages generally use lower case for such references but here I'm referring to a whole swathe of languages grouped together so I'll proceed to use the upper case for it.


Explanations

In VB a String value is referenced as a single object. The name of the variable references it as an indivisible item. In C, on the other hand, a string is handled by a pointer to the first character of that string. Unless you use the pointer indicator (* in c) any reference to the value of the variable returns, not any character of the string, but simply the address where these characters may be found in sequence in memory. Also in C, the length of a string is determined by the position of the first NullChar character relative to the start position of the string. I use NullChar to mean a single character position with a value of zero. This is quite different from the concept of Nulls as such, whose explanation doesn't fit here. In VB a String is stored as a list of characters but with a separate Word (16-bit) value representing the length of the string. The character list thus has no need for the extra NullChar at the end.

At this point I'll go into a little explanation of how procedures are called in VB and C. Please feel free to comment with corrections if I get any of the details wrong. It's been a long time since I did any serious work in C and never needed as full an understanding in VB. So, here goes...

The first idea to understand is how the Stack is used to pass values and variables to the called procedure from the calling code. To understand this it is necessary to appreciate the difference between those parameters that are used exclusively within the called process itself, and those that are used there but whose resultant values should also be available to the calling one. In it's simplest form those which are needed back again are passed as pointer values and those which aren't, where practical, are passed as the values themselves.

So, in C you may see something like :
Expand|Select|Wrap|Line Numbers
  1. dword myfunc(dword x, dword* y, char* z)
x is a 32-bit numeric value. This is PUSHed onto the Stack first as the value of x.
y is also a 32-bit numeric value - but the procedure is expecting the address of y rather than its value. The address is thus PUSHed onto the Stack next.
z is a String or char pointer. This address is PUSHed onto the Stack last.

In VB you may see something like :
Expand|Select|Wrap|Line Numbers
  1. Function MyFunc(ByVal lngX As Long, ByRef lngY As Long, ByVal strZ As String) As Long
lngX is a 32-bit numeric value. This is PUSHed onto the Stack first as the value of lngX.
lngY is also a 32-bit numeric value - but as it's ByRef the procedure is expecting the address of lngY rather than its value. The address is thus PUSHed onto the Stack next.
strZ is a String. The address is PUSHed onto the Stack last.

I won't go into how literal values (or others defined such that they're not to be updated) are handled in any detail but the compiler ensures that no changes can be made to values which are passed by address even though, otherwise, that could be possible.

When the called procedure receives control it will POP the values off the stack in reverse order and use them as specified in the declaration.

By now you will already have noticed that C treats strings differently so in VB, if you want to use a procedure designed for C, you have to prepare an existing String before passing it across by adding a NullChar at the end. For returned String values you may sometimes be given a length separately, but you'll always have to use something like Left() to assign the correct String value to a variable.


Rules

This is where I'll indicate how to declare something in VB when, as is commonly the case, you're dealing with standard (Non-VB) libraries that expect to interface with C code. An example of the sort of procedure I'm talking about would be RegEnumValue Function. Notice the syntax has a middle column that indicates what type of information is expected. Below I'll try to indicate for each type how it should be called from VB.

NB. Each also has an indicator in the left column which also needs to be taken into consideration as this affects how it needs to be used. You may notice that all that include Out have C Types that start LP - for Long Pointer. This is because the procedure changes the value pointed to and, of course, both caller and callee have access to the same memory pointed to.

Expand|Select|Wrap|Line Numbers
  1. C Type      VB Type         Desc                        Explanation
  2. HKEY                        Hive Key                    Same as DWORD
  3. DWORD       ByVal Long      Double Word                 32-bit value itself
  4. LPTSTR      ByVal String    Long Pointer to String      32-bit pointer to first char of String
  5. LPDWORD     ByRef Long      Long Pointer to Long        32-bit pointer to 32-bit value
  6. NULL        ByVal Long      Long Pointer to anything    *See below
  7. *This is a special case so is treated the same as a DWORD with a value of 0.  This is interpreted as a pointer to 0 - or Null Pointer.
When you pass any form of String across you need to ensure that it's terminated with a NullChar. More importantly, always pay very careful attention to any parameters used for specifying string lengths - even, and especially, for maximum lengths of String values to be returned. When passing a String parameter that the called procedure is expected to fill itself, remember it will expect a buffer to be available into which it can copy its value. It's critical, if you don't want your whole project to crash and burn, that you prepare a space in advance that's large enough to handle whatever can be thrown at it. Sometimes procedures have a parameter whereby you can specify the maximum length of string it should be allowed to use but other times they may not. Be conservative and ensure you always allow plenty. A good way to do this in VB is to say strX = Space(255) before you try to pass it across.


Conclusion

While this appears to be very awkward and complicated, by following some of the simple rules above we can use procedures that were built to interface with C from within VBA.
Feb 3 '18 #1
Share this Article
Share on Google+