Could somebody explain how to determine when to use _OFFSET and when it should be _UNSIGNED?
First of all, I don't think it really makes much difference whether it is _UNSIGNED or not. Even if you do arithmetic on it, I don't think QB64 cares about integer overflow, so I don't think it makes any difference. I use _UNSIGNED out of preference.
The short answer:
_OFFSET means a pointer. Use it any time that you need to pass or receive a pointer. With the API, types that start with P or LP, and parameters that start with p or lp are generally pointers.
The long answer:
As for when to use the _OFFSET data type, it is basically any time that you need to pass a pointer to a function as a parameter, or have a pointer returned from a function as a return value. The _OFFSET type is basically the same as a void pointer in C. A pointer is a variable that holds a memory address. (I sometimes refer to the memory address itself as being a pointer, but usually, "pointer" means a variable that holds a memory address.) The symbol for a pointer in C is the asterisk, *,
following some other type name. So for example:
void *
means a void pointer, that is, a pointer to a variable of any type.
Also, for example:
CHAR *
means a pointer to a CHAR.
So, you would look at the function prototype in the MSDN article, and match the data types of all of the parameters and the return value to QB64 data types. _OFFSET is the type to use for any kind of pointer. MSDN has a
list of data types:
http://msdn.microsoft.com/en-us/library/aa383751%28v=vs.85%29.aspxYou can look up the types in that article. Note that many types are derived from other types, so you may have to follow the chain back, looking up each type that the other type was derived from, until you get back to a standard type like int or void*. Deriving types in C is accomplished with typedef. (Just as a note: the current version of that article on the web seems to have a problem, where the #ifdef UNICODE blocks aren't displaying properly, at least in my browser. I am going by an older version of that article that I saved.)
For example, let's look at the QueryDosDevice function that is used above.
http://msdn.microsoft.com/en-us/library/aa365461%28v=VS.85%29.aspxDWORD WINAPI QueryDosDevice(
__in_opt LPCTSTR lpDeviceName,
__out LPTSTR lpTargetPath,
__in DWORD ucchMax
);
So, the function returns a DWORD, and accepts 3 parameters: an LPCTSTR, an LPTSTR, and a DWORD.
For the return type:If the return type were void, it would be a SUB. Any return type other than void means a FUNCTION. Look up "DWORD" in the data type list. (The URL is given above.)
A 32-bit unsigned integer. The range is 0 through 4294967295 decimal
This type is declared in WinDef.h as follows:
typedef unsigned long DWORD;
Obviously, the QB64 type that corresponds to this is _UNSIGNED LONG.
QueryDosDeviceW (Unicode) and QueryDosDeviceA (ANSI)
In this instance, I chose to use the ASCII version of the function (QueryDosDeviceA) instead of the Unicode version (QueryDosDeviceW) for simplicity. (A stands for ANSI, W stands for wide, as in wide char.)
So, we have:
FUNCTION QueryDosDeviceA~& (
For the first parameter:Look up LPCTSTR in the data type list:
An LPCWSTR if UNICODE is defined, an LPCSTR otherwise. For more information, see Windows Data Types for Strings.
This type is declared in WinNT.h as follows:
#ifdef UNICODE
typedef LPCWSTR LPCTSTR;
#else
typedef LPCSTR LPCTSTR;
#endif
As noted above, I have chosen to use the ASCII version of the function, so it corresponds to LPCSTR. Look that up:
A pointer to a constant null-terminated string of 8-bit Windows (ANSI) characters. For more information, see Character Sets Used By Fonts.
This type is declared in WinNT.h as follows:
typedef __nullterminated CONST CHAR *LPCSTR;
This tells us that it is a pointer to a string. Since it is a pointer, the QB64 type it corresponds to is _OFFSET.
By the way, all parameters, including pointers, will be passed BYVAL. So, we have:
FUNCTION QueryDosDeviceA~& (BYVAL lpDeviceName AS _UNSIGNED _OFFSET,
For the second parameter:Look up LPTSTR in the data type list:
LPTSTR
An LPWSTR if UNICODE is defined, an LPSTR otherwise. For more information, see Windows Data Types for Strings.
This type is declared in WinNT.h as follows:
#ifdef UNICODE
typedef LPWSTR LPTSTR;
#else
typedef LPSTR LPTSTR;
#endif
Again, I decided not to use Unicode this time, so look up LPSTR:
A pointer to a null-terminated string of 8-bit Windows (ANSI) characters. For more information, see Character Sets Used By Fonts.
This type is declared in WinNT.h as follows:
typedef CHAR *LPSTR;
Again, it is a pointer to a string. Again, pointers correspond to _OFFSET.
FUNCTION QueryDosDeviceA~& (BYVAL lpDeviceName AS _UNSIGNED _OFFSET, BYVAL lpTargetPath AS _UNSIGNED _OFFSET,
For the third parameter:This is a DWORD. We have already looked up DWORD, and we know it is an _UNSIGNED LONG. We have finished our function declaration:
FUNCTION QueryDosDeviceA~& (BYVAL lpDeviceName AS _UNSIGNED _OFFSET, BYVAL lpTargetPath AS _UNSIGNED _OFFSET, BYVAL ucchMax AS _UNSIGNED LONG)
Based on the above example, it should be trivial to figure out the declaration for GetLastError:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms679360%28v=vs.85%29.aspxDWORD WINAPI GetLastError(void);
The void in the parameter list means that it accepts no parameters. For the return value, we already know that a DWORD is an unsigned long:
FUNCTION GetLastError~& ()
Usage:Now that I have declared the function, notice how I use it.
The first usage:IF QueryDosDeviceA~&(0, _OFFSET(buffer), sizeofbuffer) = 0 THEN
The first parameter:lpDeviceName [in, optional]
An MS-DOS device name string specifying the target of the query. The device name cannot have a trailing backslash; for example, use "C:", not "C:\".
This parameter can be NULL. In that case, the QueryDosDevice function will store a list of all existing MS-DOS device names into the buffer pointed to by lpTargetPath.
lpDeviceName is an optional pointer to a null terminated ASCII string holding the device name. In this case, I want a list of all of the DOS device names, so I give it a null pointer, 0. This is in harmony with: "If lpDeviceName is NULL, the function retrieves a list of all existing MS-DOS device names." This is why it is referred to as optional, because the pointer can be left null, 0.
The second parameter:lpTargetPath [out]
A pointer to a buffer that will receive the result of the query. The function fills this buffer with one or more null-terminated strings. The final null-terminated string is followed by an additional NULL.
If lpDeviceName is non-NULL, the function retrieves information about the particular MS-DOS device specified by lpDeviceName. The first null-terminated string stored into the buffer is the current mapping for the device. The other null-terminated strings represent undeleted prior mappings for the device.
If lpDeviceName is NULL, the function retrieves a list of all existing MS-DOS device names. Each null-terminated string stored into the buffer is the name of an existing MS-DOS device, for example, \Device\HarddiskVolume1 or \Device\Floppy0.
lpTargetPath is a pointer to a buffer to receive the data as null terminated ASCII strings. I give it a pointer to a buffer, _OFFSET(buffer).
The third parameter:ucchMax [in]
The maximum number of TCHARs that can be stored into the buffer pointed to by lpTargetPath.
We need to know what a TCHAR is, since it is involved both in ucchMax and in the return value. Look up TCHAR in the data type list:
A WCHAR if UNICODE is defined, a CHAR otherwise.
This type is declared in WinNT.h as follows:
#ifdef UNICODE
typedef WCHAR TCHAR;
#else
typedef char TCHAR;
#endif
Since we are not using Unicode this time, it is a char, a byte of an ASCII string. (Note that if we were using Unicode, a TCHAR would be two bytes, as you would find out by looking up WCHAR. Keep this in mind if you ever use Unicode functions, as buffers will need two bytes per character in that instance.)
ucchMax is an unsigned long specifying how large the buffer is. I give it an unsigned long, sizeofbuffer, containing the size of buffer in TCHARs.
It is very important that you
don't report a size larger than the buffer actually is. The function has no other way of knowing how large the buffer is, and could overwrite other data if you specify a size that is too large. (Again, if you ever use a Unicode function, you will need 2 bytes per character, so the number of TCHARs would be LEN(buffer)\2.)
Return value:The function returns an unsigned long.
If the function succeeds, the return value is the number of TCHARs stored into the buffer pointed to by lpTargetPath.
If the function fails, the return value is zero. To get extended error information, call GetLastError.
If the buffer is too small, the function fails and the last error code is ERROR_INSUFFICIENT_BUFFER.
So, that is what I test for. If the return value is 0, then I check the error code using GetLastError~&. If the error code is ERROR_INSUFFICIENT_BUFFER, 0x7a, then I try again with a larger buffer.
After a successful call, buffer, the buffer pointed to by lpTargetPath, will contain a list of all of the DOS device names, each null terminated, with an extra null at the end of the list.
The second usage:IF QueryDosDeviceA~&(_OFFSET(comports(i * 2)), _OFFSET(buffer), sizeofbuffer) = 0 THEN
The difference this time is in the first parameter. By now I have gone through the list of all device names, and have picked out the ones that start with "COM". I now want to find the current mapping for each of them. lpDeviceName is a pointer to a null terminated ASCII string containing the device name. So I give it _OFFSET(comports(i * 2)), which is exactly that. (As I said in the original post, I leave a terminating null until after this call.)
After a successful call, buffer, the buffer pointed to by lpTargetPath, will contain one or more null terminated strings, with an extra null after the last. I am only interested in the first of these, which is the current mapping.
I hope that this answers the question, and adequately demonstrates how to use the Windows API from QB64. If not, inquire further, and I or someone else will try to answer it. Please point out any mistakes.
Regards,
Michael