RESULTS.C

//*--------------------------------------------------------------------------------- 
//| ODBC System Administrator
//|
//| This code is furnished on an as-is basis as part of the ODBC SDK and is
//| intended for example purposes only.
//|
//| Title: RESULTS.C
//| This module contains functions which allow you to view results via an
//| owner-drawn list box. Having the list box functionality made it easy
//| to implement Pipes, but has some serious draw-backs as far as record
//| limits, storage methods, etc... If you are looking for a great way to
//| paint data in a grid, check out the C++ or Cursor Demo samples that came
//| with this SDK.
//*---------------------------------------------------------------------------------
#include <stdio.h>
#include "errcheck.h"
#include "standard.h"
#include "results.h"
#include "math.h"
#include "ini.h"


//------------------------------------------------------------------------
// Defines
//------------------------------------------------------------------------
VSZFile;


//------------------------------------------------------------------------
// Globals
//------------------------------------------------------------------------
char szErrMsg[100];
dCSEG(char) szONE[] = "1";
dCSEG(char) szZERO[] = "0";
dCSEG(char) szdate[] = "%02u/%02u/%02u";
dCSEG(char) sztime[] = "%02u:%02u:%02u";
dCSEG(char) sztimestmp[] = "%02u/%02u/%02u %02u:%02u:%02u.%lu";
dCSEG(char) szTypeNotFound[] = "Not found";
dCSEG(char) szMaxRowsFetched[] = "Maximum rows fetched. Total rows: %lu";


struct {
SWORD type; // Data type value
LPSTR sztype; // String equivalent
} SqlTypes[] = {
// type sztype
// ------------------- -----------------------------
SQL_BIGINT, "SQL_BIGINT=-5",
SQL_BINARY, "SQL_BINARY=-2",
SQL_BIT, "SQL_BIT=-7",
SQL_CHAR, "SQL_CHAR=1",
SQL_DATE, "SQL_DATE=9",
SQL_DECIMAL, "SQL_DECIMAL=3",
SQL_DOUBLE, "SQL_DOUBLE=8",
SQL_FLOAT, "SQL_FLOAT=6",
SQL_INTEGER, "SQL_INTEGER=4",
SQL_LONGVARBINARY, "SQL_LONGVARBINARY=-4",
SQL_LONGVARCHAR, "SQL_LONGVARCHAR=-1",
SQL_NUMERIC, "SQL_NUMERIC=2",
SQL_REAL, "SQL_REAL=7",
SQL_SMALLINT, "SQL_SMALLINT=5",
SQL_TIME, "SQL_TIME=10",
SQL_TIMESTAMP, "SQL_TIMESTAMP=11",
SQL_TINYINT, "SQL_TINYINT=-6",
SQL_VARBINARY, "SQL_VARBINARY=-3",
SQL_VARCHAR, "SQL_VARCHAR=12",
};


struct {
SWORD type; // Data type value
LPSTR sztype; // String equivalent
} CTypes[] = {
// type sztype
// ------------------- -----------------------------
SQL_C_BINARY, "SQL_C_BINARY=-2",
SQL_C_BIT, "SQL_C_BIT=-7",
SQL_C_CHAR, "SQL_C_CHAR=1",
SQL_C_DATE, "SQL_C_DATE=9",
SQL_C_DOUBLE, "SQL_C_DOUBLE=8",
SQL_C_FLOAT, "SQL_C_FLOAT=7",
SQL_C_LONG, "SQL_C_LONG=4",
SQL_C_SHORT, "SQL_C_SHORT=5",
SQL_C_TIME, "SQL_C_TIME=10",
SQL_C_TIMESTAMP, "SQL_C_TIMESTAMP=11",
SQL_C_TINYINT, "SQL_C_TINYINT=-6",
};

typedef struct tagDATATYPE{
SWORD type; // Data type value
LPSTR sztype; // String equivalent
} DATATYPE;


