Unicode API Functions
I’ve been assuming that you want to use only the ANSI versions of API functions, but if all your customers are running Windows NT, you can increase the efficiency of your programs by using Unicode functions. There are several ways to do this. One hard way is to fake Unicode strings by using byte arrays in Declare statements. The other hard way is to use a real pointer to the internal Unicode data. The easy way is to use a type library. Try the hard ways first so that you’ll appreciate the easy way.
There’s another reason to use Unicode functions. All COM API functions are Unicode by definition. You must define these as Unicode even if you’re dealing with Windows 95. Let’s take an example. REGTLB is a command-line program that can register type libraries. It calls the LoadTypeLib and RegisterTypeLib functions, which are COM functions in OLEAUT32.DLL. Both these functions are defined as Unicode functions in the Windows API type library. You can simply call them from Visual Basic and they’ll do the right thing. Of course, one of the type libraries you might want to register is WIN.TLB. Catch 22: you can’t register a type library using functions that are defined in that type library.
So let’s take a look at two ways of defining LoadTypeLib (RegisterTypeLib is similar). After cleanup of extraneous macros, the C definitions of LoadTypeLib and RegisterTypeLib look like this:
HRESULT LoadTypeLib(LPCWSTR szFile, ITypeLib ** pptlib);
HRESULT RegisterTypeLib(ITypeLib * ptlib, LPWSTR szFullPath, _
LPWSTR szHelpDir);
An HRESULT is a COM error type that is closely related to Visual Basic’s Err object. Any function that returns an HRESULT will recognize Basic-style error traps with On Error. In other words, to Visual Basic, HRESULT isn’t really a return value and LoadTypeLib and RegisterTypeLib aren’t really functions. It’s as if they were defined like this:
Sub LoadTypeLib(szFile As String, pptlib As ITypeLib)
Sub RegisterTypeLib(ByVal ptlib As ITypeLib, szFullPath As String, _
szHelpDir As String)
ITypeLib is a standard COM type. I’ll use Object instead. You get late binding instead of early binding, but that’s OK in this example. More on that later.
If you define these functions with Declare statements, you have to fake two things. First, you can’t define the HRESULT type. Bitwise, an HRESULT is a Long, and you’ll have to receive it as a Long and handle errors yourself rather than letting Basic do it for you. Second, you’ll have to come up with some way to pass Unicode strings. Here are two versions of the Declare statements:
#If ordUnicode = ordRawBytes Then
' Receive string arguments as Byte arrays
Private Declare Function LoadTypeLib Lib "oleaut32.dll" ( _
pFileName As Byte, pptlib As Object) As Long
Private Declare Function RegisterTypeLib Lib "oleaut32.dll" ( _
ByVal ptlib As Object, szFullPath As Byte, _
szHelpFile As Byte) As Long
#ElseIf ordUnicode = ordStrPtr Then
' Receive string arguments as pointers
Private Declare Function LoadTypeLib Lib "oleaut32.dll" ( _
ByVal pFileName As Long, pptlib As Object) As Long
Private Declare Function RegisterTypeLib Lib "oleaut32.dll" ( _
ByVal ptlib As Object, ByVal szFullPath As Long, _
ByVal szHelpFile As Long) As Long
#ElseIf ordUnicode = ordTypeLib Then
' No Declare needed!
#End If
Page 88 has some conditional code that uses the Declare or the type library entry depending on how you set the constant ordUnicode.
Function RegTypelib(sLib As String) As Long
#If ordUnicode = ordRawBytes Then
Dim suLib() As Byte, errOK As Long, tlb As Object
' Basic automatically translates strings to Unicode Byte arrays
' but doesn't null-terminate, so you must do it yourself
suLib = sLib & vbNullChar
' Pass first byte of array
errOK = LoadTypeLib(suLib(0), tlb)
If errOK = 0 Then errOK = RegisterTypeLib(tlb, suLib(0), 0)
RegTypelib = errOK
#ElseIf ordUnicode = ordStrPtr Then
Dim errOK As Long, tlb As Object
' Pass pointer to real (Unicode) string
errOK = LoadTypeLib(StrPtr(sLib), tlb)
If errOK = 0 Then errOK = RegisterTypeLib(tlb, StrPtr(sLib), 0)
RegTypelib = errOK
#ElseIf ordUnicode = ordTypeLib Then
Dim tlb As ITypeLib
On Error GoTo FailRegTypeLib
' Real HRESULT and real Unicode strings from type library
LoadTypeLib sLib, tlb
RegisterTypeLib tlb, sLib, sNullStr
Exit Function
FailRegTypeLib:
MsgBox Err & ": " & Err.Description
RegTypelib = Err
#End If
End Function
Notice that in the first two versions you have to treat the procedures as functions and handle the return value. The version that uses the type library is processed like a sub. You use error trapping to deal with the hidden HRESULT. The StrPtr function used in the second version gets a pointer to the real Unicode string and passes the pointer directly. I’ll have more to say about StrPtr in the next section.