home *** CD-ROM | disk | FTP | other *** search
/ Microsoft Internet Business Development Kit / PRODUCT_CD.iso / sqlsvr / odbcsdk / samples / cppdemo / cppdemo.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1994-12-07  |  57.3 KB  |  2,207 lines

  1. /*--------------------------------------------------------------------------
  2.   CPPDEMO.CPP - C++ demonstration of the ODBC SDK low level C++ classes
  3.  
  4.   This code is furnished on an as-is basis as part of the ODBC SDK and is
  5.   intended for example purposes only.
  6.  
  7. --------------------------------------------------------------------------*/
  8.       
  9. //////////////////////////////////////////////////////////////////////////////
  10. //
  11. //    Includes
  12. //
  13. #include    "headers.h"
  14.                     
  15.     // Attach globals to this file
  16.                   
  17. #define        INCL_GLOBAL
  18. #include    "resource.h"
  19. #include    "cppdemo.h"
  20.  
  21.  
  22. //////////////////////////////////////////////////////////////////////////////
  23. //
  24. //    Defines
  25. //
  26.  
  27.  
  28. //////////////////////////////////////////////////////////////////////////////
  29. //
  30. //    Constants
  31. //
  32.  
  33.  
  34. //////////////////////////////////////////////////////////////////////////////
  35. //
  36. //    Types
  37. //
  38. typedef struct tagErrorText
  39.     {
  40.     char        szText[1024];
  41.     char        szFmt[cbSTRLEN];
  42.     char        szSQLState[6];
  43.     char        szError[SQL_MAX_MESSAGE_LENGTH];
  44.     }
  45.     ERRORTEXT, FAR *LPERRORTEXT;
  46.         
  47.         
  48. //////////////////////////////////////////////////////////////////////////////
  49. //
  50. //    Globals
  51. //
  52.  
  53.  
  54. //////////////////////////////////////////////////////////////////////////////
  55. //
  56. //    Prototypes
  57. //
  58. BOOL    INTFUNC    fConnect(HWND);
  59. BOOL    INTFUNC    fDisconnect(HWND);
  60. void    INTFUNC    vDoSQL(HWND);
  61. void    INTFUNC vEnableMenus(HWND);
  62. void    INTFUNC    vFetch(HWND);
  63. void    INTFUNC    vFreeStmt(HWND, UWORD);
  64. void    INTFUNC vPaintWindow(HWND, PAINTSTRUCT);
  65. void    INTFUNC    vSetScroll(HWND);
  66. void    INTFUNC    vSizeScroll(HWND);
  67. int        PASCAL    WinMain(HINSTANCE, HINSTANCE, LPSTR, int);
  68.  
  69.  
  70. //////////////////////////////////////////////////////////////////////////////
  71. //
  72. //    fConnect
  73. //
  74. //      Open a connection to a data source.  Assumes that there is currently
  75. //      no connection.
  76. //
  77. //  Params:
  78. //      hwnd        -- window handle
  79. //
  80. //  Returns:
  81. //      BOOL        -- TRUE if connection succeeded, FALSE otherwise
  82. //
  83. BOOL INTFUNC fConnect(HWND hwnd)
  84.     {
  85.     LPAPPGLOB   lpGlob;             // global information
  86.     LPAPPINST   lpInst;             // instance information
  87.     RETCODE     rc;                 // SQL return code
  88.     char        sz[cbMAXSQL];       // DSN string
  89.     HCURSOR     hCurs;              // cursor handle
  90.     SWORD       swInfoValue;        // total # of bytes available to return
  91.     
  92.         // Get the global and instance-specific structures
  93.         
  94.     lpGlob = (LPAPPGLOB)GetWindowLong(hwnd, 0);
  95.     lpInst = (LPAPPINST)GetWindowLong(hwnd, sizeof(LONG));
  96.     
  97.         // If we're already connected, that's bad
  98.         
  99.     DASSERT(!lpInst->fConnected);
  100.     
  101.         // Allocate a new connection
  102.  
  103.     HOURGLASS(hCurs);                                
  104.     lpInst->cDbc = lpInst->cEnv->AllocConnect();
  105.     ARROW(hCurs);
  106.     
  107.         // If that's not successful, return failure
  108.         
  109.     if( !lpInst->cDbc )
  110.         {
  111.         vDoMessage(hwnd, IDS_NOMEMORY);
  112.         return FALSE;
  113.         }
  114.     else if( fODBCError(hwnd, lpInst->cDbc->m_rc) )
  115.         {
  116.         delete lpInst->cDbc;
  117.         lpInst->cDbc = NULL;
  118.         return FALSE;    
  119.         }
  120.     
  121.         // Don't use the cursor library for this connection, since there's no
  122.         // need for it; make the connection to a user-chosen data source
  123.         
  124.     HOURGLASS(hCurs);
  125.     fODBCError(hwnd, lpInst->cDbc->SetConnectOption(SQL_ODBC_CURSORS,
  126.                                                     SQL_CUR_USE_DRIVER));
  127.     rc = lpInst->cDbc->DriverConnect(hwnd, NULL, sz, sizeof(sz),
  128.                                      SQL_DRIVER_COMPLETE);
  129.     ARROW(hCurs);
  130.         
  131.         // Check for errors in making the connection
  132.                      
  133.     if( rc == SQL_NO_DATA_FOUND || fODBCError(hwnd, rc) )
  134.         {
  135.         delete lpInst->cDbc;
  136.         lpInst->cDbc = NULL;
  137.         return FALSE;
  138.         }
  139.  
  140.         // Get the data source name
  141.  
  142.     *lpInst->szDSN = '\0';
  143.     fODBCError(hwnd, lpInst->cDbc->GetInfo(SQL_DATA_SOURCE_NAME,
  144.                                            lpInst->szDSN,
  145.                                            SQL_MAX_DSN_LENGTH + 1,
  146.                                            &swInfoValue));
  147.  
  148.     if( !(*lpInst->szDSN) )
  149.         lstrcpy(lpInst->szDSN, szNODSN);
  150.         
  151.         // Allocate a statement
  152.         
  153.     HOURGLASS(hCurs);
  154.     lpInst->cStmt = lpInst->cDbc->AllocStmt();
  155.     ARROW(hCurs);
  156.     
  157.         // See if there was a problem with the statement allocation..
  158.         
  159.     if( !lpInst->cStmt )
  160.         {
  161.         delete lpInst->cDbc;
  162.         lpInst->cDbc = NULL;
  163.         vDoMessage(hwnd, IDS_NOMEMORY);
  164.         return FALSE;
  165.         }
  166.     else if( fODBCError(hwnd, lpInst->cStmt->m_rc) )
  167.         {
  168.         delete lpInst->cDbc;
  169.         lpInst->cDbc = NULL;
  170.         delete lpInst->cStmt;
  171.         lpInst->cStmt = NULL;
  172.         return FALSE;
  173.         }
  174.         
  175.         // Finally, the connection is good at this point
  176.         
  177.     lpInst->fConnected = TRUE;
  178.     
  179.         // Add the DSN to the window title
  180.         
  181.     lstrcpy(lpInst->szTitle, lpGlob->szTitle);
  182.     lstrcat(lpInst->szTitle, szDASH);
  183.     lstrcat(lpInst->szTitle, lpInst->szDSN);
  184.     SetWindowText(hwnd, lpInst->szTitle);
  185.     
  186.     return TRUE;
  187.     }    
  188.  
  189.  
  190. //////////////////////////////////////////////////////////////////////////////
  191. //
  192. //    fDisconnect
  193. //
  194. //      Close a connection to a data source.
  195. //
  196. //  Params:
  197. //      hwnd        -- window handle
  198. //
  199. //  Returns:
  200. //      BOOL        -- TRUE if the disconnect succeeded, FALSE otherwise
  201. //
  202. BOOL INTFUNC fDisconnect(HWND hwnd)
  203.     {
  204.     LPAPPGLOB   lpGlob;             // global information
  205.     LPAPPINST   lpInst;             // instance information
  206.     HCURSOR     hCurs;              // cursor handle
  207.     
  208.         // Get the global and instance-specific structures
  209.         
  210.     lpGlob = (LPAPPGLOB)GetWindowLong(hwnd, 0);
  211.     lpInst = (LPAPPINST)GetWindowLong(hwnd, sizeof(LONG));
  212.     
  213.         // If we're not connected, then we're now disconnected :)
  214.         
  215.     if( !lpInst->fConnected )
  216.         return TRUE;
  217.         
  218.         // Release the associated statement resources if we've
  219.         // not already done so..
  220.         
  221.     if( lpInst->cStmt )
  222.         {
  223.         vFreeStmt(hwnd, SQL_DROP);
  224.         delete lpInst->cStmt;
  225.         lpInst->cStmt = NULL;
  226.         }
  227.         
  228.         // Disconnect..
  229.  
  230.     HOURGLASS(hCurs);
  231.     if( !fODBCError(hwnd, lpInst->cDbc->Disconnect()) )
  232.         {
  233.         delete lpInst->cDbc;
  234.         lpInst->cDbc = NULL;
  235.         }
  236.     else
  237.         {
  238.         ARROW(hCurs);
  239.         vEnableMenus(hwnd);
  240.         return FALSE;
  241.         }
  242.     ARROW(hCurs);
  243.     
  244.         // Flag the connection closed and remove the DSN from the
  245.         // Window title bar display
  246.         
  247.     lpInst->fConnected = FALSE;
  248.     lpInst->szDSN[0] = '\0';
  249.     
  250.     lstrcpy(lpInst->szTitle, lpGlob->szTitle);
  251.     SetWindowText(hwnd, lpInst->szTitle);
  252.     
  253.     return TRUE;
  254.     }
  255.  
  256.  
  257. //////////////////////////////////////////////////////////////////////////////
  258. //
  259. //    vDoMessage
  260. //
  261. //      Put up an informational message box.
  262. //
  263. //  Params:
  264. //      hwnd        -- window handle
  265. //      id          -- string resource ID
  266. //
  267. //  Returns:
  268. //      none
  269. //
  270. void INTFUNC vDoMessage(HWND hwnd, UINT id)
  271.     {
  272.     LPAPPGLOB    lpGlob;
  273.     LPAPPINST    lpInst;
  274.     char        sz[cbSTRLEN];
  275.     
  276.         // Get the global and instance-specific structures
  277.         
  278.     lpGlob = (LPAPPGLOB)GetWindowLong(hwnd, 0);
  279.     lpInst = (LPAPPINST)GetWindowLong(hwnd, sizeof(LONG));
  280.     
  281.     LoadString(lpInst->hinst, id, sz, sizeof(sz));
  282.     MessageBox(hwnd, sz, lpGlob->szTitle, MB_ICONINFORMATION | MB_OK);
  283.     
  284.     return;
  285.     }
  286.  
  287.  
  288. //////////////////////////////////////////////////////////////////////////////
  289. //
  290. //    vDoSQL
  291. //
  292. //      Execute a SQL statement and, if there is a result set from this
  293. //      execution, make bindings, allocate storage, and do the first data
  294. //      fetch.
  295. //
  296. //  Params:
  297. //      hwnd        -- window handle
  298. //
  299. //  Returns:
  300. //      none
  301. //
  302. void INTFUNC vDoSQL(HWND hwnd)
  303.     {
  304.     LPAPPGLOB   lpGlob;             // global information
  305.     LPAPPINST   lpInst;             // instance information
  306.     HCURSOR     hCurs;              // cursor handle
  307.     SWORD       cCol;               // columns in result set
  308.     UWORD       i;                  // loop counter
  309.     LPCOL       lpcol;              // column information
  310.     
  311.     HOURGLASS(hCurs);
  312.     
  313.         // Get the global and instance-specific structures
  314.         
  315.     lpGlob = (LPAPPGLOB)GetWindowLong(hwnd, 0);
  316.     lpInst = (LPAPPINST)GetWindowLong(hwnd, sizeof(LONG));
  317.     
  318.         // If there's already an outstanding result set, unbind it
  319.         
  320.     if( lpInst->fResultSet )
  321.         vFreeStmt(hwnd, SQL_UNBIND);
  322.     
  323.         // Execute the SQL statement with SQLExecDirect
  324.     
  325.     if( fODBCError(hwnd, lpInst->cStmt->ExecDirect(lpInst->szSQL,
  326.                                                    lstrlen(lpInst->szSQL))) )
  327.         {
  328.         ARROW(hCurs);
  329.         return;
  330.         }
  331.         
  332.         // Determine the number of columns in the result set
  333.  
  334.     cCol = 0;        
  335.     if( fODBCError(hwnd, lpInst->cStmt->NumResultCols(&cCol)) )
  336.         {
  337.         ARROW(hCurs);
  338.         return;
  339.         }
  340.         
  341.         // If there's a row set, we've got data. Otherwise the operation was
  342.         // successful without returning any data (probably not a SELECT,
  343.         // therefore!); display a dialog to indicate this success
  344.         
  345.     lpInst->cCol = cCol;
  346.     if( lpInst->cCol )
  347.         lpInst->fResultSet = TRUE;
  348.     else
  349.         {
  350.         DialogBoxParam(lpInst->hinst, MAKEINTRESOURCE(OKBOX),
  351.                        hwnd, DLGPROC(OkDlgProc),
  352.                        (LONG)lpInst);
  353.         ARROW(hCurs);
  354.         return;
  355.         }
  356.         
  357.         // Allocate column- and row-related storage
  358.         
  359.     lpcol = lpInst->lpcol    = (LPCOL  )AllocPtr(sizeof(COL  ) *
  360.                                                 lpInst->cCol);
  361.     if( !lpcol )
  362.         {
  363.         vDoMessage(hwnd, IDS_NOMEMORY);
  364.         vFreeStmt(hwnd, SQL_DROP);
  365.         ARROW(hCurs);
  366.         return;
  367.         }
  368.     
  369.         // For each bound column, get the column attributes
  370.         
  371.     for( i = 1; i <= lpInst->cCol; i++ )
  372.         {
  373.             // Get column name
  374.             
  375.         if( fODBCError(hwnd,
  376.                        lpInst->cStmt->ColAttributes(i, SQL_COLUMN_NAME,
  377.                                                     lpcol->szName,
  378.                                                     sizeof(lpcol->szName),
  379.                                                       NULL)) )
  380.             {
  381.             vFreeStmt(hwnd, SQL_DROP);
  382.             ARROW(hCurs);
  383.             return;
  384.             }
  385.             
  386.             // Get actual column length
  387.             
  388.         if( fODBCError(hwnd, lpInst->cStmt->ColAttributes(i,
  389.                                                           SQL_COLUMN_LENGTH,
  390.                                                           &lpcol->cb)) )
  391.             {
  392.             vFreeStmt(hwnd, SQL_DROP);
  393.             ARROW(hCurs);
  394.             return;
  395.             }
  396.             
  397.             // Get display width
  398.             
  399.         if( fODBCError(hwnd,
  400.                        lpInst->cStmt->ColAttributes(i,
  401.                                                        SQL_COLUMN_DISPLAY_SIZE,
  402.                                                     &lpcol->cbc)) )
  403.             {
  404.             vFreeStmt(hwnd, SQL_DROP);
  405.             ARROW(hCurs);
  406.             return;
  407.             }
  408.             
  409.             // All columns need to be at least long enough for the column name
  410.             // and long enough to hold the <NULL> string, just in case
  411.             
  412.         if( lstrlen(lpcol->szName) > lpcol->cbc )
  413.             lpcol->cbc = lstrlen(lpcol->szName);
  414.         if( lstrlen(lpGlob->szNull) > lpcol->cbc )
  415.             lpcol->cbc = lstrlen(lpGlob->szNull);
  416.         
  417.             // Get column SQL data type
  418.             
  419.         if( fODBCError(hwnd, lpInst->cStmt->ColAttributes(i, SQL_COLUMN_TYPE,
  420.                                                           &lpcol->fSqlType)) )
  421.             {
  422.             vFreeStmt(hwnd, SQL_DROP);
  423.             ARROW(hCurs);
  424.             return;
  425.             }
  426.             
  427.             // If the SQL data type is a CHAR form, make sure that the
  428.             // display width at least as large as the transfer width
  429.             
  430.         if( (lpcol->fSqlType == SQL_CHAR ||
  431.              lpcol->fSqlType == SQL_VARCHAR ||
  432.              lpcol->fSqlType == SQL_LONGVARCHAR) &&
  433.                 lpcol->cbc < lpcol->cb )
  434.             lpcol->cbc = lpcol->cb;
  435.         
  436.             // Since we just want to see the data, let the driver
  437.             // convert it to SQL_C_CHAR format for us
  438.             
  439.         lpcol->fCType    = SQL_C_CHAR;
  440.         
  441.             // Change the transfer length to be the same length as the display
  442.             // width and increase the storage size by one to hold the final \0
  443.             // if this can be done without truncation
  444.             
  445.             // Otherwise, adjust the size of the transfers so that memory
  446.             // allocations can be done in cbCOLMEMMAX blocks
  447.         
  448.         lpcol->cb = lpcol->cbc;
  449.         if( lpcol->cb > cbCOLMEMMAX
  450.                 || lpcol->cb * lpInst->cMaxRow > cbCOLMEMMAX )
  451.             {
  452.             lpcol->cb    = (cbCOLMEMMAX / lpInst->cMaxRow) - 1;
  453.             lpcol->cbc    = lpcol->cb;
  454.             }
  455.         else
  456.             lpcol->cb++;
  457.             
  458.             // Round transfer width to even byte boundaries
  459.             
  460.         lpcol->cb = (lpcol->cb + 3) & 0xFFFFFFFC;
  461.         
  462.             // Increment the storage and display width of the row
  463.             
  464.         lpInst->cbrow += (UWORD)lpcol->cb;
  465.         lpInst->ccols += (UWORD)lpcol->cbc;
  466.         
  467.             // Go to the next element in the lpcol array
  468.             
  469.         lpcol++;
  470.         }
  471.         
  472.         // Bind the data value for each column
  473.  
  474.     lpcol = lpInst->lpcol;
  475.     for( i = 1; i <= lpInst->cCol; i++ )
  476.         {
  477.             // Allocate the memory for the column bindings
  478.         
  479.         lpcol->lpb    = (LPBYTE)AllocPtr(lpcol->cb);
  480.         lpcol->lpcb      = (LPSDWORD)AllocPtr(sizeof(SDWORD));
  481.         lpcol->lpbuf  = (LPBYTE)AllocPtr(lpcol->cb * lpInst->cMaxRow);
  482.         lpcol->lpcbuf = (LPSDWORD)AllocPtr(sizeof(SDWORD) * lpInst->cMaxRow);
  483.         if( lpcol->lpb        == NULL || lpcol->lpcb        == NULL ||
  484.             lpcol->lpbuf    == NULL || lpcol->lpcbuf    == NULL )
  485.             {
  486.             vDoMessage(hwnd, IDS_NOMEMORY);
  487.             vFreeStmt(hwnd, SQL_DROP);
  488.             ARROW(hCurs);
  489.             return;
  490.             }
  491.         
  492.             // Bind the column
  493.         
  494.         if( fODBCError(hwnd, lpInst->cStmt->BindCol(i, (SWORD)lpcol->fCType,
  495.                                                     (PTR)lpcol->lpb,
  496.                                                     lpcol->cb,
  497.                                                     lpcol->lpcb)) )
  498.             {
  499.             vFreeStmt(hwnd, SQL_UNBIND);
  500.             ARROW(hCurs);
  501.             return;
  502.             }
  503.  
  504.             // Advance to next column entry in array
  505.             
  506.         lpcol++;
  507.         }
  508.         
  509.         // Do the first fetch
  510.     
  511.     vFetch(hwnd);
  512.  
  513.     ARROW(hCurs);
  514.     return;
  515.     }
  516.  
  517.  
  518. //////////////////////////////////////////////////////////////////////////////
  519. //
  520. //    vEnableMenus
  521. //
  522. //      Enable all connection-related menu items.
  523. //
  524. //  Params:
  525. //      hwnd        -- window handle
  526. //
  527. //  Returns:
  528. //      none
  529. //
  530. void INTFUNC vEnableMenus(HWND hwnd)
  531.     {
  532.     LPAPPGLOB   lpGlob;             // global information
  533.     LPAPPINST   lpInst;             // instance information
  534.     HMENU       hmenu;              // menu handle
  535.     
  536.         // Get the global and instance-specific structures
  537.         
  538.     lpGlob = (LPAPPGLOB)GetWindowLong(hwnd, 0);
  539.     lpInst = (LPAPPINST)GetWindowLong(hwnd, sizeof(LONG));
  540.     
  541.     hmenu = GetMenu(hwnd);
  542.     
  543.     if( lpInst && lpInst->fConnected )
  544.         {
  545.         EnableMenuItem(hmenu, ID_ENV_CLOSE,     MF_ENABLE);
  546.         EnableMenuItem(hmenu, ID_SQL_EXEC,        MF_ENABLE);
  547.         if( lpInst->fResultSet && lpInst->cRow == lpInst->cMaxRow )
  548.             EnableMenuItem(hmenu, ID_SQL_FETCH,    MF_ENABLE);
  549.         else
  550.             EnableMenuItem(hmenu, ID_SQL_FETCH,    MF_DISABLE);
  551.         }
  552.     else
  553.         {
  554.         EnableMenuItem(hmenu, ID_ENV_CLOSE,     MF_DISABLE);
  555.         EnableMenuItem(hmenu, ID_SQL_EXEC,        MF_DISABLE);
  556.         EnableMenuItem(hmenu, ID_SQL_FETCH,        MF_DISABLE);
  557.         }
  558.         
  559.     if( lpInst && lpInst->fConnected && !lpInst->cStmt )
  560.         {
  561.         EnableMenuItem(hmenu, ID_SQL_EXEC,        MF_DISABLE);
  562.         EnableMenuItem(hmenu, ID_SQL_FETCH,        MF_DISABLE);
  563.         }
  564.         
  565.     return;
  566.     }
  567.  
  568.  
  569. //////////////////////////////////////////////////////////////////////////////
  570. //
  571. //    vFetch
  572. //
  573. //      Retrieve rows into the space allocated for this result set.
  574. //
  575. //  Params:
  576. //      hwnd        -- window handle
  577. //
  578. //  Returns:
  579. //      none
  580. //
  581. void INTFUNC vFetch(HWND hwnd)
  582.     {                              
  583.     LPAPPGLOB   lpGlob;             // global information
  584.     LPAPPINST   lpInst;             // instance information
  585.     RETCODE     rc;                 // SQL return code
  586.     HCURSOR     hCurs;              // cursor handle
  587.     WORD        nRow, nCol;         // loop counters
  588.     LPCOL       lpcol;              // column information
  589.     
  590.         // Get the global and instance-specific structures
  591.         
  592.     lpGlob = (LPAPPGLOB)GetWindowLong(hwnd, 0);
  593.     lpInst = (LPAPPINST)GetWindowLong(hwnd, sizeof(LONG));
  594.     
  595.         // Fetch the data
  596.         
  597.     HOURGLASS(hCurs);
  598.     for( nRow = 0; nRow < lpInst->cMaxRow; nRow++ )
  599.         {
  600.             // Get a row of data
  601.             
  602.         rc = lpInst->cStmt->Fetch();
  603.         if( rc == SQL_NO_DATA_FOUND || fODBCError(hwnd, rc) )
  604.             break;
  605.             
  606.             // Copy the row from the transfer buffer to
  607.             // the aggregate data storage buffer
  608.             
  609.         lpcol = lpInst->lpcol;
  610.         for( nCol = 0; nCol < lpInst->cCol; nCol++ )
  611.             {
  612.             lstrcpy((LPSTR)(lpcol->lpbuf + nRow * lpcol->cb),
  613.                     (LPSTR)lpcol->lpb);
  614.             *(LPSDWORD)(lpcol->lpcbuf + nRow) = *(LPSDWORD)lpcol->lpcb;
  615.                     
  616.             lpcol++;
  617.             }
  618.         }
  619.     ARROW(hCurs);
  620.  
  621.         // Did we get any data?
  622.     
  623.     if( !nRow )
  624.         lpInst->fData = FALSE;
  625.     else
  626.         lpInst->fData = TRUE;    
  627.         
  628.         // Save the number of rows fetched
  629.     
  630.     lpInst->cRow = nRow;
  631.         
  632.     return;
  633.     }
  634.  
  635.  
  636. //////////////////////////////////////////////////////////////////////////////
  637. //
  638. //    vFreeStmt
  639. //
  640. //        Release memory and resources at the same time that we issue
  641. //      SQLFreeStmt calls.
  642. //
  643. //  Params:
  644. //      hwnd        -- window handle
  645. //      fOption     -- option flag, either SQL_DROP or SQL_UNBIND
  646. //
  647. //  Returns:
  648. //      none
  649. //
  650. void INTFUNC vFreeStmt(HWND hwnd, UWORD fOption)
  651.     {
  652.     LPAPPGLOB   lpGlob;             // global information
  653.     LPAPPINST   lpInst;             // instance information
  654.     UWORD       i;                  // loop counter
  655.     LPCOL       lpcol;              // column information
  656.     
  657.         // Get the global and instance-specific structures
  658.         
  659.     lpGlob = (LPAPPGLOB)GetWindowLong(hwnd, 0);
  660.     lpInst = (LPAPPINST)GetWindowLong(hwnd, sizeof(LONG));
  661.     
  662.     if( !lpInst->cStmt )                
  663.         return;
  664.         
  665.         // Issue the SQLFreeStmt call, unless fOption == SQL_DROP, since
  666.         // SQL_DROP will be handled by the destructor for the CSTMT class;
  667.         // in addition, we'll always close the current cursor so that we
  668.         // can be assured of having a clean operation next time
  669.         
  670.     if( fODBCError(hwnd, lpInst->cStmt->Close()) )
  671.         return;
  672.     if( fOption != SQL_DROP )
  673.         if( fODBCError(hwnd, lpInst->cStmt->FreeStmt(fOption)) )
  674.             return;
  675.             
  676.         // Release memory
  677.         
  678.     lpcol = lpInst->lpcol;
  679.     for( i = 0; i < lpInst->cCol; i++ )
  680.         {
  681.         FreePtr(lpcol->lpb);
  682.         lpcol->lpb = NULL;
  683.         FreePtr(lpcol->lpcb);
  684.         lpcol->lpcb = NULL;
  685.         FreePtr(lpcol->lpbuf);
  686.         lpcol->lpbuf = NULL;
  687.         FreePtr(lpcol->lpcbuf);
  688.         lpcol->lpcbuf = NULL;
  689.         
  690.         lpcol++;
  691.         }
  692.         
  693.     FreePtr(lpInst->lpcol);
  694.     lpInst->lpcol = NULL;
  695.     
  696.         // Set default values
  697.     
  698.     lpInst->fData        = FALSE;
  699.     lpInst->fResultSet    = FALSE;
  700.     lpInst->cCol        = 0;
  701.     lpInst->cRow        = 0;
  702.     lpInst->ccols        = 0;
  703.     lpInst->cbrow        = 0;
  704.     lpInst->ccolwin        = 0;
  705.     lpInst->crowwin        = 0;
  706.     
  707.         // Reset the scroll bars if necessary
  708.  
  709.     if( lpInst->fVScroll )
  710.         {
  711.         SetScrollRange(lpInst->hwndVScroll, SB_CTL, 0, 0, FALSE);
  712.         SetScrollPos(lpInst->hwndVScroll, SB_CTL, 0, FALSE);
  713.         ShowWindow(lpInst->hwndVScroll, SW_HIDE);
  714.         lpInst->fVScroll = FALSE;
  715.         }
  716.         
  717.     if( lpInst->fHScroll )
  718.         {
  719.         SetScrollRange(lpInst->hwndHScroll, SB_CTL, 0, 0, FALSE);
  720.         SetScrollPos(lpInst->hwndHScroll, SB_CTL, 0, FALSE);
  721.         ShowWindow(lpInst->hwndHScroll, SW_HIDE);
  722.         lpInst->fHScroll = FALSE;
  723.         }        
  724.  
  725.         // Invalidate the window so that it'll all get redrawn
  726.         
  727.     InvalidateRect(hwnd, NULL, FALSE);
  728.  
  729.         // Update the window now
  730.         
  731.     UpdateWindow(hwnd);
  732.     
  733.     return;
  734.     }
  735.  
  736.  
  737. //////////////////////////////////////////////////////////////////////////////
  738. //
  739. //    fODBCError
  740. //
  741. //        Check for an ODBC error; SQL_NO_DATA_FOUND and SQL_STILL_EXECUTING
  742. //        are not considered errors.  If an error is found, bring up a message
  743. //      box detailing the error.  In the case of SQL_SUCCESS_WITH_INFO,
  744. //      a message box with the additional information is shown, but this is
  745. //      also not considered an error.
  746. //
  747. //  Params:
  748. //      hwnd        -- window handle
  749. //      rc          -- SQL return code to check
  750. //
  751. //  Returns:
  752. //      BOOL        -- TRUE if there was an ODBC error, FALSE otherwise
  753. //
  754. BOOL INTFUNC fODBCError(HWND hwnd, RETCODE rc)
  755.     {
  756.     LPAPPGLOB   lpGlob;             // global information
  757.     LPAPPINST   lpInst;             // instance information
  758.     SDWORD      fNative;            // native error code
  759.     SWORD       cbError;            // length of returned error string
  760.     LPERRORTEXT lpErr;              // error text structure
  761.     HENV        henv;               // environment handle
  762.     HDBC        hdbc;               // connection handle
  763.     HSTMT       hstmt;              // statement handle
  764.     
  765.         // Get the global and instance-specific structures
  766.         
  767.     lpGlob = (LPAPPGLOB)GetWindowLong(hwnd, 0);
  768.     lpInst = (LPAPPINST)GetWindowLong(hwnd, sizeof(LONG));
  769.  
  770.         // Check for non-errors first
  771.     
  772.     if( rc == SQL_SUCCESS )
  773.         return FALSE;
  774.         
  775.     if( rc == SQL_NO_DATA_FOUND )
  776.         {
  777.         vDoMessage(hwnd, IDS_NODATAFOUND);
  778.         return FALSE;
  779.         }
  780.         
  781.     if( rc == SQL_STILL_EXECUTING )
  782.         {
  783.         vDoMessage(hwnd, IDS_STILLEXECUTING);
  784.         return FALSE;
  785.         }            
  786.         
  787.         // Allocate storage
  788.         
  789.     lpErr = (LPERRORTEXT)AllocPtr(sizeof(ERRORTEXT));
  790.     if( lpErr == NULL )
  791.         {
  792.         vDoMessage(hwnd, IDS_NOMEMORY);
  793.         return TRUE;
  794.         }
  795.     
  796.     LoadString(lpInst->hinst, IDS_MSGFMT, lpErr->szFmt, cbSTRLEN);
  797.     
  798.         // Retrieve and display messages until they're all gone
  799.                
  800.     henv    = lpInst->cEnv    ? HENV(*(lpInst->cEnv))        : SQL_NULL_HENV;
  801.     hdbc    = lpInst->cDbc    ? HDBC(*(lpInst->cDbc))        : SQL_NULL_HDBC;
  802.     hstmt    = lpInst->cStmt    ? HSTMT(*(lpInst->cStmt))    : SQL_NULL_HSTMT;
  803.     while( SQLError(henv, hdbc, hstmt,
  804.                     (UCHAR FAR*)lpErr->szSQLState,
  805.                     &fNative,
  806.                     (UCHAR FAR*)lpErr->szError,
  807.                     SQL_MAX_MESSAGE_LENGTH - 1,
  808.                     &cbError) != SQL_NO_DATA_FOUND )
  809.         if( lstrcmpi(lpErr->szSQLState, szDATATRUNC) )
  810.             {
  811.             wsprintf(lpErr->szText, lpErr->szFmt, lpErr->szSQLState,
  812.                      fNative, lpErr->szError);
  813.             MessageBox(hwnd, lpErr->szText, lpGlob->szTitle,
  814.                         rc == SQL_SUCCESS_WITH_INFO ?
  815.                         MB_ICONINFORMATION | MB_OK :
  816.                         MB_ICONEXCLAMATION | MB_OK);
  817.             }
  818.     
  819.         // Release storage
  820.         
  821.     FreePtr(lpErr);
  822.     
  823.     return (rc != SQL_SUCCESS_WITH_INFO);
  824.     }
  825.  
  826.  
  827. //////////////////////////////////////////////////////////////////////////////
  828. //
  829. //    vPaintBox
  830. //
  831. //      Paint the box between scroll bars if required.
  832. //
  833. //  Params:
  834. //      hdc     -- device context handle
  835. //      hwnd    -- window handle
  836. //
  837. //  Returns:
  838. //      none
  839. //
  840. void INTFUNC vPaintBox(HDC hdc, HWND hwnd)
  841.     {
  842.     LPAPPGLOB   lpGlob;             // global information
  843.     LPAPPINST   lpInst;             // instance information
  844.     HRGN        hrgn;               // clipping region handle
  845.     HBRUSH      hbrushOld;          // old brush handle
  846.     RECT        rc;                 // client rectangle
  847.  
  848.         // Get the global and instance-specific structures
  849.         
  850.     lpGlob = (LPAPPGLOB)GetWindowLong(hwnd, 0);
  851.     lpInst = (LPAPPINST)GetWindowLong(hwnd, sizeof(LONG));
  852.     
  853.         // Get the client rectangle
  854.  
  855.     GetClientRect(hwnd, &rc);
  856.  
  857.         // Include the entire box in the clipping region
  858.  
  859.     hrgn = CreateRectRgn(rc.left, rc.top, rc.right, rc.bottom);
  860.     SelectClipRgn(hdc, hrgn);
  861.  
  862.         // Select the scrollbar brush and draw the box
  863.  
  864.     hbrushOld = (HBRUSH)SelectObject(hdc, lpGlob->hbrScroll);
  865.     PatBlt(hdc,
  866.            rc.right  - (lpGlob->cxVScroll - 1),
  867.            rc.bottom - (lpGlob->cyHScroll - 1),
  868.            lpGlob->cxVScroll, lpGlob->cyHScroll,
  869.            PATCOPY);
  870.  
  871.         // Restore the device context
  872.                    
  873.     SelectObject(hdc, hbrushOld);
  874.     DeleteObject(hrgn);
  875.     }
  876.  
  877.  
  878. //////////////////////////////////////////////////////////////////////////////
  879. //
  880. //    vPaintWindow
  881. //
  882. //      Paint the main window.
  883. //
  884. //  Params:
  885. //      hwnd        -- window handle
  886. //      ps          -- paint structure
  887. //
  888. //  Returns:
  889. //      none
  890. //
  891. void INTFUNC vPaintWindow(HWND hwnd, PAINTSTRUCT ps)
  892.     {
  893.     LPAPPGLOB   lpGlob;             // global information
  894.     LPAPPINST   lpInst;             // instance information
  895.     RECT        rc;                 // general-purpose rectangle
  896.     HDC         hdc;                // device context handle
  897.     HFONT       hfontOld;           // old font handle
  898.     HBRUSH      hbrushOld;          // old brush handle
  899.     LPCOL       lpcol;              // column information
  900.     UINT        cSkip,              // columns skipped width
  901.                 uFirstCol,          // first column to paint
  902.                 uLastCol,           // last column to paint
  903.                 uFirstRow,          // first row to paint
  904.                 uLastRow,           // last row to paint
  905.                 uCol, uRow;         // loop counters
  906.     int         left, right,        // edges of current column
  907.                 row, col,           // characters shifted by scroll position
  908.                 cx, cy;             // pixels shifted by scroll position
  909.     BOOL        fPaintBox;          // box between scrollbars needs painting
  910.     
  911.         // Get the global and instance-specific structures
  912.         
  913.     lpGlob = (LPAPPGLOB)GetWindowLong(hwnd, 0);
  914.     lpInst = (LPAPPINST)GetWindowLong(hwnd, sizeof(LONG));
  915.     
  916.         // Extract the hdc from the paintstruct
  917.     
  918.     hdc = ps.hdc;
  919.  
  920.     if( !lpInst->fResultSet )
  921.         FillRect(hdc, &ps.rcPaint, lpGlob->hbrWin);
  922.     else
  923.         {
  924.             // Get the client rectangle
  925.             
  926.         GetClientRect(hwnd, &rc);
  927.         
  928.             // If the paint region includes the corner box between
  929.             // the scroll bars, remember to paint it last
  930.             
  931.         if( lpInst->fVScroll && lpInst->fHScroll &&
  932.                 ps.rcPaint.right  > rc.right  - (lpGlob->cxVScroll - 1) &&
  933.                 ps.rcPaint.bottom > rc.bottom - (lpGlob->cyHScroll - 1) )
  934.             fPaintBox = TRUE;
  935.         else
  936.             fPaintBox = FALSE;
  937.             
  938.             // Adjust the paint and client rectangles to take the
  939.             // presence of scroll bars into account
  940.             
  941.         ps.rcPaint.right    = min(rc.right -
  942.                                (lpInst->fVScroll ? lpGlob->cxVScroll - 1 : 0),
  943.                                ps.rcPaint.right);
  944.         ps.rcPaint.bottom    = min(rc.bottom -
  945.                                (lpInst->fHScroll ? lpGlob->cyHScroll - 1 : 0),
  946.                                ps.rcPaint.bottom);
  947.         rc.right            = min(rc.right,        ps.rcPaint.right);
  948.         rc.bottom            = min(rc.bottom,     ps.rcPaint.bottom);
  949.         
  950.             // Determine how many characters we're shifted over due to
  951.             // scrolling and convert that to pixels
  952.             
  953.         row = GetScrollPos(lpInst->hwndVScroll, SB_CTL);
  954.         col = GetScrollPos(lpInst->hwndHScroll, SB_CTL);
  955.         
  956.         cx  = col * lpGlob->cx;
  957.         cy  = row * lpGlob->cy;
  958.         
  959.             // Locate the first and last columns that need painting and
  960.             // save the left and right edges of the bounding rectangle
  961.             
  962.         cSkip = 0;
  963.         lpcol = lpInst->lpcol;
  964.         for( uCol = 0; uCol < lpInst->cCol; uCol++ )
  965.             {
  966.             if( (int)cSkip + (int)lpcol->cbc * (int)lpGlob->cx + 2 * cxBORDER
  967.                     <= ps.rcPaint.left + cx )
  968.                 cSkip += (int)lpcol->cbc * (int)lpGlob->cx + 2 * cxBORDER;
  969.             else
  970.                 break;
  971.             lpcol++;
  972.             }
  973.             
  974.         left        = (int)cSkip - cx;
  975.         uFirstCol    = uCol;
  976.         
  977.             // Are there no columns in the invalid region?
  978.         
  979.         if( uFirstCol == lpInst->cCol )
  980.             {
  981.             if( fPaintBox )
  982.                 vPaintBox(hdc, hwnd);
  983.             return;
  984.             }
  985.         
  986.         for( uCol = uFirstCol; uCol < lpInst->cCol; uCol++ )
  987.             {
  988.             if( (int)cSkip + (int)lpcol->cbc * (int)lpGlob->cx + 2 * cxBORDER
  989.                     <= ps.rcPaint.right + cx )
  990.                 cSkip += (int)lpcol->cbc * (int)lpGlob->cx + 2 * cxBORDER;
  991.             else
  992.                 break;
  993.             lpcol++;
  994.             }
  995.             
  996.             // Are all the columns in the invalid region?
  997.             
  998.         if( uCol == lpInst->cCol )
  999.             {
  1000.             uCol--;
  1001.             lpcol--;
  1002.             cSkip -= (int)lpcol->cbc * (int)lpGlob->cx + 2 * cxBORDER;
  1003.             }
  1004.             
  1005.         right        = (int)cSkip + (int)lpcol->cbc * (int)lpGlob->cx
  1006.                       + 2 * cxBORDER - 1 - cx;
  1007.         uLastCol    = uCol;
  1008.         
  1009.             // Determine if we need to paint the titles; if so, do it
  1010.             
  1011.         if( ps.rcPaint.top < rc.top + lpGlob->cy )
  1012.             {
  1013.                 // Select color and font for the column names
  1014.         
  1015.             SetBkColor(hdc, GetSysColor(COLOR_BTNFACE));
  1016.             hfontOld = (HFONT)SelectObject(hdc, lpGlob->hfontName);
  1017.         
  1018.                 // Determine the bounding rectangle for the names
  1019.             
  1020.             rc.bottom    = rc.top + lpGlob->cy - 1;
  1021.             rc.left        = left;
  1022.             rc.right    = right;
  1023.         
  1024.                 // Fill rectangle
  1025.             
  1026.             FillRect(hdc, &rc, lpGlob->hbrBtn);
  1027.         
  1028.                 // Paint white bar across top of all the name boxes
  1029.             
  1030.             PatBlt(hdc, left, rc.top, right - left + 1, 1, WHITENESS);
  1031.  
  1032.                 // Paint column names
  1033.             
  1034.             lpcol = lpInst->lpcol;
  1035.             for( uCol = 0; uCol < uFirstCol; uCol++ )
  1036.                 lpcol++;
  1037.             rc.right = rc.left;
  1038.             for( uCol = uFirstCol; uCol <= uLastCol; uCol++ )
  1039.                 {
  1040.                 RECT    rcTemp;
  1041.                 WORD    cTextLen,
  1042.                         cHalfWidth;
  1043. #ifdef WIN32
  1044.                 SIZE    size;
  1045. #endif
  1046.                 
  1047.                     // Add a white line on the left of the name box
  1048.                     
  1049.                 PatBlt(hdc, rc.left, rc.top, 1, lpGlob->cy, WHITENESS);
  1050.                 
  1051.                     // Locate the new right edge
  1052.             
  1053.                 rc.right += (int)lpcol->cbc * (int)lpGlob->cx
  1054.                             + 2 * cxBORDER - 1;
  1055.                 
  1056.                     // Draw the name text string
  1057.  
  1058. #ifdef WIN32
  1059.                 GetTextExtentPoint(hdc, lpcol->szName,
  1060.                                    lstrlen(lpcol->szName), &size);
  1061.                 cTextLen = (WORD)size.cx;
  1062. #else
  1063.                 cTextLen = LOWORD(GetTextExtent(hdc, lpcol->szName,
  1064.                                                 lstrlen(lpcol->szName)));
  1065. #endif
  1066.                                                 
  1067.                 cHalfWidth        = (rc.right - rc.left + 1 - cTextLen) / 2;
  1068.                 rcTemp.top        = rc.top    + 1;
  1069.                 rcTemp.bottom    = rc.bottom;
  1070.                 rcTemp.left        = rc.left    + cHalfWidth;
  1071.                 rcTemp.right    = rc.right    - cHalfWidth;
  1072.                                               
  1073.                 DrawText(hdc, lpcol->szName, lstrlen(lpcol->szName),
  1074.                          &rcTemp, DT_CENTER | DT_SINGLELINE | DT_VCENTER);
  1075.                          
  1076.                     // Put a black line on the right edge of the name box
  1077.             
  1078.                 PatBlt(hdc, rc.right, rc.top, 1, lpGlob->cy, BLACKNESS);
  1079.             
  1080.                     // Move the left edge over for the next column
  1081.                     // and advance the lpcol array pointer
  1082.                      
  1083.                 rc.left = ++rc.right;
  1084.                 lpcol++;
  1085.                 }
  1086.             
  1087.                 // Paint black line across bottom of all the name boxes
  1088.             
  1089.             PatBlt(hdc, left, rc.bottom, right - left + 1, 1, BLACKNESS);
  1090.         
  1091.                 // Reset device context
  1092.             
  1093.             SelectObject(hdc, hfontOld);
  1094.             
  1095.                 // Fill anything outside the rightmost column
  1096.                 
  1097.             rc.left        = rc.right;
  1098.             rc.right    = ps.rcPaint.right;
  1099.             if( rc.left <= rc.right )
  1100.                 FillRect(hdc, &rc, lpGlob->hbrWin);
  1101.             }
  1102.             
  1103.             // Determine if we need to paint any rows
  1104.             
  1105.         if( ps.rcPaint.bottom > rc.top + lpGlob->cy - 1 &&
  1106.             lpInst->fData )
  1107.             {
  1108.                 // Locate the first and last rows that need to be painted
  1109.             
  1110.             uFirstRow = (ps.rcPaint.top <= rc.top + lpGlob->cy - 1)
  1111.                         ? cy / lpGlob->cy
  1112.                         : (ps.rcPaint.top + cy - (rc.top + lpGlob->cy))
  1113.                             / lpGlob->cy;
  1114.                         
  1115.             uLastRow  = (ps.rcPaint.bottom + cy - (rc.top + lpGlob->cy))
  1116.                             / lpGlob->cy;
  1117.             
  1118.                 // Are there really rows there?
  1119.                 
  1120.             if( uFirstRow > (UINT)(lpInst->cRow - 1) )
  1121.                 {
  1122.                 if( fPaintBox )
  1123.                     vPaintBox(hdc, hwnd);
  1124.                 return;
  1125.                 }
  1126.                 
  1127.             if( uLastRow > (UINT)(lpInst->cRow - 1) )
  1128.                 uLastRow = lpInst->cRow - 1;
  1129.             
  1130.                 // Determine the bounding rectangle for the SQL data
  1131.             
  1132.             rc.top        = rc.top + (uFirstRow + 1) * lpGlob->cy - cy;
  1133.             rc.bottom    = rc.top + lpGlob->cy - 1;
  1134.             rc.left        = left;
  1135.             rc.right    = right;
  1136.             
  1137.                 // Select the font and brush for the data text and borders;
  1138.                 // set the text background color to window background color
  1139.                 
  1140.             SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
  1141.             hfontOld    = (HFONT )SelectObject(hdc, lpGlob->hfontData);
  1142.             hbrushOld    = (HBRUSH)SelectObject(hdc, lpGlob->hbrBtn);
  1143.             
  1144.                 // Draw each row for which there is data
  1145.  
  1146.             for( uRow = uFirstRow; uRow <= uLastRow; uRow++ )
  1147.                 {
  1148.                     // Start the left and right edges at the far left
  1149.                 
  1150.                 rc.right = rc.left = left;
  1151.                 
  1152.                     // And each column for that row
  1153.                     
  1154.                 lpcol = lpInst->lpcol;
  1155.                 for( uCol = 0; uCol < uFirstCol; uCol++ )
  1156.                     lpcol++;
  1157.                 for( uCol = uFirstCol; uCol <= uLastCol; uCol++ )
  1158.                     {
  1159.                     LPBYTE        lpb;
  1160.                     LPSDWORD    lpcb;
  1161.                     
  1162.                         // Determine the new right edge
  1163.                         
  1164.                     rc.right += (int)lpcol->cbc * (int)lpGlob->cx
  1165.                                 + 2 * cxBORDER - 1;
  1166.  
  1167.                         // Fill the bounding rectangle
  1168.  
  1169.                     FillRect(hdc, &rc, lpGlob->hbrWin);
  1170.                     
  1171.                         // Draw the data text
  1172.                         
  1173.                     lpcb = lpcol->lpcbuf + uRow;
  1174.                     lpb     = lpcol->lpbuf  + uRow * lpcol->cb;
  1175.                     
  1176.                     if( *lpcb == SQL_NULL_DATA )
  1177.                         TextOut(hdc, rc.left + cxBORDER, rc.top,
  1178.                                 lpGlob->szNull, lstrlen(lpGlob->szNull));
  1179.                     else
  1180.                         TextOut(hdc, rc.left + cxBORDER, rc.top,
  1181.                                 (LPSTR)lpb, lstrlen((LPSTR)lpb));
  1182.                                  
  1183.                         // Add a line to the right of the box
  1184.                         
  1185.                     PatBlt(hdc, rc.right, rc.top, 1, lpGlob->cy, PATCOPY);
  1186.                     
  1187.                         // Move the left edge over for the next column
  1188.                         // and advance the lpcol array pointer
  1189.                         
  1190.                     rc.left = ++rc.right;
  1191.                     lpcol++;
  1192.                     }
  1193.                     
  1194.                     // Paint a line across the bottom of the row
  1195.                     
  1196.                 PatBlt(hdc, left, rc.bottom, rc.right - left, 1, PATCOPY);
  1197.                        
  1198.                     // Update the values of the bounding rectangle
  1199.                     
  1200.                 rc.top        += lpGlob->cy;
  1201.                 rc.bottom    += lpGlob->cy;
  1202.                 }
  1203.                 
  1204.                 // Restore the device context
  1205.                 
  1206.             SelectObject(hdc, hbrushOld);
  1207.             SelectObject(hdc, hfontOld);
  1208.             
  1209.                 // Save the current left edge
  1210.                 
  1211.             left         = rc.left;
  1212.             
  1213.                 // Fill anything outside the bottom row
  1214.                 
  1215.             rc.bottom    = ps.rcPaint.bottom;
  1216.             rc.left        = ps.rcPaint.left;
  1217.             rc.right    = ps.rcPaint.right;
  1218.             if( rc.top <= rc.bottom )
  1219.                 FillRect(hdc, &rc, lpGlob->hbrWin);
  1220.             
  1221.                 // Fill anything outside the rightmost column
  1222.                 
  1223.             rc.top         = ps.rcPaint.top;
  1224.             rc.bottom    = ps.rcPaint.bottom;
  1225.             rc.left        = left;
  1226.             rc.right    = ps.rcPaint.right;
  1227.             if( rc.left <= rc.right )
  1228.                 FillRect(hdc, &rc, lpGlob->hbrWin);
  1229.             }
  1230.             
  1231.             // Paint the box between scroll bars if necessary
  1232.             
  1233.         if( fPaintBox )
  1234.             vPaintBox(hdc, hwnd);
  1235.         }
  1236.         
  1237.     return;
  1238.     }
  1239.  
  1240.  
  1241. //////////////////////////////////////////////////////////////////////////////
  1242. //
  1243. //    vSetScroll
  1244. //
  1245. //        Determine if scroll bars are required, and if so, set their ranges
  1246. //      and show them.
  1247. //
  1248. //  Params:
  1249. //      hwnd        -- window handle
  1250. //
  1251. //  Returns:
  1252. //      none
  1253. //
  1254. void INTFUNC vSetScroll(HWND hwnd)
  1255.     {
  1256. static  BOOL        fInSetScroll;   // inside this procedure flag
  1257.         LPAPPGLOB   lpGlob;         // global information
  1258.         LPAPPINST   lpInst;         // instance information
  1259.         RECT        rc;             // window rectangle
  1260.         int         cx, cy,         // window dimensions
  1261.                     row, col;       // scroll positions
  1262.                 
  1263.         // Get the global and instance-specific structures
  1264.         
  1265.     lpGlob = (LPAPPGLOB)GetWindowLong(hwnd, 0);
  1266.     lpInst = (LPAPPINST)GetWindowLong(hwnd, sizeof(LONG));
  1267.     
  1268.         // Use the fInSetScroll flag to prevent recursion from WM_SIZE msgs
  1269.         
  1270.     if( fInSetScroll )
  1271.         return;
  1272.         
  1273.     fInSetScroll = TRUE;
  1274.     
  1275.         // If there's no result set, there's no need for scrollbars
  1276.         
  1277.     if( !lpInst->fResultSet )
  1278.         {
  1279.         lpInst->fVScroll =
  1280.         lpInst->fHScroll = FALSE;
  1281.         
  1282.         ShowWindow(lpInst->hwndVScroll, SW_HIDE);
  1283.         ShowWindow(lpInst->hwndHScroll, SW_HIDE);
  1284.         
  1285.         fInSetScroll = FALSE;
  1286.         return;
  1287.         }
  1288.     
  1289.         // Save the current scroll positions
  1290.         
  1291.     row = GetScrollPos(lpInst->hwndVScroll, SB_CTL);
  1292.     col = GetScrollPos(lpInst->hwndHScroll, SB_CTL);
  1293.     
  1294.         // Get window dimensions
  1295.         
  1296.     GetClientRect(hwnd, &rc);
  1297.     cx = rc.right    - rc.left    + 1;
  1298.     cy = rc.bottom    - rc.top    + 1    - lpGlob->cy;
  1299.     
  1300.         // Assume no scroll bars to begin with
  1301.         
  1302.     lpInst->fHScroll =
  1303.     lpInst->fVScroll = FALSE;
  1304.     
  1305.         // Determine if a vertical scroll bar is needed
  1306.         
  1307.     if( cy / lpGlob->cy < (int)lpInst->cRow )
  1308.         {
  1309.         lpInst->fVScroll = TRUE;
  1310.         cx -= lpGlob->cxVScroll;
  1311.         }
  1312.         
  1313.         // Determine if a horizontal scroll bar is needed
  1314.         
  1315.     if( cx / lpGlob->cx < (int)lpInst->ccols )
  1316.         {
  1317.         lpInst->fHScroll = TRUE;
  1318.         cy -= lpGlob->cyHScroll;
  1319.         
  1320.             // If there's not already a vertical scroll bar,
  1321.             // perhaps we now need one
  1322.             
  1323.         if( !lpInst->fVScroll && cy / lpGlob->cy < (int)lpInst->cRow )
  1324.             {
  1325.             lpInst->fVScroll = TRUE;
  1326.             cx -= lpGlob->cxVScroll;
  1327.             }
  1328.         }
  1329.         
  1330.     lpInst->ccolwin = cx / lpGlob->cx;
  1331.      lpInst->crowwin = cy / lpGlob->cy;
  1332.         
  1333.         // If no scrolling is necessary in either the vertical or horizontal
  1334.         // direction (or both, perhaps), reset the scroll positions
  1335.         
  1336.     if( !lpInst->fVScroll )
  1337.         row = 0;
  1338.     if( !lpInst->fHScroll )
  1339.         col = 0;
  1340.         
  1341.         // Set scroll ranges, positions, and scroll bar visibility
  1342.         
  1343.     SetScrollRange(lpInst->hwndVScroll, SB_CTL, 0,
  1344.                    lpInst->cRow - lpInst->crowwin, TRUE);
  1345.     SetScrollRange(lpInst->hwndHScroll, SB_CTL, 0,
  1346.                    lpInst->ccols + (2 * cxBORDER * lpInst->cCol / lpGlob->cx)
  1347.                        - lpInst->ccolwin + 1,
  1348.                    TRUE);
  1349.     
  1350.     SetScrollPos  (lpInst->hwndVScroll, SB_CTL, row, TRUE);
  1351.     SetScrollPos  (lpInst->hwndHScroll, SB_CTL, col, TRUE);
  1352.     
  1353.     ShowWindow    (lpInst->hwndVScroll, lpInst->fVScroll ? SW_SHOW : SW_HIDE);
  1354.     ShowWindow    (lpInst->hwndHScroll, lpInst->fHScroll ? SW_SHOW : SW_HIDE);
  1355.     
  1356.         // If the window isn't completely filled vertically,
  1357.         // add one extra to the number of display rows so that
  1358.         // there is no white space between the last row and
  1359.         // the horizontal scroll bar or the window edge
  1360.         
  1361.     if( cy % lpGlob->cy )
  1362.         lpInst->crowwin++;
  1363.         
  1364.         // Size and position the scroll bars
  1365.         
  1366.     vSizeScroll(hwnd);
  1367.     
  1368.         // Allow entrace into this function
  1369.         
  1370.     fInSetScroll = FALSE;
  1371.     
  1372.     return;
  1373.     }
  1374.     
  1375.  
  1376. //////////////////////////////////////////////////////////////////////////////
  1377. //
  1378. //    vSizeScroll
  1379. //
  1380. //      Size and position scroll bars.
  1381. //
  1382. //  Params:
  1383. //      hwnd        -- window handle
  1384. //
  1385. //  Returns:
  1386. //      none
  1387. //
  1388. void INTFUNC vSizeScroll(HWND hwnd)
  1389.     {
  1390.     LPAPPGLOB   lpGlob;             // global information
  1391.     LPAPPINST   lpInst;             // instance information
  1392.     RECT        rc;                 // window rectangle
  1393.  
  1394.         // Get the global and instance-specific structures
  1395.         
  1396.     lpGlob = (LPAPPGLOB)GetWindowLong(hwnd, 0);
  1397.     lpInst = (LPAPPINST)GetWindowLong(hwnd, sizeof(LONG));
  1398.  
  1399.         // Get the window rectangle
  1400.     
  1401.     GetClientRect(hwnd, &rc);
  1402.     
  1403.         // Place the vertical scroll bar
  1404.         
  1405.     MoveWindow(lpInst->hwndVScroll,
  1406.                 rc.right - lpGlob->cxVScroll + 1,
  1407.                 rc.top,
  1408.                 lpGlob->cxVScroll,
  1409.                 rc.bottom - rc.top + 1 -
  1410.                     (lpInst->fHScroll ? lpGlob->cyHScroll : 0),
  1411.                 TRUE);
  1412.                 
  1413.         // Place the horizontal scroll bar
  1414.         
  1415.     MoveWindow(lpInst->hwndHScroll,
  1416.                 rc.left,
  1417.                 rc.bottom - lpGlob->cyHScroll + 1,
  1418.                 rc.right - rc.left + 1 -
  1419.                     (lpInst->fVScroll ? lpGlob->cxVScroll : 0),
  1420.                 lpGlob->cyHScroll,
  1421.                 TRUE);
  1422.                 
  1423.     return;
  1424.     }
  1425.     
  1426.  
  1427. //////////////////////////////////////////////////////////////////////////////
  1428. //
  1429. //    WndProc
  1430. //
  1431. //      Main window procedure.
  1432. //
  1433. //  Params:
  1434. //      hwnd        -- window handle
  1435. //      wmsg        -- message ID
  1436. //      wParam      -- word parameter
  1437. //      lParam      -- long parameter
  1438. //
  1439. //  Returns:
  1440. //      LRESULT     -- depends on wmsg
  1441. //
  1442. LRESULT EXPFUNC WndProc(HWND    hwnd,
  1443.                         UINT    wmsg,
  1444.                         WPARAM    wParam,
  1445.                         LPARAM    lParam )
  1446.     {
  1447.     LPAPPGLOB       lpGlob;         // global information
  1448.     LPAPPINST       lpInst;         // instance information
  1449.     
  1450.         // Get the global and instance-specific structures
  1451.         
  1452.     lpGlob = (LPAPPGLOB)GetWindowLong(hwnd, 0);
  1453.     lpInst = (LPAPPINST)GetWindowLong(hwnd, sizeof(LONG));
  1454.     
  1455.     switch( wmsg )
  1456.         {
  1457.             // Initially, disable unnecessary menu items
  1458.         
  1459.         case WM_CREATE:
  1460.             vEnableMenus(hwnd);
  1461.             break;
  1462.             
  1463.         case WM_CLOSE:
  1464.             if( lpInst->fConnected )
  1465.                 {
  1466.                 int         iRes;
  1467.  
  1468.                     // Bring up a confirmation dialog box
  1469.  
  1470.                 iRes = DialogBoxParam(lpInst->hinst,
  1471.                                       MAKEINTRESOURCE(CONFIRMBOX),
  1472.                                       hwnd, DLGPROC(ConfirmDlgProc),
  1473.                                       (LONG)lpInst);
  1474.                 UpdateWindow(hwnd);
  1475.                 
  1476.                 if( iRes == IDOK && fDisconnect(hwnd) )
  1477.                     DestroyWindow(hwnd);
  1478.                 break;
  1479.                 }
  1480.             else
  1481.                 return DefWindowProc(hwnd, wmsg, wParam, lParam);
  1482.             
  1483.         case WM_COMMAND:
  1484.             switch( GET_WM_COMMAND_ID(wParam, lParam) )
  1485.                 {
  1486.                 case ID_FILE_EXIT:
  1487.                     PostMessage(hwnd, WM_CLOSE, 0, 0L);
  1488.                     break;
  1489.                     
  1490.                 case ID_ENV_OPEN:
  1491.                 
  1492.                         // Disconnect in order to reconnect..
  1493.                         
  1494.                     if( lpInst->fConnected )
  1495.                         {
  1496.                         int         iRes;
  1497.                     
  1498.                             // Bring up a confirmation dialog box
  1499.  
  1500.                         iRes = DialogBoxParam(lpInst->hinst,
  1501.                                               MAKEINTRESOURCE(CONFIRMBOX),
  1502.                                               hwnd,
  1503.                                               DLGPROC(ConfirmDlgProc),
  1504.                                               (LONG)lpInst);
  1505.                         UpdateWindow(hwnd);
  1506.                         
  1507.                         if( iRes != IDOK || !fDisconnect(hwnd) )
  1508.                             break;
  1509.                         
  1510.                             // Disable appropriate menu items, in
  1511.                             // case the new connection fails
  1512.                                         
  1513.                         vEnableMenus(hwnd);
  1514.                         }
  1515.                                     
  1516.                         // Make the connection and enable the appropriate menu
  1517.                         // items if it is successfully made
  1518.                                     
  1519.                     if( fConnect(hwnd) )
  1520.                         vEnableMenus(hwnd);
  1521.                     break;
  1522.                     
  1523.                 case ID_ENV_CLOSE:
  1524.                     {
  1525.                     int         iRes;
  1526.                     
  1527.                         // If we're not connected, there's something wrong
  1528.                     
  1529.                     DASSERT(lpInst->fConnected);
  1530.  
  1531.                         // Bring up a confirmation dialog box
  1532.  
  1533.                     iRes = DialogBoxParam(lpInst->hinst,
  1534.                                           MAKEINTRESOURCE(CONFIRMBOX),
  1535.                                           hwnd, DLGPROC(ConfirmDlgProc),
  1536.                                           (LONG)lpInst);
  1537.                     UpdateWindow(hwnd);
  1538.                 
  1539.                         // Disconnect and disable appropriate menu
  1540.                         // items if the disconnection was successful
  1541.                         
  1542.                     if( iRes == IDOK && fDisconnect(hwnd) )
  1543.                         vEnableMenus(hwnd);
  1544.                     }
  1545.                     break;
  1546.                     
  1547.                 case ID_ENV_ADD:
  1548.                     
  1549.                         // Bring up the create data source dialog box
  1550.                     
  1551.                     SQLCreateDataSource(hwnd, NULL);
  1552.                     // We don't care whether a data source was created or
  1553.                     // not, so we'll ignore the return code from this call
  1554.                     break;
  1555.                     
  1556.                 case ID_SQL_EXEC:
  1557.                     {
  1558.                     int         iRes;
  1559.                     
  1560.                     DASSERT(lpInst->fConnected && lpInst->cStmt);
  1561.                     
  1562.                         // Bring up the SQL statement dialog box and execute
  1563.                         // a SQL statement if the user gives us one to use
  1564.  
  1565.                     iRes = DialogBoxParam(lpInst->hinst,
  1566.                                           MAKEINTRESOURCE(SQLBOX), hwnd,
  1567.                                           DLGPROC(SQLDlgProc), (LONG)lpInst);
  1568.  
  1569.                     if( iRes == IDOK )
  1570.                         {
  1571.                         UpdateWindow(hwnd);
  1572.                         vDoSQL(hwnd);
  1573.                         }
  1574.                     else
  1575.                         break;
  1576.                     
  1577.                         // Draw the data
  1578.                         
  1579.                     vSetScroll(hwnd);
  1580.                     InvalidateRect(hwnd, NULL, FALSE);
  1581.                     
  1582.                         // Enable menu items
  1583.                         
  1584.                     vEnableMenus(hwnd);
  1585.                     }
  1586.                     break;
  1587.                     
  1588.                 case ID_SQL_FETCH:
  1589.                     {
  1590.                     RECT    rc;
  1591.  
  1592.                         // Exclude column titles
  1593.                     
  1594.                     GetClientRect(hwnd, &rc);
  1595.                     rc.top += lpGlob->cy + 1;
  1596.                         
  1597.                         // Get the data
  1598.                         
  1599.                     vFetch(hwnd);
  1600.                     
  1601.                         // Adjust the scrollbars if necessary
  1602.                         
  1603.                     if( lpInst->cRow < lpInst->cMaxRow )
  1604.                         vSetScroll(hwnd);
  1605.                     
  1606.                         // Paint it
  1607.                     
  1608.                     InvalidateRect(hwnd, &rc, FALSE);
  1609.                     
  1610.                         // Enable (or disable) menu items
  1611.                         
  1612.                     vEnableMenus(hwnd);
  1613.                     }
  1614.                     break;
  1615.  
  1616.                 case ID_HELP_HELP:
  1617.                     WinHelp(hwnd, szHELPFILE, HELP_CONTEXT, HLP_CPPDEMO);
  1618.                     break;
  1619.  
  1620.                 case ID_HELP_ABOUT:
  1621.  
  1622.                         // Bring up the About.. dialog box
  1623.  
  1624.                     DialogBoxParam(lpInst->hinst, MAKEINTRESOURCE(ABOUTBOX),
  1625.                                    hwnd, DLGPROC(AboutDlgProc), (LONG)lpInst);
  1626.                     break;
  1627.                 }
  1628.             break;
  1629.             
  1630.         case WM_PAINT:
  1631.             {
  1632.             PAINTSTRUCT    ps;
  1633.             
  1634.                 // Paint the window
  1635.             
  1636.             if( BeginPaint(hwnd, &ps) )
  1637.                 {
  1638.                 vPaintWindow(hwnd, ps);
  1639.                 EndPaint(hwnd, &ps);
  1640.                 }
  1641.             }
  1642.             break;
  1643.             
  1644.         case WM_ERASEBKGND:
  1645.             {
  1646.             RECT    rc;
  1647.             
  1648.             GetClientRect(hwnd, &rc);
  1649.             FillRect((HDC)wParam, &rc, lpGlob->hbrWin);
  1650.             }
  1651.             return TRUE;
  1652.             
  1653.         case WM_SYSCOLORCHANGE:
  1654.             if( lpInst->fCtl3d )
  1655.                 Ctl3dColorChange();
  1656.             
  1657.                 // Recreate brushes when system colors change
  1658.                 
  1659.             if( lpGlob->hbrWin )
  1660.                 DeleteObject(lpGlob->hbrWin);
  1661.             if( lpGlob->hbrBtn )
  1662.                 DeleteObject(lpGlob->hbrBtn);
  1663.             if( lpGlob->hbrScroll )
  1664.                 DeleteObject(lpGlob->hbrScroll);
  1665.                 
  1666.             lpGlob->hbrWin        =
  1667.                 CreateSolidBrush(GetSysColor(COLOR_WINDOW));
  1668.             lpGlob->hbrBtn        =
  1669.                 CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
  1670.             lpGlob->hbrScroll    =
  1671.                 CreateSolidBrush(GetSysColor(COLOR_SCROLLBAR));
  1672.             break;
  1673.             
  1674.         case WM_KEYDOWN:
  1675.         
  1676.                 // Convert keyboard commands into scroll equivalents
  1677.         
  1678.             if( wParam == VK_DOWN || wParam == VK_UP )
  1679.                 {
  1680.                 if( !lpInst->fVScroll )
  1681.                     break;
  1682.                     
  1683.                 wmsg = WM_VSCROLL;
  1684. #ifdef WIN32
  1685.                 wParam = (wParam == VK_DOWN ? SB_LINEDOWN : SB_LINEUP);
  1686. #else
  1687.                 GET_WM_VSCROLL_CODE(wParam, lParam) = (wParam == VK_DOWN
  1688.                                                         ? SB_LINEDOWN
  1689.                                                         : SB_LINEUP);
  1690. #endif
  1691.                 }
  1692.             else if( wParam == VK_LEFT || wParam == VK_RIGHT )
  1693.                 {
  1694.                 if( !lpInst->fHScroll )
  1695.                     break;
  1696.                     
  1697.                 wmsg = WM_HSCROLL;
  1698. #ifdef WIN32
  1699.                 wParam = (wParam == VK_RIGHT ? SB_LINEDOWN : SB_LINEUP);
  1700. #else
  1701.                 GET_WM_HSCROLL_CODE(wParam, lParam) = (wParam == VK_RIGHT
  1702.                                                         ? SB_LINEDOWN
  1703.                                                         : SB_LINEUP);
  1704. #endif
  1705.                 }
  1706.             else if( wParam == VK_HOME || wParam == VK_END )
  1707.                 {
  1708.                 if( lpInst->fVScroll )
  1709.                     {
  1710.                     wmsg = WM_VSCROLL;
  1711. #ifdef WIN32
  1712.                     wParam = (wParam == VK_HOME ? SB_TOP : SB_BOTTOM);
  1713. #else
  1714.                     GET_WM_VSCROLL_CODE(wParam, lParam) = (wParam == VK_HOME
  1715.                                                             ? SB_TOP
  1716.                                                             : SB_BOTTOM);
  1717. #endif
  1718.                     }
  1719.                 else if( lpInst->fHScroll )
  1720.                     {
  1721.                     wmsg = WM_HSCROLL;
  1722. #ifdef WIN32
  1723.                     wParam = (wParam == VK_HOME ? SB_TOP : SB_BOTTOM);
  1724. #else
  1725.                     GET_WM_HSCROLL_CODE(wParam, lParam) = (wParam == VK_HOME
  1726.                                                             ? SB_TOP
  1727.                                                             : SB_BOTTOM);
  1728. #endif
  1729.                     }
  1730.                 else
  1731.                     break;
  1732.                 }
  1733.             else if( wParam == VK_PRIOR || wParam == VK_NEXT )
  1734.                 {
  1735.                 if( lpInst->fVScroll )
  1736.                     {
  1737.                     wmsg = WM_VSCROLL;
  1738. #ifdef WIN32
  1739.                     wParam = (wParam == VK_PRIOR ? SB_PAGEUP : SB_PAGEDOWN);
  1740. #else
  1741.                     GET_WM_VSCROLL_CODE(wParam, lParam) = (wParam == VK_PRIOR
  1742.                                                             ? SB_PAGEUP
  1743.                                                             : SB_PAGEDOWN);
  1744. #endif
  1745.                     }
  1746.                 else if( lpInst->fHScroll )
  1747.                     {
  1748.                     wmsg = WM_HSCROLL;
  1749. #ifdef WIN32
  1750.                     wParam = (wParam == VK_PRIOR ? SB_PAGEUP : SB_PAGEDOWN);
  1751. #else
  1752.                     GET_WM_HSCROLL_CODE(wParam, lParam) = (wParam == VK_PRIOR
  1753.                                                             ? SB_PAGEUP
  1754.                                                             : SB_PAGEDOWN);
  1755. #endif
  1756.                     }
  1757.                 else
  1758.                     break;
  1759.                 }
  1760.             else
  1761.                 break;
  1762.                 
  1763.                 // If we adjusted the message, fall through to the scroll code
  1764.                                                                               
  1765.         case WM_VSCROLL:
  1766.         case WM_HSCROLL:
  1767.             {
  1768.                 // Scroll the results window
  1769.             
  1770.             HWND    hwndCtl;
  1771.             int        cPage,
  1772.                     cLine,    
  1773.                     nPos, iPos,
  1774.                     iOrig,
  1775.                     nMin, nMax;
  1776.             
  1777.                 // If there's no result set, don't scroll
  1778.             
  1779.             if( !lpInst->fResultSet )
  1780.                 break;
  1781.             
  1782.                 // Determine scroll direction and distance
  1783.                 
  1784.             hwndCtl    = (wmsg == WM_VSCROLL
  1785.                             ? lpInst->hwndVScroll
  1786.                             : lpInst->hwndHScroll);
  1787.             cPage    = (wmsg == WM_VSCROLL
  1788.                             ? lpInst->crowwin - 1
  1789.                             : lpInst->ccolwin);
  1790.             cLine    = 1;
  1791.             nPos    = GET_WM_HSCROLL_POS(wParam, lParam);
  1792.             iPos    =
  1793.             iOrig    = GetScrollPos(hwndCtl, SB_CTL);
  1794.             
  1795.             GetScrollRange(hwndCtl, SB_CTL, &nMin, &nMax);
  1796.             switch( GET_WM_VSCROLL_CODE(wParam, lParam) )
  1797.                 {
  1798.                 case SB_BOTTOM:
  1799.                     iPos  = nMax;
  1800.                     break;
  1801.                     
  1802.                 case SB_LINEDOWN:
  1803.                     iPos += cLine;
  1804.                     break;
  1805.                     
  1806.                 case SB_LINEUP:
  1807.                     iPos -= cLine;
  1808.                     break;
  1809.                     
  1810.                 case SB_PAGEDOWN:
  1811.                     iPos += cPage;
  1812.                     break;
  1813.                     
  1814.                 case SB_PAGEUP:
  1815.                     iPos -= cPage;
  1816.                     break;
  1817.                     
  1818.                 case SB_TOP:
  1819.                     iPos  = nMin;
  1820.                     break;
  1821.                     
  1822.                 case SB_THUMBPOSITION:
  1823.                     iPos = nPos;
  1824.                     break;
  1825.                 }
  1826.                 
  1827.                 // Don't let scroll requests leave the boundaries
  1828.                 
  1829.             if( iPos < nMin )
  1830.                 iPos = nMin;
  1831.             else if( iPos > nMax )
  1832.                 iPos = nMax;
  1833.                 
  1834.                 // If movement has occurred, scroll the window
  1835.                 
  1836.             if( iPos != iOrig )
  1837.                 {
  1838.                 RECT    rc;
  1839.                 
  1840.                 GetClientRect(hwnd, &rc);
  1841.                 rc.top        += (wmsg == WM_VSCROLL ? lpGlob->cy : 0);
  1842.                 rc.bottom    -= (lpInst->fHScroll ? lpGlob->cyHScroll - 1 : 0);
  1843.                 rc.right    -= (lpInst->fVScroll ? lpGlob->cxVScroll - 1 : 0);
  1844.                 
  1845.                 SetScrollPos(hwndCtl, SB_CTL, iPos, TRUE);
  1846.                 ScrollWindow(hwnd,
  1847.                     (wmsg == WM_HSCROLL ? lpGlob->cx * (iOrig - iPos) : 0),
  1848.                     (wmsg == WM_VSCROLL ? lpGlob->cy * (iOrig - iPos) : 0),
  1849.                     &rc, &rc);
  1850.                 UpdateWindow(hwnd);
  1851.                 }
  1852.             }
  1853.             break;
  1854.             
  1855.         case WM_SIZE:
  1856.             if( wParam == SIZE_MINIMIZED )
  1857.                 lpInst->fIsMinimized = TRUE;
  1858.             else
  1859.                 {
  1860.                 if( lpInst->fIsMinimized )
  1861.                     lpInst->fIsMinimized = FALSE;
  1862.                 vSizeScroll(hwnd);
  1863.                 if( lpInst->fResultSet )
  1864.                     vSetScroll(hwnd);
  1865.                 }
  1866.             break;
  1867.             
  1868.         case WM_DESTROY:
  1869.             WinHelp(hwnd, szHELPFILE, HELP_QUIT, NULL);
  1870.             PostQuitMessage(0);
  1871.             break;
  1872.                                                     
  1873.         default:
  1874.             return DefWindowProc(hwnd, wmsg, wParam, lParam);
  1875.         }
  1876.         
  1877.     return 0;
  1878.     }
  1879.  
  1880.  
  1881. //////////////////////////////////////////////////////////////////////////////
  1882. //
  1883. //    WinMain
  1884. //
  1885. //      Windows entry point.
  1886. //
  1887. //  Params:
  1888. //      hInstance       -- application instance handle
  1889. //      hPrevInstance   -- previous instance of application handle
  1890. //      lpszCmdLine     -- command line string
  1891. //      int             -- desired initial window appearance
  1892. //
  1893. //  Returns:
  1894. //      int             -- application return value; 0 if no errors
  1895. //
  1896. int PASCAL WinMain(    HINSTANCE    hInstance,
  1897.                     HINSTANCE    hPrevInstance,
  1898.                     LPSTR        lpszCmdLine,
  1899.                     int            nCmdShow )
  1900.     {
  1901.     LPAPPINST       lpInst;         // instance information
  1902.     LPAPPGLOB       lpGlob;         // global information
  1903.     HGLOBAL         hGlob;          // global information handle
  1904.     HWND            hwnd;           // window handle
  1905.     MSG             msg;            // message structure
  1906.     WNDCLASS        wc;             // window class
  1907.     HDC             hdc;            // device context handle
  1908.     HFONT           hfontOld;       // old font handle
  1909.     TEXTMETRIC      tm;             // text metrics structure
  1910.     
  1911.         // Allocate a new APPINST structure
  1912.         
  1913.     lpInst = new APPINST;
  1914.     if( !lpInst )
  1915.         return FALSE;
  1916.     
  1917.         // If this is the first instance of the app, register the class
  1918.         
  1919.     if( !hPrevInstance )
  1920.         {
  1921.             // Allocate a new APPGLOB structure
  1922.                           
  1923.         hGlob = GlobalAlloc(GHND | GMEM_SHARE, sizeof(APPGLOB));
  1924.         lpGlob = (LPAPPGLOB)GlobalLock(hGlob);             
  1925.         if( !lpGlob )
  1926.             {
  1927.             delete lpInst;
  1928.             if( hGlob )
  1929.                 GlobalFree(hGlob);
  1930.             return FALSE;
  1931.             }
  1932.             
  1933.             // Fill out the class structure
  1934.         
  1935.         wc.style         = CS_HREDRAW | CS_VREDRAW;
  1936.         wc.lpfnWndProc   = WndProc;
  1937.         wc.cbClsExtra    = sizeof(LONG);
  1938.         wc.cbWndExtra    = sizeof(LONG) * 2;
  1939.         wc.hInstance     = hInstance;
  1940.         wc.hIcon         =
  1941.         lpGlob->hicon     = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON));
  1942.         wc.hCursor       = LoadCursor(NULL, IDC_ARROW);
  1943.         wc.hbrBackground = (HBRUSH)(COLOR_BACKGROUND + 1);
  1944.         wc.lpszMenuName  = MAKEINTRESOURCE(IDR_MENU);
  1945.         wc.lpszClassName = szCLASSNAME;
  1946.         
  1947.         if( !RegisterClass(&wc) )
  1948.             {
  1949.             delete lpInst;
  1950.             GlobalUnlock(hGlob);
  1951.             GlobalFree(hGlob);
  1952.             return FALSE;
  1953.             }
  1954.             
  1955.             // Fill in the APPGLOB structure
  1956.             
  1957.         LoadString(hInstance, IDS_MAIN,
  1958.                    lpGlob->szTitle, sizeof(lpGlob->szTitle));
  1959.         LoadString(hInstance, IDS_SZNULL,
  1960.                    lpGlob->szNull,  sizeof(lpGlob->szNull));
  1961.         lpGlob->hbrWin        = CreateSolidBrush(GetSysColor(COLOR_WINDOW));
  1962.         lpGlob->hbrBtn        = CreateSolidBrush(GetSysColor(COLOR_BTNFACE));
  1963.         lpGlob->hbrScroll    = CreateSolidBrush(GetSysColor(COLOR_SCROLLBAR));
  1964.         lpGlob->cxVScroll    = GetSystemMetrics(SM_CXVSCROLL);
  1965.         lpGlob->cyHScroll    = GetSystemMetrics(SM_CYHSCROLL);
  1966.         lpGlob->cInstance    = 0;
  1967.         
  1968.             // Create fonts
  1969.         
  1970.         hdc = GetDC(NULL);
  1971.         if( !hdc )
  1972.             {
  1973.             delete lpInst;
  1974.             GlobalUnlock(hGlob);
  1975.             GlobalFree(hGlob);
  1976.             return FALSE;
  1977.             }
  1978.     
  1979.             // Column name font
  1980.  
  1981.         lpGlob->hfontName    = CreateFont((GetDeviceCaps(hdc, LOGPIXELSY)
  1982.                                     * cPOINTS) / 72,
  1983.                                     0, 0, 0, FW_BOLD, 0, 0, 0, 0, 0, 0, 0, 0,
  1984.                                     szFONT);
  1985.     
  1986.         hfontOld            = (HFONT)SelectObject(hdc, lpGlob->hfontName);
  1987.     
  1988.         GetTextMetrics(hdc, &tm);
  1989.         lpGlob->cx            = min(tm.tmMaxCharWidth,
  1990.                                   tm.tmAveCharWidth + tm.tmAveCharWidth / 8);
  1991.         lpGlob->cy            = tm.tmHeight + tm.tmInternalLeading;
  1992.     
  1993.             // Data font
  1994.     
  1995.         lpGlob->hfontData = CreateFont((GetDeviceCaps(hdc, LOGPIXELSY)
  1996.                                     * cPOINTS) / 72,
  1997.                                     0, 0, 0, FW_NORMAL, 0, 0, 0, 0, 0, 0, 0, 0,
  1998.                                     szFONT);
  1999.                             
  2000.         SelectObject(hdc, lpGlob->hfontData);
  2001.     
  2002.         GetTextMetrics(hdc, &tm);
  2003.         lpGlob->cx            = max(lpGlob->cx, min(tm.tmMaxCharWidth,
  2004.                                   tm.tmAveCharWidth + tm.tmAveCharWidth / 8));
  2005.         lpGlob->cy            = max(lpGlob->cy,
  2006.                                   tm.tmHeight + tm.tmInternalLeading);
  2007.     
  2008.             // Restore original font and release DC
  2009.         
  2010.         SelectObject(hdc, hfontOld);
  2011.         ReleaseDC(NULL, hdc);
  2012.         }
  2013.     else
  2014.         lpGlob = NULL;
  2015.  
  2016.         // Create cEnv, the environment object for this instance --
  2017.         // if this fails, we can't really do anything else
  2018.         
  2019.     lpInst->cEnv = new CENV;
  2020.     if( !lpInst->cEnv || !lpInst->cEnv->Success(lpInst->cEnv->m_rc) )
  2021.         {
  2022.         delete lpInst;
  2023.         if( lpGlob )
  2024.             {
  2025.             GlobalUnlock(hGlob);
  2026.             GlobalFree(hGlob);
  2027.             }
  2028.         return FALSE;
  2029.         }
  2030.     
  2031.         // Initialize this instance of the app
  2032.         
  2033.     lpInst->hinst            = hInstance;
  2034.     lpInst->fVScroll        = FALSE;
  2035.     lpInst->fHScroll        = FALSE;
  2036.     lpInst->fIsMinimized    = (nCmdShow == SW_MINIMIZE ||
  2037.                                   nCmdShow == SW_SHOWMINIMIZED ||
  2038.                                   nCmdShow == SW_SHOWMINNOACTIVE);
  2039.     lpInst->cDbc            = NULL;
  2040.     lpInst->cStmt            = NULL;
  2041.     lpInst->fConnected        = FALSE;
  2042.     lpInst->fResultSet        = FALSE;
  2043.     lpInst->fData            = FALSE;
  2044.     lpInst->cCol            = 0;
  2045.     lpInst->cRow            = 0;
  2046.     lpInst->cMaxRow            = 100;
  2047.     lpInst->crowwin            = 0;
  2048.     lpInst->ccolwin            = 0;
  2049.     lpInst->ccols            = 0;
  2050.     lpInst->cbrow            = 0;
  2051.     lpInst->lpcol            = NULL;
  2052.     
  2053.     LoadString(hInstance, IDS_SQLDEF,
  2054.                lpInst->szSQL, sizeof(lpInst->szSQL));
  2055.  
  2056.         // Create the window..
  2057.         
  2058.     if( !(hwnd = CreateWindow(szCLASSNAME, NULL,
  2059.                                   WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
  2060.                                 CW_USEDEFAULT, CW_USEDEFAULT,
  2061.                                 CW_USEDEFAULT, CW_USEDEFAULT,
  2062.                                   HWND_DESKTOP,
  2063.                                   NULL,
  2064.                                   hInstance,
  2065.                                   NULL)) )
  2066.         {
  2067.         delete lpInst;
  2068.         if( lpGlob )
  2069.             {
  2070.             GlobalUnlock(hGlob);
  2071.             GlobalFree(hGlob);
  2072.             }                              
  2073.         return FALSE;
  2074.         }
  2075.         
  2076.         // Create scroll bars
  2077.         
  2078.     lpInst->hwndVScroll    = CreateWindow(szSCROLLCLASS, NULL,
  2079.                                    WS_CHILD | WS_CLIPSIBLINGS | SBS_VERT,
  2080.                                    0, 0, 0, 0,
  2081.                                    hwnd, (HMENU)1, hInstance, NULL);
  2082.     ShowWindow(lpInst->hwndVScroll, SW_HIDE);
  2083.                                    
  2084.     lpInst->hwndHScroll    = CreateWindow(szSCROLLCLASS, NULL,
  2085.                                    WS_CHILD | WS_CLIPSIBLINGS | SBS_HORZ,
  2086.                                    0, 0, 0, 0,
  2087.                                    hwnd, (HMENU)2, hInstance, NULL);
  2088.     ShowWindow(lpInst->hwndHScroll, SW_HIDE);
  2089.     
  2090.         // If this is the first instance, lpGlob will be non-NULL
  2091.         // and, in that case, we need to add it to the class;
  2092.         // otherwise, retrieve the value of lpGlob through hGlob
  2093.         
  2094.     if( lpGlob )
  2095.         SetClassLong(hwnd, 0, (LONG)(LPVOID)hGlob);
  2096.     else
  2097.         {
  2098.         hGlob = (HGLOBAL)GetClassLong(hwnd, 0);
  2099.         lpGlob = (LPAPPGLOB)GlobalLock(hGlob);
  2100.         }
  2101.         
  2102.         // Store the lpGlob pointer in this window's data space
  2103.         
  2104.     SetWindowLong(hwnd, 0, (LONG)lpGlob);
  2105.         
  2106.         // Increment the instance counter
  2107.         
  2108.     lpGlob->cInstance++;
  2109.     
  2110.         // Set the window title
  2111.         
  2112.     lstrcpy(lpInst->szTitle, lpGlob->szTitle);
  2113.     SetWindowText(hwnd, lpInst->szTitle);
  2114.         
  2115.         // Initialize the CTL3D effects
  2116.         
  2117.     lpInst->fCtl3d = Ctl3dRegister(hInstance);
  2118.     if( lpInst->fCtl3d )
  2119.         lpInst->fAutoCtl3d = Ctl3dAutoSubclass(hInstance);
  2120.     else
  2121.         lpInst->fAutoCtl3d = FALSE;
  2122.     
  2123.         // Store the lpInst value with the window
  2124.         
  2125.     SetWindowLong(hwnd, sizeof(LONG), (LONG)lpInst);
  2126.         
  2127.         // Show the window
  2128.         
  2129.     ShowWindow(hwnd, nCmdShow);
  2130.     UpdateWindow(hwnd);
  2131.  
  2132.         // Get and dispatch messages
  2133.         
  2134.     while( GetMessage(&msg, NULL, NULL, NULL) )
  2135.         {
  2136.         TranslateMessage(&msg);
  2137.         DispatchMessage(&msg);
  2138.         }
  2139.         
  2140.         // Clean up this instance of the app
  2141.         
  2142.     if( lpInst->cStmt )
  2143.         delete lpInst->cStmt;
  2144.     if( lpInst->cDbc )
  2145.         delete lpInst->cDbc;
  2146.     if( lpInst->cEnv )
  2147.         delete lpInst->cEnv;
  2148.         
  2149.     if( lpInst->lpcol )
  2150.         {
  2151.         LPCOL    lpcol;
  2152.         WORD    n;
  2153.         
  2154.         lpcol = lpInst->lpcol;
  2155.         for( n = 0; n < lpInst->cCol; n++ )
  2156.             {
  2157.             FreePtr(lpcol->lpb);
  2158.             FreePtr(lpcol->lpcb);
  2159.             FreePtr(lpcol->lpbuf);
  2160.             FreePtr(lpcol->lpcbuf);
  2161.         
  2162.             lpcol++;
  2163.             }
  2164.         FreePtr(lpInst->lpcol);
  2165.         }
  2166.         
  2167.     if( lpInst->fCtl3d )
  2168.         Ctl3dUnregister(hInstance);
  2169.     
  2170.     delete lpInst;
  2171.     lpGlob->cInstance--;
  2172.     
  2173.         // If this was the last instance, clean up the global structs
  2174.         
  2175.     if( !lpGlob->cInstance )
  2176.         {
  2177.             // Delete objects
  2178.         
  2179.         if( lpGlob->hbrWin )
  2180.             DeleteObject(lpGlob->hbrWin);
  2181.         if( lpGlob->hbrBtn )
  2182.             DeleteObject(lpGlob->hbrBtn);
  2183.         if( lpGlob->hbrScroll )
  2184.             DeleteObject(lpGlob->hbrScroll);
  2185.         
  2186.         if( lpGlob->hfontName )
  2187.             DeleteObject(lpGlob->hfontName);
  2188.         if( lpGlob->hfontData )
  2189.             DeleteObject(lpGlob->hfontData);
  2190.         
  2191.         DestroyIcon(lpGlob->hicon);
  2192.         
  2193.             // Free up the global structure
  2194.             
  2195.         GlobalUnlock(hGlob);
  2196.         GlobalFree(hGlob);
  2197.         }
  2198.     else
  2199.         {
  2200.             // Otherwise, just decrement the lock count on the global struct
  2201.             
  2202.         GlobalUnlock(hGlob);
  2203.         }
  2204.  
  2205.     return TRUE;
  2206.     }
  2207.