//------------------------------------------------------------------------
// Local function prototypes
//------------------------------------------------------------------------
void CheckDisplayMode(LPSTR strin, SDWORD cbin, LPSTR strout);
BOOL INTFUN DrawRow(DRAWITEMSTRUCT FAR * dw,
RESULTSSET FAR * rs, int xLeftCol, int xRightCol, BOOL fSelect);


//*------------------------------------------------------------------------
//| CreateResultsSet:
//| This is the first function that should be called to create a
//| results set. When there is no more use for the results set,
//| call DeleteResultsSet to delete it.
//|
//| Parameters:
//| in rs Pointer to a new results set
//| in hwndClient Client window
//| in hInst Instance handle of caller
//| in count How many columns in the results set
//| in szTitle Title for the window
//|
//| Returns:
//| TRUE if there were no errors,
//| FALSE otherwise
//*------------------------------------------------------------------------
BOOL EXTFUN CreateResultsSet(RESULTSSET FAR * rs, HWND hwndClient, HINSTANCE hInst,
int count, LPSTR szTitle)
{
if(!rs ||
count <=0)
return FALSE;

memset(rs, 0, sizeof(RESULTSSET));
rs->cbColumns = count;
rs->hInst = hInst;
rs->hwndClient = hwndClient;
if(*szTitle)
lstrcpy(rs->szTitle, szTitle);

rs->md = (METADATA FAR *)GetMemory(sizeof(METADATA) * count);
if(!rs->md)
return FALSE;

return TRUE;
}


//*------------------------------------------------------------------------
//| SetMetaDataColumn:
//| This function must be called for each column in the results set. The
//| information placed for each row can be obtained using ODBC functions
//| such as SQLDescribeCol and SQLColAttribute.
//| Parameters:
//| in rs Pointer to a results set
//| in iCol Column number
//| in szCol Pointer to column name
//| in szTypeName Data type name
//| in fSqlType ODBC data type number
//| in precision Precision
//| in scale Scale
//| in cbDisplay Display size
//| in fAlign Alignment
//| Returns:
//| Nothing
//*------------------------------------------------------------------------
BOOL EXTFUN SetMetaDataColumn(RESULTSSET FAR * rs, int iCol, LPSTR szCol,
LPSTR szTypeName, SDWORD fSqlType, UDWORD precision, SWORD scale,
int cbDisplay, UINT fAlign)
{
if(!rs ||
iCol < 0 ||
!szCol ||
!*szCol ||
!szTypeName ||
!*szTypeName) {
PostError((LPSTR)szInvalidParms);
return FALSE;
}

rs->md[iCol].szColumnName = (LPSTR)GetMemory(lstrlen(szCol)+1);
if(!rs->md[iCol].szColumnName)
return FALSE;
precision = min(precision, MAXBYTES);
lstrcpy(rs->md[iCol].szColumnName, szCol);
lstrcpy(rs->md[iCol].szTypeName, szTypeName);
rs->md[iCol].fSqlType = fSqlType;
rs->md[iCol].precision = precision;
rs->md[iCol].scale = scale;
rs->md[iCol].cbDisplaySize = cbDisplay;
rs->md[iCol].fAlign = fAlign;
rs->md[iCol].cbOffset = (iCol > 0) ? (UINT)(precision + rs->md[iCol-1].cbOffset) : (UINT)(precision);
++rs->md[iCol].cbOffset; // Room for terminators

return TRUE;
}



//*------------------------------------------------------------------------
//| AllocateRowData:
//| Call this function for each row in the results set to allocate
//| memory and insert a row into the results set.
//| Parameters:
//| in rs Pointer to results set
//| in rd Pointer to a row data structure
//| in cColor Text color
//| in cBkg Background color
//| Returns:
//| Pointer to a ROWDATA structure
//*------------------------------------------------------------------------
ROWDATA FAR * AllocateRowData(RESULTSSET FAR * rs, COLORREF cColor, COLORREF cBkg)
{
ROWDATA FAR * rd;
int dex;

if(!rs) {
PostError((LPSTR)szInvalidParms);
return FALSE;
}
rd = (ROWDATA FAR *)GetMemory(sizeof(ROWDATA));
if(!rd)
return NULL;
rd->textColor = cColor;
rd->bkgrnd = cBkg;
rd->cd = (COLUMNDATA FAR *)GetMemory((sizeof(COLUMNDATA) * rs->cbColumns));
if(!rd->cd)
return NULL;
rd->data = (LPSTR)GetMemory(rs->md[rs->cbColumns-1].cbOffset + 1);
if(!rd->data)
return NULL;
rd->cd[0].szCols = rd->data;
for(dex=1; dex<rs->cbColumns; dex++)
rd->cd[dex].szCols = rd->data + rs->md[dex-1].cbOffset;

return rd;
}



