Visual Basic 6 Win32 API Tutorial, Page 4
Remember in Chapter 1 when I said that I'd get back to this function? Well, now is the time to reveal what the big stink about this function was. The function itself isn't the problem; it's the declaration from the API Viewer that causes it. If you pasted the declaration from the version supplied in VB 4.0 because you wanted to read INI files, you got something like this:
Declare Function GetPrivateProfileString Lib "kernel32" _ Alias "GetPrivateProfileStringA" (ByVal lpApplicationName _ As String, lpKeyName As Any, ByVal lpDefault As String, _ ByVal lpReturnedString As String, ByVal nSize As Long, _ ByVal lpFileName As String) As Long
Many VB programmers started to have a lot of problems in using this call, and if you look at the declaration hard enough, you'll see why. It's that second parameter that is causing all the problems. If you look at Microsoft's documentation for this parameter, you'll see this definition of lpKeyName:
Pointer to the null-terminated string containing the key name whose associated string is to be retrieved. If this parameter is NULL, all key names in the section specified by the lpAppName parameter are copied to the buffer specified by the lpReturnedString parameter.
If the parameter is a string value, why isn't it passed in ByVal like all the others? The hitch is when you have to pass in a null value. Declaring a string like this:
Dim strNull as String strNull = ""
does not make the string null as far as a DLL function is concerned! VB has added a string constant called vbNullString, which you should use when you need to pass in a null value to a string parameter. Microsoft posted the fix on their web site, which said that the declaration should be (incidentally, this is the declaration you will get if you use the API Viewer with Visual Basic 6):
Declare Function GetPrivateProfileString _ Lib "kernel32" Alias "GetPrivateProfileStringA" _ (ByVal lpApplicationName As String, _ ByVal lpKeyName As Any, ByVal lpDefault _ As String, ByVal lpReturnedString As _ String, ByVal nSize As Long, _ ByVal lpFileName As String) As Long
However, I disagree with this declaration, since the data type declaration for the lpKeyName argument has been left as As Any. The function's declaration clearly states that the argument expects a pointer to a null-terminated string; so why would anyone want to pass in a UDT or Long, etc. to this function? I usually declare GetPrivateProfileString like this:
Declare Function GetPrivateProfileString _ Lib "kernel32" Alias _ "GetPrivateProfileStringA" (ByVal _ lpApplicationName As String, ByVal _ lpKeyName As String, ByVal lpDefault As String, _ ByVal lpReturnedString As String, _ ByVal nSize As Long, _ ByVal lpFileName As String) As Long
It makes my life a lot easier when I use it this way!
I think the reason why this mistake got more publicity than all of the other bugs that VB has had is that a lot of programmers needed to manipulate INI files. Therefore, this call probably got more use than, say, GetProcAddress. Whenever you make an API call, it's a good idea to find out exactly what the DLL is expecting. Check the SDK from Microsoft whenever possible - it might save you a lot of pain when you're trying to find a very subtle bug with parameter data types.