From: Giovanni Dicanio on
Tom Serface wrote:
> I believe countof gives you the size of the array regardless of the size
> of the items in the array whereas sizeof gives you the sizeof the item.
> I think in the case of OP's function it wants the length of the buffer
> in bytes rather than chars. That's why I used sizeof in this case.

Reading the prototypes of SQLGetInstalledDrivers in the header file
<odbcinst.h> (in VS2008):

BOOL INSTAPI SQLGetInstalledDrivers
(
__out_ecount(cchBufMax) LPSTR lpszBuf,
WORD cchBufMax,
WORD* pcchBufOut
);


BOOL INSTAPI SQLGetInstalledDriversW
(
__out_ecount(cchBufMax) LPWSTR lpszBuf,
WORD cchBufMax,
WORD* pcchBufOut
);


It is clear that the function SQLGetInstalledDrivers() wants the size of
buffer in *TCHAR's*; in fact, the Hungarian prefix 'cch' (instead of
'cb'), is used and tells us that the TCHAR count is expected.

So, Mihai was correct in suggesting the use of _countof() instead of
sizeof(), to make the code Unicode aware.

The real problem here is the wrong MSDN documentation

http://msdn.microsoft.com/en-us/library/ms714005.aspx

that uses this misleading ANSI-only prototype:

BOOL SQLGetInstalledDrivers(
LPSTR lpszBuf,
WORD cbBufMax,
WORD * pcbBufOut);

I think that Tom was mislead by this documentation (if I read 'cb'
prefix, I think of sizeof(), too, like Tom did; instead, if I read 'cch'
prefix, I think about _countof()).

Giovanni
From: Giovanni Dicanio on
Joseph M. Newcomer wrote:

>> // Get the name of the Excel-ODBC driver
>> void CSpreadSheet::GetExcelDriver()
>> {
>> char szBuf[2001];
>> WORD cbBufMax = 2000;
>> WORD cbBufOut;
>> char *pszBuf = szBuf;
> ****
> Apparently due to terminal brain damage on the part of the documentation team, the
> documentation specifies this as an LPSTR, and it should be an LPTSTR.

Yes, that documentation is wrong, and also the use of the 'cb' size
prefix in MSDN online prototype is wrong and misleading, IMHO:

http://msdn.microsoft.com/en-us/library/ms714005.aspx

Note that in the VS2008 header file <odbcinst.h> they got it right,
using 'cch' prefix.

> Also, weirdly, it uses a WORD to define the length. Didn't anyone in the SQL group read
> that Win32 is now out?

:-))

Probably they wanted to put a constraint to the size of the buffer,
making it maximum of 2^16 = 65,536 TCHAR's.


> A more serious question is why you have chosen random numbers like 2001, 2000, etc.
>
> To handle this correctly, you would typically do something like
> [...]
>
> Assuming that any randomly-chosen value will be correct is not safe. The above loop is
> the standard pattern for an API that can truncate without giving an error.

Considering the choice of WORD to express buffer length, the maximum
size of the buffer is 64K (i.e. 65,536) TCHAR's, so I would just
allocate a buffer of this size, and pass it to the API.

I think that 64KB (or 128KB in case of Unicode build) is fine to
allocate in these days of GigaBytes :)

So, the code logic is simpler and there is no need for the loop:

TCHAR szBuf[ 64*1024 ]; // 64K TCHAR buffer
WORD cchBufMax = _countof(szBuf) - 1;

Giovanni
From: Giovanni Dicanio on
hamishd wrote:

> This is the entire function:
>
> // Get the name of the Excel-ODBC driver
> void CSpreadSheet::GetExcelDriver()
> {

I tried to modified your code and it seems to work (also in Unicode
builds)...
Here it is a compilable function GetExcelDriver(), that returns empty
string on error, or the required string on success:

<code>

// Gets the name of the Excel-ODBC driver.
// Returns empty string on error.
CString GetExcelDriver()
{
TCHAR szBuf[64*1024];
WORD cchBufMax = _countof(szBuf) - 1;
WORD cchBufOut;
TCHAR *pszBuf = szBuf;

// Get the names of the installed drivers ("odbcinst.h" has to be
included )
if(!SQLGetInstalledDrivers(szBuf,cchBufMax, &cchBufOut))
{
// Error
return _T("");
}

// Search for the driver...
do
{
if( _tcsstr( pszBuf, _T("Excel") ) != NULL )
{
// Found !
return CString( pszBuf );
}
pszBuf = _tcschr( pszBuf, _T('\0') ) + 1;
}
while( pszBuf[1] != _T('\0') );

// Not found - return empty string
return _T("");
}