//*------------------------------------------------------------------------
//| SetColumnData:
//| Call this function for a particular column in a ROWDATA structure.
//| If memory has been allocated for the column, it will be freed
//| and reallocated for the new string.
//| Parameters:
//| in icol Which column?
//| in rd Pointer to a row data structure
//| in str Pointer to the new buffer
//| Returns:
//| TRUE if successful
//| FALSE on error
//*------------------------------------------------------------------------
BOOL EXTFUN SetColumnData(int icol, ROWDATA FAR * rd, LPSTR str)
{
if(!str ||
!*str)
return FALSE;

lstrcpy(rd->cd[icol].szCols, str);
return TRUE;
}


//*------------------------------------------------------------------------
//| FreeRowData:
//| Pass a pointer to the ROWDATA structure to free. Obviously since
//| you are asked for a RESULTSSET pointer, you should call this
//| function before freeing the results set data.
//| Parms:
//| in rs Pointer to results set
//| in rd Pointer to row data
//| Returns:
//| Nothing.
//*------------------------------------------------------------------------
void EXTFUN FreeRowData(RESULTSSET FAR * rs, ROWDATA FAR * rd)
{
ReleaseMemory(rd->data);
ReleaseMemory((LPVOID)rd->cd);
ReleaseMemory((LPVOID)rd);
}



//*------------------------------------------------------------------------
//| FreeResultsSet:
//| Call this function to free all of the memory for a results set.
//| Parms:
//| in rs Pointer to results set data to free
//| Returns:
//| TRUE If successful
//| FALSE if there was an error
//*------------------------------------------------------------------------
void EXTFUN FreeResultsSet(RESULTSSET FAR * rs)
{
int dex;

DeleteObject(rs->hFont);

for(dex=0; dex<rs->cbColumns; dex++)
ReleaseMemory(rs->md[dex].szColumnName);
ReleaseMemory(rs->md);
ReleaseMemory(rs);
return;
}



//*------------------------------------------------------------------------
//| CreateResultsFont:
//| This function is called to create a font for the results set. The
//| default font is used if the lf parameter is NULL. Alternatively
//| the user can pass in a complete LOGFONT structure to use for the
//| font.
//| Parms:
//| in rs Pointer to results set to store info
//| in hwnd Window handle to verify font
//| in lf LOGFONT structure to use, NULL for dft
//| Returns:
//| Nothing.
//*------------------------------------------------------------------------
void CreateResultsFont(RESULTSSET FAR * rs, HWND hwnd, LOGFONT FAR * lf)
{
HDC hdc;
LOGFONT logfont;
HFONT hf;
TEXTMETRIC tm;
SIZE sz;
int tmp, dex, cbExtra;

if(!lf) {
memset(&logfont, 0, sizeof(LOGFONT));
GetDefaultFont(&logfont);
}
else
memmove(&logfont, lf, sizeof(LOGFONT));

rs->hFont = CreateFontIndirect(&logfont);
hdc = GetDC(hwnd);
hf = SelectObject(hdc, rs->hFont);
GetTextMetrics(hdc, &tm);
rs->cx = tm.tmAveCharWidth;

rs->cy = tm.tmHeight + tm.tmExternalLeading;
cbExtra = GetSystemMetrics(SM_CYBORDER);
rs->cTitleHeight = rs->cy + (7 * cbExtra);
rs->yTitleLoc = (rs->cTitleHeight / 2) + rs->cy;
for(dex=0, tmp=0; dex<rs->cbColumns; dex++) {
GetTextExtentPoint(hdc, rs->md[dex].szColumnName,
lstrlen(rs->md[dex].szColumnName), &sz);
rs->md[dex].cColWidth = (rs->md[dex].cbDisplaySize * rs->cx) + (7 * cbExtra);
rs->md[dex].cColWidth = max((UINT)(sz.cx * 1.5),
rs->md[dex].cColWidth);
rs->md[dex].xCol = tmp;
tmp += rs->md[dex].cColWidth;
}
rs->cRowWidth = tmp;

SelectObject(hdc,hf);
ReleaseDC(hwnd, hdc);
}


