Prev: Let 's talk about how to custom draw a window caption bar,any one interesting?
Next: CFileDialog Exception
From: Giovanni Dicanio on 8 Jan 2009 05:24 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 8 Jan 2009 05:32 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 8 Jan 2009 07:03 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 8 Jan 2009 10:20 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 8 Jan 2009 10:21
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 |