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
- dword myfunc(dword x, dword* y, char* z)
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
- Function MyFunc(ByVal lngX As Long, ByRef lngY As Long, ByVal strZ As String) As Long
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
- C Type VB Type Desc Explanation
- HKEY Hive Key Same as DWORD
- DWORD ByVal Long Double Word 32-bit value itself
- LPTSTR ByVal String Long Pointer to String 32-bit pointer to first char of String
- LPDWORD ByRef Long Long Pointer to Long 32-bit pointer to 32-bit value
- NULL ByVal Long Long Pointer to anything *See below
- *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.
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.