//*------------------------------------------------------------------------
//| FindRightCol:
//| This function will take the left column and a results set descriptor
//| and return the right column index based on what will fit in the
//| window.
//| Parms:
//| in rs Pointer to results set to store info
//| in xLeftCol Current left column index
//| in cWidth Available width
//| Returns:
//| Index to be used for right column
//*------------------------------------------------------------------------
int FindRightCol(RESULTSSET FAR * rs, int xLeftCol, int cWidth)
{
int xRightCol;
int cSpace;

xRightCol = xLeftCol;
cSpace = cWidth - rs->md[xLeftCol].cColWidth;
while(cSpace>0 &&
xRightCol < rs->cbColumns-1) {
++xRightCol;
cSpace -= rs->md[xRightCol].cColWidth;
}
return xRightCol;
}


//*------------------------------------------------------------------------
//| DrawRowData:
//| This function will do the actual drawing on the screen based on the
//| control structures passed in.
//| Parms:
//| in rs Pointer to results set to store info
//| in dwitem Draw structure
//| in xLeftCol Current left column index
//| in xRightCol Right column index
//| Returns:
//| Nothing.
//*------------------------------------------------------------------------
void DrawRowData(RESULTSSET FAR * rs, DRAWITEMSTRUCT FAR * dwitem,
int xLeftCol, int xRightCol)
{
switch(dwitem->itemAction) {
case ODA_DRAWENTIRE:
case ODA_SELECT:
DrawRow(dwitem, rs, xLeftCol, xRightCol,
(dwitem->itemState == ODS_SELECTED));
return;
}
}


//*------------------------------------------------------------------------
//| DrawColumnTitles:
//| This function is called when we need to paint the column titles for a
//| results set. We will simply write them out.
//| Parms:
//| in hdc Handle to our device contex
//| in rs Our results set to draw
//| in crect Client rectangle to paint in
//| in xLeftCol Left column
//| in xRightCol Right column
//| Returns:
//| Nothing.
//*------------------------------------------------------------------------
void INTFUN DrawColumnTitles(HDC hdc, RESULTSSET FAR * rs,
RECT FAR * crect, int xLeftCol, int xRightCol)
{
int dex, offset, cright=0;
RECT rect;
HFONT hf;

hf = SelectObject(hdc, rs->hFont);
SetTextColor(hdc, RDATA_BLACK);
offset = 0 - rs->md[xLeftCol].xCol;
for (dex=xLeftCol; dex<=xRightCol; dex++)
cright += rs->md[dex].cColWidth;
Rectangle(hdc, crect->left, crect->top, min(cright, crect->right), crect->bottom+1);
SetBkColor(hdc, RDATA_GRAY);

rect.top = crect->top +1;
rect.bottom = crect->bottom;
for(dex=xLeftCol; dex<=xRightCol; dex++) {
rect.left = rs->md[dex].xCol + offset;
rect.right = rect.left + rs->md[dex].cColWidth;
MoveTo(hdc, rect.right, rect.top);
LineTo(hdc, rect.right, rect.bottom);
++rect.left;
#ifdef TITLE_DEBUG
{
char tmpbuff[50];
wsprintf(tmpbuff, "Column: %d, left=%d, top=%d, right=%d, bottom=%d",
dex,
rect.left, rect.top,
rect.right, rect.bottom);
DrawFocusRect(hdc, &rect);
MessageBox(NULL, (LPSTR)tmpbuff, "Debug", MB_OK);
DrawFocusRect(hdc, &rect);
}
#endif
ExtTextOut(hdc, rs->md[dex].xCol + 3 + offset, rect.top + 4,
ETO_CLIPPED | ETO_OPAQUE,
&rect,
rs->md[dex].szColumnName,
lstrlen(rs->md[dex].szColumnName),
NULL);
}
SelectObject(hdc,hf); // change font back
}