</code>

You can make it a static member function of your class, and call it like
this:

...
m_sExcelDriver = GetExcelDriver();
...

However, reading the code you linked from CodeProject, it seems to me
that there are other problems in that code (that seems to be written for
ANSI-only builds, it seems to me not Unicode aware).

If you want to learn how to properly use CString class and write
Unicode-aware code, you may want to read this article by Joe on CodeProject:

http://www.codeproject.com/KB/string/cstringmgmt.aspx

(Probably you will find also similar articles on Joe's own site).

Giovanni

From: Tom Serface on
I was looking at the documentation. I apologize. I didn't think I had to
read into the code to get the actual scoop and from that prototype I agree
totally that it looks like it's asking for number of characters. In the
doco it said "size of the buffer" and didn't even list a wide version at
all. I wonder if anyone has ever used or tested these functions :o)

Tom

"Giovanni Dicanio" <giovanniDOTdicanio(a)REMOVEMEgmail.com> wrote in message
news:eE5xEuXcJHA.552(a)TK2MSFTNGP06.phx.gbl...

> Reading the prototypes of SQLGetInstalledDrivers in the header file
> <odbcinst.h> (in VS2008):
>
> BOOL INSTAPI SQLGetInstalledDrivers
> (
> __out_ecount(cchBufMax) LPSTR lpszBuf,
> WORD cchBufMax,
> WORD* pcchBufOut
> );
>
>
> BOOL INSTAPI SQLGetInstalledDriversW
> (
> __out_ecount(cchBufMax) LPWSTR lpszBuf,
> WORD cchBufMax,
> WORD* pcchBufOut
> );
>
>
> It is clear that the function SQLGetInstalledDrivers() wants the size of
> buffer in *TCHAR's*; in fact, the Hungarian prefix 'cch' (instead of
> 'cb'), is used and tells us that the TCHAR count is expected.
>
> So, Mihai was correct in suggesting the use of _countof() instead of
> sizeof(), to make the code Unicode aware.
>
> The real problem here is the wrong MSDN documentation
>
> http://msdn.microsoft.com/en-us/library/ms714005.aspx
>
> that uses this misleading ANSI-only prototype:
>
> BOOL SQLGetInstalledDrivers(
> LPSTR lpszBuf,
> WORD cbBufMax,
> WORD * pcbBufOut);
>
> I think that Tom was mislead by this documentation (if I read 'cb' prefix,
> I think of sizeof(), too, like Tom did; instead, if I read 'cch' prefix, I
> think about _countof()).
>
> Giovanni

From: Tom Serface on
Is it possible that this could have changed in VS 2008? I wonder if that is
why the doco is just out of date?

Tom

"Giovanni Dicanio" <giovanniDOTdicanio(a)REMOVEMEgmail.com> wrote in message
news:OFkcjyXcJHA.4684(a)TK2MSFTNGP03.phx.gbl...

> Yes, that documentation is wrong, and also the use of the 'cb' size prefix
> in MSDN online prototype is wrong and misleading, IMHO:
>
> http://msdn.microsoft.com/en-us/library/ms714005.aspx
>
> Note that in the VS2008 header file <odbcinst.h> they got it right, using
> 'cch' prefix.
>
>> Also, weirdly, it uses a WORD to define the length. Didn't anyone in the
>> SQL group read
>> that Win32 is now out?
>
> :-))
>
> Probably they wanted to put a constraint to the size of the buffer, making
> it maximum of 2^16 = 65,536 TCHAR's.
>
>
>> A more serious question is why you have chosen random numbers like 2001,
>> 2000, etc.
>>
>> To handle this correctly, you would typically do something like
>> [...] Assuming that any randomly-chosen value will be correct is not
>> safe. The above loop is
>> the standard pattern for an API that can truncate without giving an
>> error.
>
> Considering the choice of WORD to express buffer length, the maximum size
> of the buffer is 64K (i.e. 65,536) TCHAR's, so I would just allocate a
> buffer of this size, and pass it to the API.
>
> I think that 64KB (or 128KB in case of Unicode build) is fine to allocate
> in these days of GigaBytes :)
>
> So, the code logic is simpler and there is no need for the loop:
>
> TCHAR szBuf[ 64*1024 ]; // 64K TCHAR buffer
> WORD cchBufMax = _countof(szBuf) - 1;
>
> Giovanni