//*------------------------------------------------------------------------
//| DrawRow:
//| Call this function for each row which must be painted.
//| Parms:
//| in dw Draw structure
//| in rs Our results set to draw
//| in xLeftCol Index to left-most column displayed
//| in xRightCol Index to right-most column displayed
//| in fSelect Is the item supposed to be selected?
//| Returns:
//| TRUE if successful,
//| FALSE otherwise
//*------------------------------------------------------------------------
//#define RECT_DEBUG
BOOL INTFUN DrawRow(DRAWITEMSTRUCT FAR * dw,
RESULTSSET FAR * rs,
int xLeftCol, int xRightCol,
BOOL fSelect)
{
ROWDATA FAR * rd=(ROWDATA FAR *)dw->itemData;
int dex;
int offset;
int cright=0;
RECT rect;
HFONT hf;

//
// First set the font and text colors according to the user's request, then draw
// a line at the bottom of the row for a separator. Note that the rcItem
// rectangle passed to us in the DRAWITEMSTRUCT is for the
//
hf = SelectObject(dw->hDC, rs->hFont);
dw->rcItem.right = min(rs->cRowWidth, dw->rcItem.right);
for (dex=xLeftCol; dex<=xRightCol; dex++)
cright += rs->md[dex].cColWidth;
// Draw top of box
MoveTo(dw->hDC, dw->rcItem.left, dw->rcItem.top);
LineTo(dw->hDC, min(cright, dw->rcItem.right), dw->rcItem.top);

// Draw bottom also, to take care of last line
MoveTo(dw->hDC, dw->rcItem.left, dw->rcItem.bottom);
LineTo(dw->hDC, min(cright, dw->rcItem.right), dw->rcItem.bottom);

#ifdef RECT_DEBUG
{
char tmpbuff[50];
wsprintf(tmpbuff, "dw->rcItem, left=%d, top=%d, right=%d, bottom=%d",
dw->rcItem.left, dw->rcItem.top,
dw->rcItem.right, dw->rcItem.bottom);
DrawFocusRect(dw->hDC, &dw->rcItem);
MessageBox(NULL, (LPSTR)tmpbuff, "Debug", MB_OK);
DrawFocusRect(dw->hDC, &dw->rcItem);
}
#endif

//
// Now loop through each column in the row and draw it's contents by creating
// a logical rectangle for each column, then filling in that rectangle with
// the value to be displayed.
//
rect.top = dw->rcItem.top+1;
rect.bottom = dw->rcItem.bottom;
SetBkMode(dw->hDC, TRANSPARENT);
if(fSelect) {
SetBkColor(dw->hDC, GetSysColor(COLOR_HIGHLIGHT));
SetTextColor(dw->hDC, GetSysColor(COLOR_HIGHLIGHTTEXT));
}
else {
SetBkColor(dw->hDC, rd->bkgrnd);
SetTextColor(dw->hDC, rd->textColor);
}
offset = 0 - rs->md[xLeftCol].xCol;
for (dex=xLeftCol; dex<=xRightCol; dex++) {
rect.left = offset + rs->md[dex].xCol;
rect.right = rect.left + rs->md[dex].cColWidth;
MoveTo(dw->hDC, rect.right, rect.top);
LineTo(dw->hDC, rect.right, rect.bottom);

#ifdef RECT_DEBUG
{
char tmpbuff[50];
wsprintf(tmpbuff, "Column: %d, left=%d, top=%d, right=%d, bottom=%d",
dex,
rect.left, rect.top,
rect.right, rect.bottom);
DrawFocusRect(dw->hDC, &rect);
MessageBox(NULL, (LPSTR)tmpbuff, "Debug", MB_OK);
DrawFocusRect(dw->hDC, &rect);
}
#endif
SetTextAlign(dw->hDC, rs->md[dex].fAlign);
if(dex != xLeftCol)
++rect.left;
ExtTextOut(dw->hDC, rs->md[dex].xCol + 3 + offset, rect.top + 4,
ETO_CLIPPED | ETO_OPAQUE,
&rect,
rd->cd[dex].szCols,
lstrlen(rd->cd[dex].szCols),
NULL);
}
SelectObject(dw->hDC,hf); // change font back

return TRUE;
}


//*------------------------------------------------------------------------
//| HandleHScroll:
//| This function adds a new item to our results set.
//| Parms:
//| in wParam Scroll option
//| in rs Results set pointer
//| in hwnd Window handle for column title
//| in hwndHScroll Scroll bar window handle
//| in xLeftCol Left column index
//| in xRightCol Right column index
//| in hwndList Listbox window handle
//| in cbColumns Number of columns
//| in cbClient Width of screen available to draw in
//| in tRect Bounding rectangle for client window
//| Returns:
//| Index to string if successful, LB_ERRSPACE otherwise
//*------------------------------------------------------------------------
void HandleHScroll(WPARAM wParam, RESULTSSET FAR * rs,
HWND hwnd, HWND hwndHScroll, int FAR * xLeftCol, int FAR * xRightCol,
HWND hwndList, int cbColumns, int cbClient, RECT FAR * tRect)
{
int cHScrollPos;
int fhScroll=FALSE;

cHScrollPos = GetScrollPos(hwndHScroll, SB_CTL);
switch(wParam) {
case SB_LINEUP: // Shift right one column
if(!*xLeftCol)
fhScroll = FALSE;
else {
--cHScrollPos;
fhScroll = TRUE;
--*xLeftCol;
}
break;

case SB_LINEDOWN: // Shift left one column
if(*xLeftCol+1 == cbColumns)
fhScroll = FALSE; // No change required
else {
++cHScrollPos;
fhScroll = TRUE;
++*xLeftCol;
}
break;

case SB_PAGEUP: // Shift right one screen
if(!*xLeftCol)
fhScroll = FALSE;
else {
--cHScrollPos;
fhScroll = TRUE;
--*xLeftCol;
}
break;

case SB_PAGEDOWN: // Shift left one screen
if(*xLeftCol+1 == cbColumns)
fhScroll = FALSE; // No change required
else {
if(*xLeftCol < *xRightCol) {
cHScrollPos += *xRightCol - *xLeftCol;
*xLeftCol = *xRightCol;
fhScroll = TRUE;
}
else {
++cHScrollPos;
++*xLeftCol;
fhScroll = TRUE;
}
}
break;

case SB_THUMBPOSITION: // Specific location
break;
}

//
// If movement is required, we will have adjusted the scroll position
// and columns already. Calculate what columns will fit on our current
// display to find the rightmost column. Next invalidate the areas
// requiring painting and set the new scroll position. This will cause
// each row to be redrawn starting with the new rwi->xLeftCol.
//
if(fhScroll) { // Movement is required
RECT rect;
*xRightCol = FindRightCol(rs, *xLeftCol, cbClient);
GetClientRect(hwndList, &rect);
InvalidateRect(hwndList, &rect, TRUE);
SetScrollPos(hwndHScroll, SB_CTL, cHScrollPos, TRUE);
InvalidateRect(hwnd, tRect, TRUE);
}
}


//*------------------------------------------------------------------------
//| HandleVirtualHScroll:
//| This function should be called in response to the WM_KEYDOWN
//| message. It will look for a virtual key to see if the user
//| is trying to do scrolling. If so, we will force the scroll
//| to happen.
//| Parms:
//| in wParam Value of wParam for WM_KEYDOWN
//| in hwndList Handle of list box
//| in hwndOwner Owner window of the horizontal scrollbar
//| Returns:
//| Nothing.
//*------------------------------------------------------------------------
void HandleVirtualHScroll(WPARAM wParam, HWND hwndList, HWND hwndOwner)
{
switch(wParam) {
case VK_HOME:
SendMessage(hwndList, WM_VSCROLL, SB_TOP, 0L);
return;

case VK_END:
SendMessage(hwndList, WM_VSCROLL, SB_BOTTOM, 0L);
return;

case VK_PRIOR:
SendMessage(hwndList, WM_VSCROLL, SB_PAGEUP, 0L);
return;

case VK_NEXT:
SendMessage(hwndList, WM_VSCROLL, SB_PAGEDOWN, 0L);
return;

case VK_UP:
SendMessage(hwndList, WM_VSCROLL, SB_LINEUP, 0L);
return;

case VK_DOWN:
SendMessage(hwndList, WM_VSCROLL, SB_LINEDOWN, 0L);
return;

case VK_LEFT:
SendMessage(hwndOwner, WM_HSCROLL, SB_LINEUP, 0L);
return;

case VK_RIGHT:
SendMessage(hwndOwner, WM_HSCROLL, SB_LINEDOWN, 0L);
return;
}
}


//*------------------------------------------------------------------------
//| AddRowData:
//| This function adds a new item to our results set.
//| Parms:
//| in rs Pointer to results set
//| in rd Pointer to row data to add
//| Returns:
//| Index to string if successful, LB_ERRSPACE otherwise
//*------------------------------------------------------------------------
int AddRowData(RESULTSSET FAR * rs, ROWDATA FAR * rd)
{
int rtn;
DWORD cbCnt;
rtn = (int)SendMessage(rs->hwndList, LB_ADDSTRING, 0, (LPARAM)(ROWDATA FAR *)rd);
if(rtn == LB_ERRSPACE) {
cbCnt = SendMessage(rs->hwndList, LB_GETCOUNT, 0, 0L);
wsprintf(szErrMsg, szMaxRowsFetched, cbCnt);
MessageBox(rs->hwndClient, szErrMsg, szErrTitle, MB_OK);
}
return rtn;
}






//*------------------------------------------------------------------------
//| GetNumResultsCols:
//| Given an hstmt which has an executed statement on it, find the number
//| of results columns in it.
//| Parms:
//| in hstmt Statement handle with results set
//| Returns:
//| Number of columns
//*------------------------------------------------------------------------
SWORD GetNumResultsCols(HSTMT hstmt)
{
SWORD cbCols;
RETCODE retcode;

retcode = SQLNumResultCols(hstmt, &cbCols);
if(RC_NOTSUCCESSFUL(retcode))
return -1;
else
return cbCols;
}



//*------------------------------------------------------------------------
//| GetTypeName:
//| This function will return the null-terminated character name of
//| the type passed in.
//| Parms:
//| in type SQL_TYPE or C_TYPE
//| in fType The fCType or fSqlType
//| Returns:
//| Nothing.
//*------------------------------------------------------------------------
LPSTR GetTypeName(int type, int fType)
{
int dex, stopdex;
DATATYPE FAR * dt;

if(type == SQL_TYPE) {
stopdex = NumItems(SqlTypes);
dt = (DATATYPE FAR *)&SqlTypes;
}
else {
stopdex = NumItems(CTypes);
dt = (DATATYPE FAR *)&CTypes;
}
for(dex=0; dex<stopdex; dex++)
if(dt[dex].type == fType)
return dt[dex].sztype;
return (LPSTR)szTypeNotFound;
}


//*------------------------------------------------------------------------
//| ConvertSqlTypeToChar:
//| This function will convert the value passed in to it's character equivalent.
//| Parms:
//| in rs Pointer to results set
//| in col Which column is it?
//| in inbuff Input buffer
//| in outbuff Output buffer
//| in rtnd Returned bytes from SQLGetData
//| Returns:
//| Nothing.
//*------------------------------------------------------------------------
void ConvertSqlTypeToChar(RESULTSSET FAR * rs, int col, LPSTR inbuff,
LPSTR outbuff, SDWORD rtnd)
{
LPSTR tmpstr;
SWORD FAR * tmpsword;
SDWORD FAR * tmpsdword;
SFLOAT FAR * tmpsfloat;
SDOUBLE FAR * tmpsdouble;
DATE_STRUCT FAR * tmpdate;
TIME_STRUCT FAR * tmptime;
TIMESTAMP_STRUCT FAR * tmptimestmp;

*outbuff = '\0';
switch(rs->md[col].fSqlType) {
//
// Look for any non-displayable characters and change them to periods
//
case SQL_CHAR:
case SQL_VARCHAR:
case SQL_LONGVARCHAR:
CheckDisplayMode((LPSTR)inbuff, rtnd, outbuff);
tmpstr = outbuff + rtnd;
*tmpstr = '\0';
break;

case SQL_BINARY:
case SQL_VARBINARY:
case SQL_LONGVARBINARY:
lstrcpy(outbuff, "0x");

BinToChar(outbuff+2, (LPSTR)inbuff, rtnd); 
break;

case SQL_TINYINT:
case SQL_SMALLINT:
tmpsword = (SWORD FAR *)inbuff;
wsprintf(outbuff, "%d", *tmpsword);
break;

case SQL_INTEGER:
case SQL_BIGINT:
tmpsdword = (SDWORD FAR *)inbuff;
wsprintf(outbuff, "%ld", *tmpsdword);
break;

case SQL_FLOAT:
case SQL_DOUBLE:
tmpsdouble = (SDOUBLE FAR *)inbuff;
sprintf(outbuff, "%Fg", *tmpsdouble);
break;

case SQL_REAL:
tmpsfloat = (SFLOAT FAR *)inbuff;
sprintf(outbuff, "%Fg", *tmpsfloat);
break;

case SQL_BIT:
tmpsword = (SWORD FAR *)inbuff;
lstrcpy(outbuff, (*tmpsword) ? (LPSTR)szONE : (LPSTR)szZERO);
break;

case SQL_DECIMAL:
case SQL_NUMERIC:
lstrcpy(outbuff, inbuff);
break;

case SQL_DATE:
tmpdate = (DATE_STRUCT FAR *)inbuff;
wsprintf(outbuff, szdate, tmpdate->month, tmpdate->day, tmpdate->year);
break;

case SQL_TIME:
tmptime= (TIME_STRUCT FAR *)inbuff;
wsprintf(outbuff, sztime, tmptime->hour, tmptime->minute, tmptime->second);
break;

case SQL_TIMESTAMP:
tmptimestmp = (TIMESTAMP_STRUCT FAR *)inbuff;
wsprintf(outbuff, sztimestmp, tmptimestmp->year, tmptimestmp->month,
tmptimestmp->day, tmptimestmp->hour, tmptimestmp->minute,
tmptimestmp->second, tmptimestmp->fraction);
break;
}

return;
}


//*------------------------------------------------------------------------
//| CheckDisplayMode:
//| This function looks through a string for the count specified, then
//| changes any x"00" to a period so it can be displayed.
//| Parms:
//| strin - String coming in
//| cbin - Byte count of incoming string
//| strout - Output string
//*------------------------------------------------------------------------
void CheckDisplayMode(LPSTR strin, SDWORD cbin, LPSTR strout)
{
SDWORD dex,max=cbin;
LPSTR str=strout;

if(cbin < 0)
max = lstrlen(strin);
memcpy(strout, strin, (size_t)max);
for(dex=0; dex<cbin; dex++, str++)
if(!*str)
*str = '.';
}


//*------------------------------------------------------------------------
//| BinToChar:
//| Takes a string and converts to its hexidecimal equivalent
//*------------------------------------------------------------------------
void BinToChar(LPSTR outstr, LPSTR instr, SDWORD count)
{
UCHAR uletter;
LPSTR istr=instr;
LPSTR ostr=outstr;

while(count--) {
uletter = (*instr & 0xF0) >> 4; // High nibble
if(uletter <= 9)
*ostr++ = uletter + '0';
else
*ostr++ = 'A' + (uletter - 10);
uletter = *instr++ & 0x0F;
if(uletter <= 9)
*ostr++ = uletter + '0';
else
*ostr++ = 'A' + (uletter - 10);
}
*ostr = '\0';
}