• Welcome to Jose's Read Only Forum 2023.
 

C++ Version Of Sql Demo

Started by Frederick J. Harris, September 14, 2009, 01:11:05 AM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Frederick J. Harris

Attached is cppSqlDemo.zip which contains all the *.cpp and *.h files for the project.  I did this one first and then the PowerBASIC version.  This C++ version compiles with Visual Studio 2008 to 33K.  That's pretty amazing considering there are around 2500 lines of code.

You may note in all the *.cpp files a #define _UNICODE remarked out like so...

//#define   _UNICODE

For Visual C++ 6 that line needs to be uncommented in all the files.  For Dev-cpp, CodeBlocks, and Visual Studio 2008 C++ it needs to be commented out.  Also, for VC++ 6 you need to go into the Project Settings, C++ Tab, and remove the _MBCS and type in its place UNICODE.  The C++ app is strictly coded with all the tchar.h unicode conditional compilation macros.

Oh!  One other little thingy.  I wrapped all the database processing with the Access & Sql Server stuff in a thread procedure.  So with Visiual C++ 6 you need to go into the Project Settings, C++ Tab, and select 'Code Generation' from a list box and tell it to use the Multi-Threaded version of the C Run Time.

Actually, with VC 6 I got the size down to an incredible 32K one time on something I did, but I was getting a warning.  However, the program worked perfectly.  I deleted the whole project and started over hoping to get it working with no warnings, but I can't seem to reproduce what I had done.  Now its coming in 80K with VC6.  Oh Well, the VC9 version is 33K, and that's pretty good.  

Frederick J. Harris

I'll list all the *.cpp & *.h files.  There are a lot of them; however, they are all small.  I have the project broken up into a lot of smaller piecies than the PowerBASIC version. 

First is Main.h and Main.cpp.  These contain WinMain() and all the start up code, as well as listing of all the includes.


//Main.h
#ifndef   MAIN_H
#define   MAIN_H
#define   IDC_SQL_DRIVERS          2000
#define   IDC_EXCEL                2005
#define   IDC_MS_ACCESS            2010
#define   IDC_SQL_SERVER_EXPRESS   2015
#include  <sql.h>     //ODBC header file
#include  <sqlext.h>  //ODBC header file
#include  "SqlProcs.h"
//#define MY_DEBUG
#endif




/*
Main.cpp

SqlDemo -  Application shows various functionality of ODBC (Open Database Connectivity).  Specifically,
there are four buttons on the main form/dialog/window which when clicked will do the following:

1) Repeatedly call the ODBC function SqlDrivers() to dump from your registry the installed ODBC drivers
   on your system.  These drivers along with various pertinent information will be displayed in a
   scrollable output screen the program will create.  This information is useful because it is these
   database driver names/strings you need to form an ODBC connection string for connection to the database;

2) Connect to Microsoft Excel Book1.xls file and display contents of file to a scrollable output window
   the program creates.  To exercise this functionality you need to create the Book1.xls file yourself
   because I have not been able to figure out how to use ODBC to create the data -  I only know how to
   read and dump it.  To create the specific data this program was designed to read, open Excel and
   paste the following into the blank Sheet1 of Book1.xls and save this file to the directory from which
   your compiled executable will run.  To correctly past the data in copy this below then place your
   mouse cursor over cell A1 in the Sheet1 of Excel and right click & execute 'Paste'.

   Id Float_Point Date_Field Text_Field
   1 3.14159 11/15/1952 My Birthday
   2 1.23456 6/30/1969 Walk On Moon?
   3 15.1234 1/1/2006 Some String
   4 0.54321 4/1/2006 April Fools Day!

3) Clicking the 3rd button invokes the Microsoft Access database driver and creates an Access database
   named Testdata.mdb in whatever directory you are running the program from.  It then inserts four lines
   of data just as you see above regarding Excel into a Table1 that it also creates within the database.
   Upon successfully completing that task it creates a scrollable output window and dumps the data as well
   as other diagnostic data to there.  I might point out you don't need to have Microsoft Access installed
   for this to work.  I believe a standard Windows install will cause many of these various database drivers
   to be installed on your system.

4) Clicking the 4th button will create an Sql Server Express database exactly similiar to the Access database
   described above if you have an instance of SQL Server Express running on your system.  Sql Server Express
   and Sql Server Management Studio Express can be downloaded for free from Microsoft.  These programs allow
   you to become familiar with these powerful database systems without having to pay very costly fees.

I want to point out one cautionary note!  The way SqlDemo works is that it attempts to read and/or create
databases and files within whatever directory you are running the executable.  There is a major problem
though with several prominant locations on Windows computers; particularly C:\Documents and Settings and
C:\Program Files.  If you attempt to run the executable from any of these locations or folders under these
locations in Windows XP the program will fail to create the databases.  I'm mentioning this, for example,
because the default location Microsoft Visual Studio 2002 on through 2008 will place your various projects
is under your Documents and Settings folder somewhere, and while you'll have no trouble developing or compiling
the program from there, you simply won't be able to run it from there, at least not on XP.  I have no
experience with Vista/Windows 7, etc, with its file virtualization technologies, but I can tell you for a fact
it won't work on XP from those locations.  If you insist on developing the code at that location, you'll have
to copy the executable to somewhere else to run it.  Actually, it'll run from there fine; it just won't create
the databases when you click the Access or Sql Server buttons.  However, in those situations the program will
output diagnostic information to you informing you of various difficulties.

Another thing the program does is create an Output.txt file containing Debug log type information that may
be helpful to you in learning about ODBC or programming in general.  The program will create this Output.txt
file if the symbol MY_DEBUG is defined.  You can find this symbol in Main.h.  Just uncomment the line and the
file will be created.

Concerning the way the program works, when it starts you'll see a small form with four buttons on it.  You can
click the buttons in any sequence and as many times as you like.  Since I couldn't figure out how to do much
with the ODBC drivers for Excel, repeatedly clicking that button won't do a whole lot - it'll just repeatedly
dump those four lines to another output screen.  In the case of the Access and Sql Server buttons though, its a
bit more interesting.

The 1st time you click either of those buttons the program creates the database and Table1 within the database,
then inserts and dumps those four records described above under my discussion of Excel.  Clicking the button
additional times simply adds four more records to the preexisting database, whether it be Access or Sql Server.
So, if you want, you can fill up your screen with output windows.  Each time a button is clicked, memory
allocations are made adequate for storing whatever lines of text are to be displayed in the scrollable output
screen.  Pointers to these memory allocations are stored in the .cbWndExtra bytes of the "frmOutput" class
which is registered just below in fnWndProc_OnCreate().

Another vexing issue I faced in developing this demo is that I had tremendous trouble with the arrow and
hourglass mouse cursors.  I wanted to show an hourglass mouse cursor when processing was taking place and before
the output screen became vusible so as to let the user know something was going on.  However, particularly with
Sql Server, what was happening was that when you clicked the button, you would see either no hourglass at all,
or just a brief flicker of one, and the output screen might not become visible for quite a few seconds
afterwards, which would leave you wandering if the program failed, locked up, or whatever.  My diagnosis of this
issue (and I'm not 100% sure it is right, but I think it is) is that asynchronous processing is taking place
within the various ODBC and driver layers, and this asynchronous processing is taking place much slower than
the Windows GUI code in my app which appears to blow through the various calls to change the cursor to an
hourglass and back to an arrow again.

The only way I was able to solve the problem was to start seperate threads for the various functions containing
ODBC function calls, and this seemed to work.  I realize it made this demo more complex, but I hated the situation
with the cursor turning back to an arrow long before the output screen showed itself.

One final note I'll make is that you will probably find this app to your dislike.  It doesn't use MFC, .NET, or
any other class framework.  In fact, about the only C++ specific thing it does is use my own String class to
lighten the burden of string minipulations.  Its basically a pure Sdk Api style C program except for that and
perhaps my personal inclination to use a for loop instead of a massive switch construct to map Windows messages
to the procedure that will handle them.  In WinTypes.h you'll find my WindowsEventArguements and EVENTHANDLER
structs that I use to 'crack' messages.  With MY_DEBUG turned off the executable is only around 35K with Visual
Studio 2008 and C++.

Oh!  There is another final note actually!  When I started putting together this app I was going to add a button
for Microsoft's MSDE, which was their first incarnation of a stripped down version of SQL Server, which eventually
morphed into SQL Server Express.  While probably not too many folks have this anymore, the coding for it is about
exactly the same as for SQL Server.  Actually, it is SQL Server.  The only difference, if you have this on your
computer, and want to try this program with it, is that instead of this in the connection string for the SERVER
name...

                                     SERVER=YourComputerName\SQLEXPRESS

you'll just use...

                                             SERVER=localhost;

In the procedure SqlServerThread() I have comments that explain how to change it to use MSDE instead of Sql Server
Express.  I expect there still may be a few of these around.  I usually put it on my Windows 2000 laptops yet.

Preprocessor Settings From VC9 - "WIN32;NDEBUG;_WINDOWS;_CRT_SECURE_NO_WARNINGS;_CRT_NON_CONFORMING_SWPRINTFS"
*/
#include      <windows.h>        //Main include file for C++ Windows programming
//#define       _UNICODE         //Needed for Unicode for VC6
#include      <tchar.h>          //Used for portability to ansi & unicode
#include      <stdio.h>          //good old fashioned CRT procedures
#include      <string.h>         //low level C string procedures
#include      <odbcinst.h>       //Necessary to create access database
#include      <sql.h>            //ODBC header file
#include      <sqlext.h>         //ODBC header file
#include      "WinTypes.h"       //Message cracking logic structures
#include      "strings.h"        //prototypes of members of my String class
#include      "Main.h"           //mostly a few equates for the buttons on the main form
#include      "Sql.h"            //Sql Class used to offer ODBC functionality.
#include      "SqlProcs.h"       //Some of my ODBC wrappers to hide some real grungy ODBC code
#include      "frmOutput.h"      //Output screen code
#include      "Excel.h"          //routines for loading & dumping Excel Workbook
#include      "SqlServer.h"      //routines for dealing with SQL Server
#include      "SqlDrivers.h"     //routine to dump to Output Screen the ODBC Drivers on your system
EVENTHANDLER  EventHandler[3];   //struct to associate Windows message with function pointer
EVENTHANDLER  frmOutputHdlr[5];  //which points to event procedure that handles message
#if defined(MY_DEBUG)            //Unremark //#define MY_DEBUG in Main.h to generate Output.txt log file
    FILE*     fp;
#endif


long fnWndProc_OnCreate(lpWndEventArgs Wea)                       //First procedure (other than WinMain()) to
{                                                                 //run in this program.  If MY_DEBUG is defined
TCHAR szClassName[16];                                           //in Main.h the program attempts to create an
WNDCLASSEX wc;                                                   //Output.txt output log file in whatever directory
String s1;                                                       //the program is run from.
HWND hCtl;                                                       //The next thing the program does after this is
                                                                  //RegisterClassEx() the frmOutput class.  This is
Wea->hIns = ((LPCREATESTRUCT)Wea->lParam)->hInstance;            //important in this app because it is the output
#if defined(MY_DEBUG)                                            //screen where data this program creates is
     TCHAR lpBuffer[MAX_PATH];                                    //displayed.  The way this output screen works is
     DWORD nBufLen=MAX_PATH;                                      //it allocates 16 extra .cbWndExtra bytes in its
     GetCurrentDirectory(nBufLen,lpBuffer);                       //WNDCLASSEX struct to hold info about the data it
     s1=lpBuffer;                                                 //will display.  In the various procedures of this
     s1=s1+_T("\\Output.txt");                                    //program that accumulate ODBC data an estimate is
     fp=_tfopen(s1.lpStr(),_T("w"));                              //always made of the number of lines of output that
     _ftprintf(fp,_T("Entering fnWndProc_OnCreate()\n"));         //will be needed.  A memory allocation is made for
     _ftprintf(fp,_T("  s1.lpStr() = %s\n"),s1.lpStr());          //a pointer for each line to be displayed.  Further
     #if defined(UNICODE)                                         //allocations are made for each needed byte of each
         _ftprintf(fp,TEXT("  UNICODE Is Defined!\r\n"));         //line.  The pointer to the pointers are stored at
     #else                                                        //offset four in the cbWndExtra bytes, and the
         _ftprintf(fp,TEXT("  UNICODE Is Not Defined!\r\n"));     //of pointers (lines) are stored at offset zero.
     #endif                                                       //See the code in frmOutput_OnCreate() to see details
#endif                                                           //of what's stored there and how it is used.

_tcscpy(szClassName,_T("frmOutput"));                  //frmOutput is our Output Screen          //The output screen
wc.lpszClassName=szClassName;                          wc.lpfnWndProc=frmOutput;                 //contains scroll logic
wc.cbSize=sizeof (WNDCLASSEX);                         wc.style=CS_DBLCLKS;                      //that will allow you
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);               wc.hInstance=Wea->hIns;                   //to scroll through
wc.hIconSm=NULL;                                       wc.hCursor=LoadCursor(NULL,IDC_ARROW);    //the data.  The
wc.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);  wc.cbWndExtra=16;                         //frmOutput_OnClose()
wc.lpszMenuName=NULL;                                  wc.cbClsExtra=0;                          //logic de-allocates
RegisterClassEx(&wc);                                                                            //the memory for the
                                                                                                  //displayed data.
TCHAR* szCaption[]=
{
  _T("Display ODBC Drivers On Your Computer"),     //The program's interface consists only of these four buttons on the
  _T("Read Excel Spreadsheet With ODBC"),          //main screen.  You can see what the program does by reading the
  _T("Create, Load And Dump MS Access Database"),  //captions for the buttons just left.  The CreateWindow() calls to
  _T("Create, Load And Dump Sql Server Express")   //create the buttons are just below.
};
HINSTANCE hIns=((LPCREATESTRUCT)Wea->lParam)->hInstance;
DWORD dwStyle=WS_CHILD | WS_VISIBLE;
hCtl=CreateWindow(_T("button"),szCaption[0],dwStyle,30,20,325,30,Wea->hWnd,(HMENU)(IDC_SQL_DRIVERS),hIns,NULL);
hCtl=CreateWindow(_T("button"),szCaption[1],dwStyle,30,60,325,30,Wea->hWnd,(HMENU)(IDC_EXCEL),hIns,NULL);
hCtl=CreateWindow(_T("button"),szCaption[2],dwStyle,30,100,325,30,Wea->hWnd,(HMENU)(IDC_MS_ACCESS),hIns,NULL);
hCtl=CreateWindow(_T("button"),szCaption[3],dwStyle,30,140,325,30,Wea->hWnd,(HMENU)(IDC_SQL_SERVER_EXPRESS),hIns,NULL);
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Leaving fnWndProc_OnCreate()\n\n"));
#endif

return 0;
}


long fnWndProc_OnCommand(lpWndEventArgs Wea)
{
switch(LOWORD(Wea->wParam))
{
  case IDC_SQL_DRIVERS:
    btnSqlDrivers_OnClick(Wea);        //Found in SqlDrivers.cpp
    break;
  case IDC_EXCEL:
    btnExcel_OnClick(Wea);             //Found In Excel.cpp
    break;
  case IDC_MS_ACCESS:
    btnAccess_OnClick(Wea);            //Found in Access.cpp
    break;
  case IDC_SQL_SERVER_EXPRESS:
    btnSqlServerExpress_OnClick(Wea);  //Found in SqlServer.cpp
    break;
}

return 0;
}


long fnWndProc_OnClose(lpWndEventArgs Wea)
{
HCURSOR hArrow;
HWND hOutput;

#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Entering fnWndProc_OnClose()\n"));
#endif
hArrow=LoadCursor(NULL,IDC_ARROW);
SetClassLong(Wea->hWnd, GCL_HCURSOR, (LONG) hArrow);

//Note!   The code below sends WM_CLOSE messages to any Output windows the user may have failed to [X] out
//        of before before [X]'ing out of the main window.  Remember, the memory allocations for the lines
//        of text visible in the various output windows are stored in pointers in the frmOutput Windows
//        cbWndExtra bytes.  If this memory isn't properly released, it'll leak bad.

do        //Search And Destroy Mission For Any Sql Drivers Windows Hanging Around
{
  hOutput=FindWindowEx(0,0,_T("frmOutput"),_T("ODBC Database Drivers On Your System"));
  if(hOutput)
     SendMessage(hOutput,WM_CLOSE,0,0);
  else
     break;
}while(TRUE);

do        //Search And Destroy Mission For Any Excel Output Windows Hanging Around
{
  hOutput=FindWindowEx(0,0,_T("frmOutput"),_T("Data Dump Of Excel Spreadsheet With ODBC"));
  if(hOutput)
     SendMessage(hOutput,WM_CLOSE,0,0);
  else
     break;
}while(TRUE);

do        //Search And Destroy Mission For Any Access Output Windows Hanging Around
{
  hOutput=FindWindowEx(0,0,_T("frmOutput"),_T("Data Dump Of Access Database With ODBC"));
  if(hOutput)
     SendMessage(hOutput,WM_CLOSE,0,0);
  else
     break;
}while(TRUE);

do        //Search And Destroy Mission For Any Sql Server Output Windows Hanging Around
{
  hOutput=FindWindowEx(0,0,_T("frmOutput"),_T("ODBC Demo With Sql Server"));
  if(hOutput)
     SendMessage(hOutput,WM_CLOSE,0,0);
  else
     break;
}while(TRUE);

DestroyWindow(Wea->hWnd);
PostQuitMessage(WM_QUIT);
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Leaving fnWndProc_OnClose()\n\n"));
     fclose(fp);
#endif

return 0;
}


void AttachEventHandlers(void)      //This procedure is called from WinMain() and associates
{                                   //Windows messages with the procedure to handle them.
EventHandler[0].Code=WM_CREATE,    EventHandler[0].fnPtr=fnWndProc_OnCreate;
EventHandler[1].Code=WM_COMMAND,   EventHandler[1].fnPtr=fnWndProc_OnCommand;  //EventHandler is a struct that looks like this...
EventHandler[2].Code=WM_CLOSE,     EventHandler[2].fnPtr=fnWndProc_OnClose;    //
frmOutputHdlr[0].Code=WM_CREATE,   frmOutputHdlr[0].fnPtr=frmOutput_OnCreate;  //  struct EVENTHANDLER
frmOutputHdlr[1].Code=WM_PAINT,    frmOutputHdlr[1].fnPtr=frmOutput_OnPaint;   //  {
frmOutputHdlr[2].Code=WM_SIZE,     frmOutputHdlr[2].fnPtr=frmOutput_OnSize;    //   unsigned int    Code;   //i.e., WM_CREATE, etc
frmOutputHdlr[3].Code=WM_VSCROLL,  frmOutputHdlr[3].fnPtr=frmOutput_OnVScroll; //   long            (*fnPtr)(lpWndEventArgs);
frmOutputHdlr[4].Code=WM_CLOSE,    frmOutputHdlr[4].fnPtr=frmOutput_OnClose;   //  }
}


long __stdcall fnWndProc(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
WndEventArgs Wea;                           //No doubt about it, I'm a minimalist when it comes to Window Procedures.
                                             //I like to make them as small as possible, and don't like big switch
for(unsigned int i=0;i<3;i++)               //constructs (don't much like useless macros neither, or any other meaning-
{                                           //less crap).  The way this works is that when Windows calls this program's
     if(EventHandler[i].Code==msg)           //Window Procedure (fnWndProc()) the program iterates through the array of
     {                                       //EVENTHANDLER structs seeking a match between the msg sent to the WndProc
        Wea.hWnd=hwnd, Wea.lParam=lParam, Wea.wParam=wParam;   //and the .Code member of the EventHandler struct.  If a
        return (*EventHandler[i].fnPtr)(&Wea);                 //match is found, the appropriate event handler function
     }                                                         //is called through the function pointer member .fnPtr
}                                                             //set in AttachEventHandlers().

return (DefWindowProc(hwnd,msg,wParam,lParam));
}


int __stdcall WinMain(HINSTANCE hIns, HINSTANCE hPrevIns, LPSTR lpszArgument, int iShow)
{
TCHAR szClassName[]=TEXT("SqlDemo");
WNDCLASSEX wc;
MSG messages;
HWND hWnd;

AttachEventHandlers();
wc.lpszClassName=szClassName;                wc.lpfnWndProc=fnWndProc;
wc.cbSize=sizeof (WNDCLASSEX);               wc.style=CS_DBLCLKS;
wc.hIcon=LoadIcon(NULL,IDI_APPLICATION);     wc.hInstance=hIns;
wc.hIconSm=LoadIcon(NULL, IDI_APPLICATION);  wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.hbrBackground=(HBRUSH)COLOR_BTNSHADOW;    wc.cbWndExtra=0;
wc.lpszMenuName=NULL;                        wc.cbClsExtra=0;
RegisterClassEx(&wc);
hWnd=CreateWindow(szClassName,TEXT("ODBC Demo"),WS_OVERLAPPEDWINDOW,150,100,395,220,HWND_DESKTOP,0,hIns,0);
ShowWindow(hWnd,iShow);
while(GetMessage(&messages,NULL,0,0))
{
  TranslateMessage(&messages);
  DispatchMessage(&messages);
}

return messages.wParam;
}

Frederick J. Harris

#2

//WinTypes.h
#ifndef WINTYPES_H
#define WINTYPES_H

typedef struct    WindowsEventArguments
{
HWND             hWnd;
WPARAM           wParam;
LPARAM           lParam;
HINSTANCE        hIns;
}WndEventArgs,    *lpWndEventArgs;


struct EVENTHANDLER
{
unsigned int    Code;
long            (*fnPtr)(lpWndEventArgs);
};

#endif



//Strings.h
#if !defined(STRINGS_H)
#define STRINGS_H
#define EXPANSION_FACTOR      2
#define MINIMUM_ALLOCATION   16

class String
{
public:
~String();                               //String Destructor
String();                                //Uninitialized Constructor
String(const TCHAR);                     //Constructor Initializes String With TCHAR
String(const TCHAR*);                    //Constructor Initializes String With TCHAR*
String(const String&);                   //Constructor Initializes String With Another String (Copy Constructor)
String(const int);                       //Constructor Initializes Buffer To Specific Size
String& operator=(const TCHAR);          //Assigns TCHAR To String
String& operator=(const TCHAR*);         //Assigns TCHAR* To String
String& operator=(const String&);        //Assigns one String to another (this one)
bool operator==(const String);           //For comparing Strings
String& operator+(const TCHAR);          //For adding TCHAR to String
String& operator+(const TCHAR*);         //For adding null terminated TCHAR array to String
String& operator+(const String&);        //For adding one String to Another
String Left(unsigned int);               //Returns String of iNum Left Most chars of this
String Right(unsigned int);              //Returns String of iNum Right Most chars of this
String Mid(unsigned int, unsigned int);  //Returns String consisting of number of chars from some offset
String Remove(const TCHAR*, bool);       //Returns A String With A Specified TCHAR* Removed
int InStr(const TCHAR);                  //Returns one based offset of a specific TCHAR in a String
int InStr(const TCHAR*, bool);           //Returns one based offset of a particular TCHAR pStr in a String
int InStr(const String&, bool);          //Returns one based offset of where a particular String is in another String
String LTrim();                          //Returns String with leading spaces/tabs removed
String RTrim();                          //Returns String with spaces/tabs removed from end
String Trim();                           //Returns String with both leading and trailing whitespace removed
unsigned int ParseCount(const TCHAR);    //Returns count of Strings delimited by a TCHAR passed as a parameter
void Parse(String*, TCHAR);              //Returns array of Strings in first parameter as delimited by 2nd TCHAR delimiter
String CStr(const int);                  //Converts String to integer
String CStr(const unsigned int);         //Converts String to unsigned int
String CStr(const short int);            //Converts String to 16 bit int
String CStr(const double);               //Converts String to double
int iVal();                              //Returns int value of a String
int LenStr(void);                        //Returns the length of the String
TCHAR* lpStr();                          //Returns the address of the 1st byte of the String buffer this->pStrBuffer
void Print(bool);

protected:
TCHAR* pStrBuffer;
int    iAllowableCharacterCount;
};

#endif



//Strings.cpp
#include  <windows.h>
#include  <tchar.h>
#include  <stdlib.h>
#include  <stdio.h>
#include  <string.h>
#include  "Strings.h"


String::~String()                                      //String Destructor - frees a string's memory
{                                                      //allocation
delete [] pStrBuffer;
}


String::String()                                       //Uninitialized Constructor - For variable
{                                                      //declarations such as; String strName;
pStrBuffer=new TCHAR[MINIMUM_ALLOCATION];
pStrBuffer[0]=_T('\0');
this->iAllowableCharacterCount=MINIMUM_ALLOCATION-1;
}


String::String(const TCHAR ch)  //Constructor: Initializes with TCHAR
{
pStrBuffer=new TCHAR[16];
pStrBuffer[0]=ch;
pStrBuffer[1]=_T('\0');
iAllowableCharacterCount=MINIMUM_ALLOCATION-1;
}


String::String(const TCHAR* pStr)  //Constructor: Initializes with TCHAR*
{
int iLen,iNewSize;

iLen=_tcslen(pStr);
iNewSize=(iLen/16+1)*16;
pStrBuffer=new TCHAR[iNewSize];
this->iAllowableCharacterCount=iNewSize-1;
_tcscpy(pStrBuffer,pStr);
}


String::String(const String& s)  //Constructor Initializes With Another String, i.e., Copy Constructor
{
int iLen,iNewSize;

iLen=_tcslen(s.pStrBuffer);
iNewSize=(iLen/16+1)*16;
this->pStrBuffer=new TCHAR[iNewSize];
this->iAllowableCharacterCount=iNewSize-1;
_tcscpy(this->pStrBuffer,s.pStrBuffer);
}


String::String(const int iSize)  //Constructor Creates String With Custom Sized
{                                //Buffer (rounded up to paragraph boundary)
int iNewSize;

iNewSize=(iSize/16+1)*16;
pStrBuffer=new TCHAR[iNewSize];
this->iAllowableCharacterCount=iNewSize-1;
}


String& String::operator=(const TCHAR ch)  //Overloaded operator = for assigning a TCHARacter to a String
{
this->pStrBuffer[0]=ch;
this->pStrBuffer[1]=_T('\0');

return *this;
}


String& String::operator=(const TCHAR* pStr)   //Constructor For If Pointer To Asciiz String Parameter
{
int iLen,iNewSize;

iLen=_tcslen(pStr);
if(iLen<this->iAllowableCharacterCount)
    _tcscpy(pStrBuffer,pStr);
else
{
    delete [] pStrBuffer;
    iNewSize=(iLen/16+1)*16;
    pStrBuffer=new TCHAR[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
    _tcscpy(pStrBuffer,pStr);
}

return *this;
}


String& String::operator=(const String& strRight)    //Overloaded operator = for assigning
{                                                   //another String to a String
int iRightLen,iThisLen,iNewSize;
TCHAR* pNew;

if(this==&strRight)
    return *this;
iRightLen=_tcslen(strRight.pStrBuffer);
iThisLen=_tcslen(this->pStrBuffer);
if(iRightLen < this->iAllowableCharacterCount)
    _tcscpy(pStrBuffer,strRight.pStrBuffer);
else
{
    if(iThisLen) //There Is Something Stored In This!
    {
       iNewSize=iThisLen+iRightLen+1;
       iNewSize=(iNewSize/16+1)*16;
       pNew=new TCHAR[iNewSize];
       this->iAllowableCharacterCount=iNewSize-1;
       _tcscpy(pNew,pStrBuffer);
       _tcscat(pNew,strRight.pStrBuffer);
       delete [] this->pStrBuffer;
       pStrBuffer=pNew;
    }
    else
    {
       iNewSize=(iRightLen/16+1)*16;
       delete [] this->pStrBuffer;
       this->pStrBuffer=new TCHAR[iNewSize];
       this->iAllowableCharacterCount=iNewSize-1;
       _tcscpy(pStrBuffer,strRight.pStrBuffer);
    }
}

return *this;
}


bool String::operator==(const String strCompare)
{
if(_tcscmp(this->pStrBuffer,strCompare.pStrBuffer)==0)  //strcmp
    return true;
else
    return false;
}


String& String::operator+(const TCHAR ch)      //Overloaded operator + (Puts TCHAR in String)
{
int iLen,iNewSize;
TCHAR* pNew;

iLen=_tcslen(this->pStrBuffer);
if(iLen<this->iAllowableCharacterCount)
{
    this->pStrBuffer[iLen]=ch;
    this->pStrBuffer[iLen+1]=_T('\0');
}
else
{
    iNewSize=((this->iAllowableCharacterCount*EXPANSION_FACTOR)/16+1)*16;
    pNew=new TCHAR[iNewSize];
    this->iAllowableCharacterCount=iNewSize-1;
    _tcscpy(pNew,this->pStrBuffer);
    delete [] this->pStrBuffer;
    this->pStrBuffer=pNew;
    this->pStrBuffer[iLen]=ch;
    this->pStrBuffer[iLen+1]=_T('\0');
}

return *this;
}


String& String::operator+(const TCHAR* pChar) //Overloaded operator + (Adds TCHAR literals
{                                             //or pointers to Asciiz Strings)
int iLen,iNewSize;
TCHAR* pNew;

iLen=_tcslen(this->pStrBuffer)+_tcslen(pChar);
if(iLen<this->iAllowableCharacterCount)
    _tcscat(this->pStrBuffer,pChar);
else
{
    iNewSize=(iLen*EXPANSION_FACTOR/16+1)*16;
    pNew=new TCHAR[iNewSize];
    this->iAllowableCharacterCount = iNewSize-1;
    _tcscpy(pNew,this->pStrBuffer);
    delete [] pStrBuffer;
    _tcscat(pNew,pChar);
    this->pStrBuffer=pNew;
}

return *this;
}


String& String::operator+(const String& strRight)  //Overloaded operator + Adds Another String
{                                                  //to the left operand
int iLen,iNewSize;
TCHAR* pNew;

iLen=_tcslen(this->pStrBuffer) + _tcslen(strRight.pStrBuffer);
if(iLen < this->iAllowableCharacterCount)
{
    if(this->pStrBuffer)
       _tcscat(this->pStrBuffer,strRight.pStrBuffer);
    else
       _tcscpy(this->pStrBuffer,strRight.pStrBuffer);
}
else
{
    if(this->pStrBuffer)
    {
       iNewSize=(iLen*EXPANSION_FACTOR/16+1)*16;
       pNew=new TCHAR[iNewSize];
       this->iAllowableCharacterCount=iNewSize-1;
       _tcscpy(pNew,this->pStrBuffer);
       delete [] pStrBuffer;
       _tcscat(pNew,strRight.pStrBuffer);
       this->pStrBuffer=pNew;
    }
    else
    {
       iNewSize=(iLen*EXPANSION_FACTOR/16+1)*16;
       pNew=new TCHAR[iNewSize];
       this->iAllowableCharacterCount=iNewSize-1;
       _tcscpy(pNew,strRight.pStrBuffer);
       this->pStrBuffer=pNew;
    }
}

return *this;
}


String String::Left(unsigned int iNum)
{
unsigned int iLen,i,iNewSize;
String sr;

iLen=_tcslen(this->pStrBuffer);
if(iNum<iLen)
{
    iNewSize=(iNum*EXPANSION_FACTOR/16+1)*16;
    sr.iAllowableCharacterCount=iNewSize-1;
    sr.pStrBuffer=new TCHAR[iNewSize];
    for(i=0;i<iNum;i++)
        sr.pStrBuffer[i]=this->pStrBuffer[i];
    sr.pStrBuffer[iNum]=_T('\0');
    return sr;
}
else
{
    sr=*this;
    return sr;
}
}


String String::Remove(const TCHAR* pToRemove, bool blnCaseSensitive)
{
int i,j,iParamLen,iReturn=0;
bool blnFound=false;

if(*pToRemove==0)
    return true;
iParamLen=_tcslen(pToRemove);
i=0, j=0;
do
{
  if(pStrBuffer[i]==0)
     break;
  if(blnCaseSensitive)
     iReturn=_tcsncmp(pStrBuffer+i,pToRemove,iParamLen);  //strncmp
  else
     iReturn=_tcsnicmp(pStrBuffer+i,pToRemove,iParamLen); //_strnicmp
  if(iReturn!=0)
  {
     if(blnFound)
        pStrBuffer[j]=pStrBuffer[i];
     j++, i++;
  }
  else   //made a match
  {
     blnFound=true;
     i=i+iParamLen;
     pStrBuffer[j]=pStrBuffer[i];
     j++, i++;
  }
}while(1);
if(blnFound)
    pStrBuffer[i-iParamLen]=_T('\0');
String sr=pStrBuffer;

return sr;
}


String String::Right(unsigned int iNum)  //Returns Right$(strMain,iNum)
{
unsigned int iLen,i,j,iNewSize;
String sr;

iLen=_tcslen(this->pStrBuffer);
if(iNum<iLen)
{
    iNewSize=(iNum*EXPANSION_FACTOR/16+1)*16;
    sr.iAllowableCharacterCount=iNewSize-1;
    sr.pStrBuffer=new TCHAR[iNewSize];
    j=0;
    for(i=iLen-iNum;i<=iLen;i++)
    {
        _tprintf(_T("%u\t%u\t%c\n"),i,j,pStrBuffer[i]);
        sr.pStrBuffer[j]=this->pStrBuffer[i];
        j++;
    }
    sr.pStrBuffer[iNum]=_T('\0');
    return sr;
}
else
{
    sr=*this;
    return sr;
}
}


String String::Mid(unsigned int iStart, unsigned int iCount)
{
unsigned int iLen,i,j,iNewSize;
String sr;

iLen=_tcslen(this->pStrBuffer);
if(iStart && iStart<=iLen)
{
    if(iCount && iStart+iCount-1<=iLen)
    {
       iNewSize=(iCount*EXPANSION_FACTOR/16+1)*16;
       sr. iAllowableCharacterCount=iNewSize-1;
       sr.pStrBuffer=new TCHAR[iNewSize];
       j=0;
       sr.pStrBuffer=new TCHAR[iNewSize];
       for(i=iStart-1;i<iStart+iCount-1;i++)
       {
           sr.pStrBuffer[j]=this->pStrBuffer[i];
           j++;
       }
       sr.pStrBuffer[iCount]=_T('\0');
       return sr;
    }
    else
    {
       sr=*this;
       return sr;
    }
}
else
{
    sr=*this;
    return sr;
}
}


int String::InStr(const TCHAR ch)
{
int iLen,i;

iLen=_tcslen(this->pStrBuffer);
for(i=0;i<iLen;i++)
{
     if(this->pStrBuffer[i]==ch)
        return (i+1);
}

return 0;
}


int String::InStr(const TCHAR* pStr, bool blnCaseSensitive)
{
int i,iParamLen,iRange;

if(*pStr==0)
    return 0;
iParamLen=_tcslen(pStr);
iRange=_tcslen(pStrBuffer)-iParamLen;
if(iRange>=0)
{
    for(i=0;i<=iRange;i++)
    {
        if(blnCaseSensitive)
        {
           if(_tcsncmp(pStrBuffer+i,pStr,iParamLen)==0)   //strncmp
              return i+1;
        }
        else
        {
           if(_tcsnicmp(pStrBuffer+i,pStr,iParamLen)==0)  //_strnicmp
              return i+1;
        }
    }
}

return 0;
}


int String::InStr(const String& s, bool blnCaseSensitive)
{
int i,iParamLen,iRange,iLen;

iLen=_tcslen(s.pStrBuffer);
if(iLen==0)
    return 0;
iParamLen=iLen;
iRange=_tcslen(pStrBuffer)-iParamLen;
if(iRange>=0)
{
    for(i=0;i<=iRange;i++)
    {
        if(blnCaseSensitive)
        {
           if(_tcsncmp(pStrBuffer+i,s.pStrBuffer,iParamLen)==0)  //strncmp
              return i+1;
        }
        else
        {
           if(_tcsnicmp(pStrBuffer+i,s.pStrBuffer,iParamLen)==0) //_strnicmp
              return i+1;
        }
    }
}

return 0;
}


String String::LTrim()
{
unsigned int i,iCt=0,iLenStr;

iLenStr=this->LenStr();
for(i=0;i<iLenStr;i++)
{
     if(pStrBuffer[i]==32||pStrBuffer[i]==9)
        iCt++;
     else
        break;
}
if(iCt)
{
    for(i=iCt;i<=iLenStr;i++)
        pStrBuffer[i-iCt]=pStrBuffer[i];
}

return *this;
}


String String::RTrim()
{
unsigned int iCt=0, iLenStr;

iLenStr=this->LenStr()-1;
for(unsigned int i=iLenStr; i>0; i--)
{
     if(this->pStrBuffer[i]==32 || this->pStrBuffer[i]==9)
        iCt++;
     else
        break;
}
this->pStrBuffer[this->LenStr()-iCt]=0;

return *this;
}


String String::Trim()
{
this->LTrim();
this->RTrim();

return *this;
}


unsigned int String::ParseCount(const TCHAR c)  //returns one more than # of
{                                              //delimiters so it accurately
unsigned int iCtr=0;                          //reflects # of strings delimited
TCHAR* p;                                      //by delimiter.

p=this->pStrBuffer;
while(*p)
{
  if(*p==c)
     iCtr++;
  p++;
}

return ++iCtr;
}


void String::Parse(String* pStr, TCHAR delimiter)
{
unsigned int i=0;
TCHAR* pBuffer=0;
TCHAR* c;
TCHAR* p;

pBuffer=new TCHAR[this->LenStr()+1];
if(pBuffer)
{
    p=pBuffer;
    c=this->pStrBuffer;
    while(*c)
    {
     if(*c==delimiter)
     {
        pStr[i]=pBuffer;
        p=pBuffer;
        i++;
     }
     else
     {
        *p=*c;
        p++;
        *p=0;
     }
     c++;
    }
    pStr[i]=pBuffer;
    delete [] pBuffer;
}
}


int String::iVal()
{
return _ttoi(this->pStrBuffer);  //atoi
}


String String::CStr(const int iNum)
{
String sr;
_stprintf(sr.pStrBuffer,_T("%d"),iNum);
return sr;
}


String String::CStr(const unsigned int iNum)
{
String sr;
_stprintf(sr.pStrBuffer,_T("%u"),iNum);
return sr;
}


String String::CStr(const short int iNum)
{
String sr;
_stprintf(sr.pStrBuffer,_T("%d"),iNum);
return sr;
}


String String::CStr(const double dblNum)
{
String sr(32);
_stprintf(sr.pStrBuffer,_T("%f"),dblNum);
return sr;
}


int String::LenStr(void)
{
return _tcslen(this->pStrBuffer);
}


TCHAR* String::lpStr()
{
return pStrBuffer;
}

void String::Print(bool blnCrLf)
{
_tprintf(_T("%s"),pStrBuffer);
if(blnCrLf)
    _tprintf(_T("\n"));
}

Frederick J. Harris

#3

//SqlProcs.h
#ifndef SQL_PROCS_H
#define SQL_PROCS_H

unsigned int iInstallerError();
void MkDate(TIMESTAMP_STRUCT&, TCHAR*);
TIMESTAMP_STRUCT ParseDate(TCHAR*, TCHAR*, TCHAR*);

#endif




//SqlProcs.cpp
#include  <windows.h>
//#define   _UNICODE
#include  <tchar.h>
#include  <stdio.h>
#include  <odbcinst.h>  
#include  <sql.h>      
#include  <sqlext.h>    
#include  "Strings.h"
#include  "SqlProcs.h"


unsigned int iInstallerError()
{
DWORD pErr;
TCHAR szErrMsg[512];
WORD  cbMsgBuffer=512;
WORD  cbRet;
WORD  wErrNum=1;

while(SQLInstallerError(wErrNum,&pErr,szErrMsg,cbMsgBuffer,&cbRet)!=SQL_NO_DATA)
{
 wErrNum++;
};

return (unsigned int)pErr;
}


void MkDate(TIMESTAMP_STRUCT& ts, TCHAR* szBuffer)
{
TCHAR szMonth[4],szDay[4],szYear[8];

_stprintf(szMonth,_T("%u"),ts.month);
_stprintf(szDay,_T("%u"),ts.day);
_stprintf(szYear,_T("%u"),ts.year);
_tcscpy(szBuffer,szMonth);
_tcscat(szBuffer,_T("/"));
_tcscat(szBuffer,szDay);
_tcscat(szBuffer,_T("/"));
_tcscat(szBuffer,szYear);

return;
}


TIMESTAMP_STRUCT ParseDate(TCHAR* szDate, TCHAR* szFormat, TCHAR* szDelimiter)
{
UINT i=0,j=0,k=0;
TIMESTAMP_STRUCT ts;
TCHAR buf[3][8];             //buf[0] for month, buf[1] for day, buf[2] for year
TCHAR* p;

memset(buf,0,sizeof(buf));  //zero out buf[]
p=szDate;
for(i=0;i<_tcslen((TCHAR*)szDate);i++)
{
    if(*p!=*szDelimiter)
    {
       buf[j][k++]=*p;
       buf[j][k+1]=_T('\0');
    }
    else
    {
       j++;
       k=0;
    }
    p++;
}
if(!_tcsicmp((TCHAR*)szFormat,_T("MDY")))
{
   ts.month=(short)_ttoi(buf[0]);
   ts.day=(short)_ttoi(buf[1]);
   ts.year=(short)_ttoi(buf[2]);
}
if(!_tcsicmp((TCHAR*)szFormat,_T("DMY")))
{
   ts.day=(short)_ttoi(buf[0]);
   ts.month=(short)_ttoi(buf[1]);
   ts.year=(short)_ttoi(buf[2]);
}
if(!_tcsicmp((TCHAR*)szFormat,_T("YMD")))
{
   ts.year=(short)_ttoi(buf[0]);
   ts.month=(short)_ttoi(buf[1]);
   ts.day=(short)_ttoi(buf[2]);
}

return ts;
}




//Access.h
#ifndef MSACCESS_H
#define MSACCESS_H
void ErrorMemFree(TCHAR**, unsigned int);
long blnLineFailed(HWND, SQL&, TCHAR**, unsigned int&, TCHAR*);
unsigned int iCreateDB(TCHAR*);
unsigned int blnMakeTable(SQL&);
int GetRecordCount(SQL&, unsigned int&, LPCRITICAL_SECTION);
UINT blnInsert(SQL&, TCHAR**, unsigned int&, HWND, int, LPCRITICAL_SECTION);
UINT blnDumpData(SQL&, TCHAR**, unsigned int&, HWND, LPCRITICAL_SECTION);
void btnAccess_OnClick(lpWndEventArgs Wea);
#endif




//Access.cpp
#include  <windows.h>
//#define   _UNICODE
#include  <tchar.h>
#include  <stdio.h>
#include  <string.h>
#include  <process.h>
#include  <odbcinst.h>
#include  <sql.h>
#include  <sqlext.h>
#include  "WinTypes.h"
#include  "SqlProcs.h"
#include  "Strings.h"
#include  "Sql.h"
#include  "Main.h"
#include  "Access.h"
#if defined(MY_DEBUG)
    extern FILE* fp;
#endif


void ErrorMemFree(TCHAR** pLns, unsigned int iNum)   //If there are any memory allocation errors
{                                                    //anywhere along the way, this little thingy
unsigned int i,blnFree=0;                           //unravels all the allocations done up to the
                                                     //point where the 1st allocation error occurred.
for(i=0;i<=iNum;i++)
     blnFree=(unsigned int)GlobalFree(pLns[i]);
blnFree=(unsigned int)GlobalFree(pLns);
}


long blnLineFailed(HWND hWnd, SQL& Sql, TCHAR** ptrLines, unsigned int& iLine, TCHAR* pBuffer)
{
ptrLines[iLine]=(TCHAR*)GlobalAlloc(GPTR,(_tcslen(pBuffer)+1)*sizeof(TCHAR));
if(!ptrLines[iLine])
{
    MessageBox(hWnd,_T("Memory Allocation Error!"),_T("Not Good!"),MB_ICONERROR);
    ErrorMemFree(ptrLines,iLine);
    Sql.ODBCDisconnect();            //This procedure does a nice job of taking care of
    return TRUE;                     //the rather monotonous sequence of operations
}                                   //necessary allocate memory for a line of text, copy
_tcscpy(ptrLines[iLine],pBuffer);   //it to the allocated address, and increment the iLine
iLine++;                            //count variable

return FALSE;
}


unsigned int iCreateDB(TCHAR* szDBName)    //To create a Microsoft Access Database from
{                                          //scratch, you use SQLConfigDataSource().  I
TCHAR szCreate[256];                      //believe if the database already exists at the
                                           //specific location SQLInstallerError() returns
_tcscpy(szCreate,_T("CREATE_DB="));       //'11'.
_tcscat(szCreate,szDBName);
if(SQLConfigDataSource(0,ODBC_ADD_DSN,_T("Microsoft Access Driver (*.mdb)"),szCreate))
    return TRUE;
else
    return iInstallerError();
}


UINT blnMakeTable(SQL& Sql)          //Uses SQL Create Table statement to add table
{                                    //to database represented by sql->hConn
TCHAR szQuery[256];
SQLHSTMT hStmt;

_tcscpy(szQuery,_T("CREATE TABLE Table1 (Id LONG  NOT NULL PRIMARY KEY, Float_Point DOUBLE, Date_Field DATETIME, Text_Field CHAR(16));"));
SQLAllocHandle(SQL_HANDLE_STMT,Sql.hConn,&hStmt);
if(SQLExecDirect(hStmt,(SQLTCHAR*)szQuery,SQL_NTS)==0)
{
    SQLFreeHandle(SQL_HANDLE_STMT,hStmt);
    return(TRUE);
}
else
    return(FALSE);
}


int GetRecordCount(SQL& Sql, unsigned int& iLine)
{
unsigned int iRecCt=0;
TCHAR szQuery[128];
SQLHSTMT hStmt;
long iJnk;

_tcscpy(szQuery,_T("SELECT Count(*)  As RecordCount From Table1;"));
SQLAllocHandle(SQL_HANDLE_STMT,Sql.hConn,&hStmt);
SQLBindCol(hStmt,1,SQL_C_ULONG,&iRecCt,0,&iJnk);
if(SQLExecDirect(hStmt,(SQLTCHAR*)szQuery,SQL_NTS)!=SQL_SUCCESS)
{
    SQLGetDiagRec(SQL_HANDLE_STMT,hStmt,1,Sql.szErrCode,&Sql.iNativeErrPtr,Sql.szErrMsg,512,&Sql.iTextLenPtr);
    SQLFreeHandle(SQL_HANDLE_STMT,hStmt);
    return -1;
}
else
{
    SQLFetch(hStmt);
    SQLCloseCursor(hStmt);
    SQLFreeHandle(SQL_HANDLE_STMT,hStmt);
    return iRecCt;
}
}



UINT blnInsert(SQL& sql, TCHAR** ptrLines, unsigned int& iLine, HWND hWnd, int iCountExistingRecords)
{
TCHAR* szStr[]={_T("My Birthday"),_T("Walk On Moon?"),_T("Some String"),_T("April Fools Day")};
TCHAR* szDate[]={_T("11/15/1952"),_T("6/30/1969"),_T("1/1/2006"),_T("4/1/2006")};
double dblNum[]={3.14159,1.23456,15.1234,0.54321};
TCHAR  szQuery[100],szString[32],szBuffer[128];      //Let me give you a hint about something.  If you decide
SQLINTEGER iNts=SQL_NTS;                             //to use raw ODBC as your database access methodology, the
UINT i,id,iRet=FALSE;                                //hard part is SQLBindParameter() for inserting prepared
TIMESTAMP_STRUCT ts;                                 //SQL statements, and SQLBindCol() for selecting data.  These
SQLINTEGER iJnk;                                     //will inevitably take you some time to learn.  I chose an
SQLHSTMT hStmt;                                      //integer, a double, a data, and a char string so as to get
double dbl;                                          //you started on the most common data types.

#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Entering blnInsert()\n"));
     _ftprintf(fp,_T("  iCountExistingRecords = %u\n"),iCountExistingRecords);
#endif
if(SQLAllocHandle(SQL_HANDLE_STMT,sql.hConn,&hStmt)==SQL_SUCCESS)
{
    _tcscpy((TCHAR*)szQuery,_T("INSERT INTO Table1(Id, Float_Point, Date_Field, Text_Field) VALUES(?,?,?,?);"));
    if(blnLineFailed(hWnd,sql,ptrLines,iLine,szQuery))
       return FALSE;
    iLine++;
    if(blnLineFailed(hWnd,sql,ptrLines,iLine,_T("                                                         SQLExecute(hStmt)")))
       return FALSE;
    if(blnLineFailed(hWnd,sql,ptrLines,iLine,_T("iId      Double           Date           String           0=SQL_SUCCESS")))
       return FALSE;
    if(blnLineFailed(hWnd,sql,ptrLines,iLine,_T("========================================================================")))
       return FALSE;
    if(SQLPrepare(hStmt,(SQLTCHAR*)szQuery,SQL_NTS)==SQL_SUCCESS)
    {
       SQLBindParameter(hStmt,1,SQL_PARAM_INPUT,SQL_C_LONG,SQL_INTEGER,0,0,&id,0,&iJnk);
       SQLBindParameter(hStmt,2,SQL_PARAM_INPUT,SQL_C_DOUBLE,SQL_DOUBLE,0,0,&dbl,0,&iJnk);
       SQLBindParameter(hStmt,3,SQL_PARAM_INPUT,SQL_C_TYPE_DATE,SQL_TYPE_TIMESTAMP,16,0,&ts,0,&iJnk);
       SQLBindParameter(hStmt,4,SQL_PARAM_INPUT,SQL_C_TCHAR,SQL_CHAR,31,0,szString,32,&iNts);
       #if defined(MY_DEBUG)
           _ftprintf(fp,_T("\n                                                         SQLExecute(hStmt)\n"));
           _ftprintf(fp,_T("  iId      Double           Date           String           0=SQL_SUCCESS  \n"));
           _ftprintf(fp,_T("  ========================================================================\n"));
       #endif
       for(i=0;i<4;i++)
       {
           id=i+iCountExistingRecords, dbl=dblNum[i];
           ts=ParseDate(szDate[i],_T("mdy"),_T("/"));
           _tcscpy(szString,szStr[i]);
           if(SQLExecute(hStmt)==SQL_SUCCESS)
           {
              memset(szBuffer,0,128);
              _stprintf(szBuffer,_T("%-6u%8.2f           %-12.10s  %-20s%6u"),id,dbl,szDate[i],szString,SQL_SUCCESS);
              if(blnLineFailed(hWnd,sql,ptrLines,iLine,szBuffer))
                 return FALSE;
              #if defined(MY_DEBUG)
                  _ftprintf(fp,_T("  %u\t%f\t%s\t%s\t\t%u\n"),id,dbl,szDate[i],szString,SQL_SUCCESS);
              #endif
           }
           else
           {
              SQLGetDiagRec(SQL_HANDLE_STMT,hStmt,1,sql.szErrCode,&sql.iNativeErrPtr,sql.szErrMsg,512,&sql.iTextLenPtr);
              #if defined(MY_DEBUG)
                  _ftprintf(fp,_T("  sql.dr.szErrCode = %s\n"),sql.szErrCode);
                  _ftprintf(fp,_T("  sql.dr.szErrMsg  = %s\n"),sql.szErrMsg);
              #endif
              SQLFreeHandle(SQL_HANDLE_STMT,hStmt);
              return FALSE;
           }
       }
       iRet=TRUE;
       #if defined(MY_DEBUG)
           _ftprintf(fp,_T("\n"));
       #endif
    }
    SQLFreeHandle(SQL_HANDLE_STMT,hStmt);
}
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Leaving blnInsert()\n\n"));
#endif

return iRet;
}


UINT blnDumpData(SQL& sql, TCHAR** ptrLines, unsigned int& iLine, HWND hWnd)
{
TCHAR szQuery[100],szBuffer[128],szDate[12];
SQLTCHAR szString[16];
TIMESTAMP_STRUCT ts;
SQLINTEGER iJnk;
SQLHSTMT hStmt;
double dblNum;
UINT iId;

#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Entering blnDumpData()\n"));
#endif
if(SQLAllocHandle(SQL_HANDLE_STMT,sql.hConn,&hStmt)==SQL_SUCCESS)
{
    _tcscpy(szQuery,_T("SELECT Table1.Id,Table1.Float_Point,Table1.Date_Field,Table1.Text_Field FROM Table1;"));
    SQLBindCol(hStmt,1,SQL_C_ULONG,&iId,0,&iJnk);
    SQLBindCol(hStmt,2,SQL_C_DOUBLE,&dblNum,0,&iJnk);
    SQLBindCol(hStmt,3,SQL_C_TYPE_DATE,&ts,0,&iJnk);
    SQLBindCol(hStmt,4,SQL_C_TCHAR,szString,sizeof(TCHAR)*16,&iJnk);
    if(SQLExecDirect(hStmt,(SQLTCHAR*)szQuery,SQL_NTS)==SQL_SUCCESS)
    {
       if(blnLineFailed(hWnd,sql,ptrLines,iLine,_T("iId      Double           Date           String           0=SQL_SUCCESS")))
          return FALSE;
       if(blnLineFailed(hWnd,sql,ptrLines,iLine,_T("========================================================================")))
          return FALSE;
       do
       {
          if(SQLFetch(hStmt)==SQL_NO_DATA)
             break;
          memset(szBuffer,0,sizeof(TCHAR)*128);
          MkDate(ts,szDate);
          _stprintf(szBuffer,_T("%-6u%8.2f           %-12.10s  %-16s    %6u"),iId,dblNum,szDate,szString,SQL_SUCCESS);
          if(blnLineFailed(hWnd,sql,ptrLines,iLine,szBuffer))
             return FALSE;
       }  while(TRUE);
    }
    SQLCloseCursor(hStmt);
    SQLFreeHandle(SQL_HANDLE_STMT,hStmt);
    #if defined(MY_DEBUG)
        _ftprintf(fp,_T("Leaving blnDumpData()\n\n"));
    #endif
    return TRUE;
}
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Leaving blnDumpData()\n\n"));
#endif

return FALSE;
}


void AccessThread(void* pVoid)
{
unsigned int iLine=0,iScreenLinesNeeded=12,iReturn=0;
unsigned int iDatabaseReturn=0;
TCHAR** ptrLines=NULL;
HWND hMainWnd,hOutput;
TCHAR lpBuffer[512];
HCURSOR hArrow;
int iRecCt=0;
String s1;
SQL Sql;

#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Entering AccessThread()\n"));
#endif
hOutput=FindWindowEx(0,0,_T("frmOutput"),_T("Data Dump Of Access Database With ODBC"));
hMainWnd=FindWindowEx(0,0,TEXT("SqlDemo"),TEXT("ODBC Demo"));
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("  hMainWnd        = %u\n"),hMainWnd);
     _ftprintf(fp,_T("  hOutput         = %u\n"),hOutput);
#endif
if(hOutput)
{
    Sql.strDriver=_T("Microsoft Access Driver (*.mdb)");
    iReturn=GetCurrentDirectory(512,lpBuffer);
    Sql.strDBQ=lpBuffer;
    Sql.strDBQ = Sql.strDBQ + _T("\\") + _T("TestData.mdb");
    iDatabaseReturn=iCreateDB(Sql.strDBQ.lpStr());
    #if defined(MY_DEBUG)
        _ftprintf(fp,_T("  iDatabaseReturn = %u\n"),iDatabaseReturn);
    #endif
    Sql.ODBCConnect();
    if(iDatabaseReturn==1)  //Created New Access Database OK
    {
       if(Sql.blnConnected==TRUE)
       {
          if(blnMakeTable(Sql))
             iRecCt=GetRecordCount(Sql,iLine);
          #if defined(MY_DEBUG)
              _ftprintf(fp,_T("  iRecCt = %u\n"),iRecCt);
          #endif
          iScreenLinesNeeded=iScreenLinesNeeded+iRecCt+12;
          ptrLines=(TCHAR**)GlobalAlloc(GPTR,iScreenLinesNeeded*sizeof(TCHAR*));
          if(!ptrLines)
          {
             MessageBox(hMainWnd,_T("Memory Allocation Error!"),_T("Not Good!"),MB_ICONERROR);
             Sql.ODBCDisconnect();
             return;
          }
          SetWindowLong(hOutput,0,(long)iScreenLinesNeeded);
          SetWindowLong(hOutput,4,(long)ptrLines);
          if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,lpBuffer))
             return;
          if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,Sql.strConnectionString.lpStr()))
             return;
          if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,_T("ODBC Connection Succeeded, Database Successfully Created, Table1 Successfully Created!")))
             return;
          if(blnInsert(Sql,ptrLines,iLine,hMainWnd,++iRecCt))
          {
             iLine++;
             if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,_T("blnInsert() Succeeded!")))
                return;
          }
          else
          {
             iLine++;
             if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,_T("blnInsert() Failed!")))
             {
                hArrow=LoadCursor(NULL,IDC_ARROW);
                SetClassLong(hMainWnd, GCL_HCURSOR, (LONG) hArrow);
                return;
             }
          }
          iLine++;
          blnDumpData(Sql,ptrLines,iLine,hMainWnd);
          Sql.ODBCDisconnect();
       }
       return;
    }
    if(iDatabaseReturn==11)  //Database Apparently Already Exists!!!
    {
       if(Sql.blnConnected==TRUE)
       {
          iRecCt=GetRecordCount(Sql,iLine);
          #if defined(MY_DEBUG)
              _ftprintf(fp,_T("  iRecCt = %u\n"),iRecCt);
          #endif
          iScreenLinesNeeded=iScreenLinesNeeded+iRecCt+12;
          ptrLines=(TCHAR**)GlobalAlloc(GPTR,iScreenLinesNeeded*sizeof(TCHAR*));
          if(!ptrLines)
          {
             MessageBox(hMainWnd,_T("Memory Allocation Error!"),_T("Not Good!"),MB_ICONERROR);
             Sql.ODBCDisconnect();
             return;
          }
          SetWindowLong(hOutput,0,(long)iScreenLinesNeeded);
          SetWindowLong(hOutput,4,(long)ptrLines);
          if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,lpBuffer))
             return;
          if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,Sql.strConnectionString.lpStr()))
             return;
          if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,_T("ODBC Connection Succeeded, Database Successfully Created, Table1 Successfully Created!")))
             return;
          if(blnInsert(Sql,ptrLines,iLine,hMainWnd,++iRecCt))
          {
             iLine++;
             if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,_T("blnInsert() Succeeded!")))
                return;
          }
          else
          {
             iLine++;
             if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,_T("blnInsert() Failed!")))
                return;
          }
          iLine++;
          blnDumpData(Sql,ptrLines,iLine,hMainWnd);
          Sql.ODBCDisconnect();
          return;
       }
       else
       {
          MessageBox
          (
           hMainWnd,
           _T("There is a problem.  You may be trying to run SqlDemo from a directory ")
           _T("where Windows Won't allow databases to be created.  Documents and Settings ")
           _T("and Program Files Are two such places.  Try putting SqlDemo.exe into some.")
           _T("folder which isn't one of Windows 'Special' folders."),
           _T("Couldn't Open Database.  It May Not Exist!"), MB_ICONERROR
          );
          hArrow=LoadCursor(NULL,IDC_ARROW);
          SetClassLong(hMainWnd,GCL_HCURSOR,(LONG)hArrow);
          return;
        }
    }
}
}


void btnAccess_OnClick(lpWndEventArgs Wea)
{
unsigned long hThread;
TCHAR lpBuffer[512];
HCURSOR hCursor;
HWND hOutput;

#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Entering btnAccess_OnClick()\n"));
#endif
EnableWindow(GetDlgItem(Wea->hWnd,IDC_MS_ACCESS),FALSE);
hCursor=LoadCursor(NULL,IDC_WAIT);
SetClassLong(Wea->hWnd, GCL_HCURSOR, (LONG) hCursor);
SetCursor(hCursor);
_tcscpy(lpBuffer,_T("Data Dump Of Access Database With ODBC"));
hOutput=CreateWindowEx(0,_T("frmOutput"),lpBuffer,WS_OVERLAPPEDWINDOW|WS_VSCROLL,200,500,775,275,HWND_DESKTOP,0,GetModuleHandle(NULL),Wea->hWnd);
hThread=_beginthread(AccessThread,0,0);
WaitForSingleObject((HANDLE)hThread,INFINITE);
ShowWindow(hOutput,SW_SHOWNORMAL);
hCursor=LoadCursor(NULL,IDC_ARROW);
SetClassLong(Wea->hWnd,GCL_HCURSOR,(LONG)hCursor);
SetCursor(hCursor);
EnableWindow(GetDlgItem(Wea->hWnd,IDC_MS_ACCESS),TRUE);
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Leaving btnAccess_OnClick()\n\n"));
#endif
}


Frederick J. Harris


//Excel.h
#ifndef EXCEL_H
#define EXCEL_H

int GetExcelRecordCount(SQL&, unsigned int&);
UINT blnDumpExcelData(SQL&, TCHAR**, unsigned int&, HWND);
void btnExcel_OnClick(lpWndEventArgs);

#endif



#include  <windows.h>
//#define   _UNICODE
#include  <tchar.h>
#include  <stdio.h>
#include  <string.h>
#include  <process.h>
#include  <sql.h>                        //ODBC header file
#include  <sqlext.h>                     //ODBC header file
#include  "WinTypes.h"
#include  "SqlProcs.h"
#include  "Strings.h"
#include  "Sql.h"
#include  "Main.h"
#include  "Excel.h"
#include  "Access.h"

#if defined(MY_DEBUG)
    extern   FILE* fp;
#endif


int GetExcelRecordCount(SQL& Sql, unsigned int& iLine)
{
unsigned int iRecCt=0;
TCHAR szQuery[128];
SQLHSTMT hStmt;
long iJnk;

_tcscpy(szQuery,_T("SELECT Count(*)  As RecordCount From [Sheet1$];"));
SQLAllocHandle(SQL_HANDLE_STMT,Sql.hConn,&hStmt);
SQLBindCol(hStmt,1,SQL_C_ULONG,&iRecCt,0,&iJnk);
if(SQLExecDirect(hStmt,(SQLTCHAR*)szQuery,SQL_NTS)!=SQL_SUCCESS)
{
    SQLGetDiagRec(SQL_HANDLE_STMT,hStmt,1,Sql.szErrCode,&Sql.iNativeErrPtr,Sql.szErrMsg,512,&Sql.iTextLenPtr);                                                         
    SQLFreeHandle(SQL_HANDLE_STMT,hStmt);
    return -1;
}
else
{
    SQLFetch(hStmt);
    SQLCloseCursor(hStmt);
    SQLFreeHandle(SQL_HANDLE_STMT,hStmt);
    return iRecCt;
}
}


UINT blnDumpExcelData(SQL& sql, TCHAR** ptrLines, unsigned int& iLine, HWND hWnd)
{
SQLHSTMT          hStmt;
TCHAR             szQuery[100];
TCHAR             szDate[16];
SQLINTEGER        iJnk;
UINT              iId;
double            dblNum;
TIMESTAMP_STRUCT  ts;
SQLTCHAR          szString[64];
TCHAR             szBuffer[128];

if(SQLAllocHandle(SQL_HANDLE_STMT,sql.hConn,&hStmt)==SQL_SUCCESS)
{
    _tcscpy(szQuery,_T("SELECT Id, Float_Point, Date_Field, Text_Field FROM [Sheet1$];"));
    SQLBindCol(hStmt,1,SQL_C_ULONG,&iId,0,&iJnk);
    SQLBindCol(hStmt,2,SQL_C_DOUBLE,&dblNum,0,&iJnk);
    SQLBindCol(hStmt,3,SQL_C_TYPE_DATE,&ts,0,&iJnk);
    SQLBindCol(hStmt,4,SQL_C_TCHAR,szString,64,&iJnk);
    iLine++;

    //Allocate Heading
    ptrLines[iLine]=(TCHAR*)GlobalAlloc(GPTR,48*sizeof(TCHAR));
    if(!ptrLines[iLine])
    {
       MessageBox(hWnd,_T("Memory Allocation Error!"),_T("Not Good!"),MB_ICONERROR);
       ErrorMemFree(ptrLines,iLine);
       sql.ODBCDisconnect();
       return FALSE;
    }
    _tcscpy(ptrLines[iLine],_T("iId      Double           Date           String"));
    iLine++;

    //Allocate Equal ( = ) Sign String Seperator
    ptrLines[iLine]=(TCHAR*)GlobalAlloc(GPTR,55*sizeof(TCHAR));
    if(!ptrLines[iLine])
    {
       MessageBox(hWnd,_T("Memory Allocation Error!"),_T("Not Good!"),MB_ICONERROR);
       ErrorMemFree(ptrLines,iLine);
       sql.ODBCDisconnect();
       return FALSE;
    }
    _tcscpy(ptrLines[iLine],_T("======================================================"));
    iLine++;

    if(SQLExecDirect(hStmt,(SQLTCHAR*)szQuery,SQL_NTS)==SQL_SUCCESS)
    {
       while(SQLFetch(hStmt)!=SQL_NO_DATA)
       {
        MkDate(ts,szDate);
        memset(szBuffer,0,128);
        _stprintf(szBuffer,_T("%-6u%8.2f           %-12.10s  %-20s"),iId,dblNum,szDate,szString);
        ptrLines[iLine]=(TCHAR*)GlobalAlloc(GPTR,(_tcslen(szBuffer)+1)*sizeof(TCHAR));
        if(!ptrLines[iLine])
        {
           MessageBox(hWnd,_T("Memory Allocation Error!"),_T("Not Good!"),MB_ICONERROR);
           ErrorMemFree(ptrLines,iLine);
           sql.ODBCDisconnect();
           return FALSE;
        }
        _tcscpy(ptrLines[iLine],szBuffer);
        iLine++;
       }
    }
    SQLCloseCursor(hStmt);
    SQLFreeHandle(SQL_HANDLE_STMT,hStmt);
    return TRUE;
}

return FALSE;
}


void ExcelThread(void* pVoid)
{
unsigned int iLine=0,iScreenLinesNeeded=10;
HWND hMainWnd,hOutput;
TCHAR** ptrLines=NULL;
TCHAR lpBuffer[512];
DWORD nBufLen=512;
int iRecCt=0;
String s1;
SQL Sql;

#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Entering ExcelThread()\n"));
#endif
hOutput=FindWindowEx(0,0,_T("frmOutput"),_T("Data Dump Of Excel Spreadsheet With ODBC"));
hMainWnd=FindWindowEx(0,0,TEXT("SqlDemo"),TEXT("ODBC Demo"));
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("  hMainWnd   = %u\n"),hMainWnd);
     _ftprintf(fp,_T("  hOutput    = %u\n"),hOutput);
#endif
Sql.strDriver=_T("Microsoft Excel Driver (*.xls)");
GetCurrentDirectory(nBufLen,lpBuffer);
Sql.strDBQ = lpBuffer;
Sql.strDBQ = Sql.strDBQ +_T("\\") + _T("Book1.xls");
Sql.ODBCConnect();
if(Sql.blnConnected==TRUE)
{
    #if defined(MY_DEBUG)
        _ftprintf(fp,_T("  Sql.blnConnected=TRUE\n"));
    #endif
    iRecCt=GetExcelRecordCount(Sql,iLine);
    iScreenLinesNeeded=iScreenLinesNeeded+iRecCt+5;
    ptrLines=(TCHAR**)GlobalAlloc(GPTR,iScreenLinesNeeded*sizeof(TCHAR*));
    if(!ptrLines)
    {
       MessageBox(hMainWnd,_T("Memory Allocation Error!"),_T("Not Good!"),MB_ICONERROR);
       Sql.ODBCDisconnect();
       return;
    }
    SetWindowLong(hOutput,0,iScreenLinesNeeded);
    SetWindowLong(hOutput,4,(long)ptrLines);

    //Make First Output Line For frmOutput
    ptrLines[0]=(TCHAR*)GlobalAlloc(GPTR,(_tcslen(lpBuffer)+1)*sizeof(TCHAR));
    if(!ptrLines[0])
    {
       MessageBox(hMainWnd,_T("Memory Allocation Error!"),_T("Not Good!"),MB_ICONERROR);
       ErrorMemFree(ptrLines,iLine);
       Sql.ODBCDisconnect();
       return;
    }
    _tcscpy(ptrLines[0],lpBuffer);
    iLine++;

    //Make Second Output Line For frmOutput
    ptrLines[1]=(TCHAR*)GlobalAlloc(GPTR,(_tcslen(Sql.strConnectionString.lpStr())+1)*sizeof(TCHAR));
    //ptrLines[1]=(TCHAR*)GlobalAlloc(GPTR,(_tcslen(Sql.szCnIn)+1)*sizeof(TCHAR));
    if(!ptrLines[1])
    {
       MessageBox(hMainWnd,_T("Memory Allocation Error!"),_T("Not Good!"),MB_ICONERROR);
       ErrorMemFree(ptrLines,iLine);
       Sql.ODBCDisconnect();
       return;
    }
    //_tcscpy(ptrLines[1],Sql.szCnIn);
    _tcscpy(ptrLines[1],Sql.strConnectionString.lpStr());
    iLine++;

    //Make Third Output Line For frmOutput
    ptrLines[2]=(TCHAR*)GlobalAlloc(GPTR,27*sizeof(TCHAR));
    if(!ptrLines[2])
    {
       MessageBox(hMainWnd,_T("Memory Allocation Error!"),_T("Not Good!"),MB_ICONERROR);
       ErrorMemFree(ptrLines,iLine);
       Sql.ODBCDisconnect();
       return;
    }
    _tcscpy(ptrLines[2],_T("ODBC Connection Succeeded!"));
    iLine++;
    if(iRecCt>0)
       blnDumpExcelData(Sql,ptrLines,iLine,hMainWnd);
    Sql.ODBCDisconnect();
}
else
{
    #if defined(MY_DEBUG)
        _ftprintf(fp,_T("  Sql.blnConnected=FALSE\n"));
    #endif
}
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Leaving ExcelThread()\n"));
#endif
}


void btnExcel_OnClick(lpWndEventArgs Wea)
{
unsigned long hThread;
HCURSOR hCursor;
TCHAR szVar[48];
HINSTANCE hIns;
HWND hOutput;

#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Entering btnExcel_OnClick()\n"));
#endif
hCursor=LoadCursor(NULL,IDC_WAIT);
SetClassLong(Wea->hWnd,GCL_HCURSOR,(LONG)hCursor);
hIns=GetModuleHandle(NULL);
_tcscpy(szVar,_T("Data Dump Of Excel Spreadsheet With ODBC"));
hOutput=CreateWindow(_T("frmOutput"),szVar,WS_OVERLAPPEDWINDOW,200,500,725,275,0,0,hIns,0);
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("  hMainWnd   = %u\n"),Wea->hWnd);
     _ftprintf(fp,_T("  hOutput    = %u\n"),hOutput);
#endif
hThread=_beginthread(ExcelThread,0,0);
WaitForSingleObject((HANDLE)hThread,INFINITE);
ShowWindow(hOutput,SW_SHOWNORMAL);
hCursor=LoadCursor(NULL,IDC_ARROW);
SetClassLong(Wea->hWnd,GCL_HCURSOR,(LONG)hCursor);
SetCursor(hCursor);
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Leaving btnExcel_OnClick()\n\n"));
#endif
}

Frederick J. Harris

#5

//SqlServer.h
#ifndef SQLSERVER_H
#define SQLSERVER_H

unsigned int iCreateDB(TCHAR*);
unsigned int blnMakeTable(SQL&);  
int GetRecordCount(SQL&, unsigned int&);
unsigned int blnInsert(SQL&, TCHAR**, unsigned int&, HWND, int);
unsigned int blnDumpData(SQL&, TCHAR**, unsigned int&, HWND);
void btnAccess_OnClick(lpWndEventArgs);
void btnSqlServerExpress_OnClick(lpWndEventArgs);
void btnSqlServerMSDE_OnClick(lpWndEventArgs);

#endif



//SqlServer.cpp
#include   <windows.h>
//#define    _UNICODE
#include   <tchar.h>
#include   <shlobj.h>
#include   <stdio.h>
#include   <string.h>
#include   <process.h>
#include   <sql.h>                        //ODBC header file
#include   <sqlext.h>                     //ODBC header file
#include   "Main.h"
#include   "WinTypes.h"
#include   "SqlProcs.h"
#include   "Strings.h"
#include   "Sql.h"
#include   "SqlServer.h"
#include   "Access.h"
#if defined(MY_DEBUG)
    extern FILE* fp;
#endif


long blnCreateSqlServerExpressDB(SQL& Sql, TCHAR* szDBName)
{
TCHAR lpBuffer[MAX_PATH];
DWORD nBufLen=MAX_PATH;
SQLHSTMT hStmt;
String s1,s2;

// C:\Program Files\Microsoft SQL Server\MSSQL.1\MSSQL\Data\   << Path To SQL Server Express On My HP Laptop
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("  Entering blnCreateSqlServerExpressDatabase()\n"));
#endif
GetCurrentDirectory(nBufLen,lpBuffer);
s1=lpBuffer;
s1=s1+_T("\\");
s2 = _T("CREATE DATABASE ");
s2 = s2 + szDBName + _T(" ON (NAME=") + _T("'") + szDBName + _T("',");
s2 = s2 + _T("FILENAME=") + _T("'") + s1 + szDBName + _T(".mdf',SIZE=10,MAXSIZE=50,FILEGROWTH=5) LOG ON (NAME='");
s2 = s2 + szDBName + _T("Log',FILENAME='") + s1 + szDBName + _T(".ldf',SIZE=5,MAXSIZE=25,FILEGROWTH=5MB);");
SQLAllocHandle(SQL_HANDLE_STMT,Sql.hConn,&hStmt);
if(SQLExecDirect(hStmt,(SQLTCHAR*)s2.lpStr(),SQL_NTS)==SQL_ERROR)
{
    #if defined(MY_DEBUG)
        _ftprintf(fp,_T("  SQLExecuteDirect() Failed On Database Creation!\n"));
    #endif
    SQLGetDiagRec(SQL_HANDLE_STMT,hStmt,1,Sql.szErrCode,&Sql.iNativeErrPtr,Sql.szErrMsg,512,&Sql.iTextLenPtr);
    #if defined(MY_DEBUG)
        _ftprintf(fp,_T("  Sql.szErrCode     = %s\n"),Sql.szErrCode);
        _ftprintf(fp,_T("  Sql.szErrMsg      = %s\n"),Sql.szErrMsg);
        _ftprintf(fp,_T("  Sql.iNativeErrPtr = %d\n"),Sql.iNativeErrPtr);
    #endif
    SQLFreeHandle(SQL_HANDLE_STMT,hStmt);
    #if defined(MY_DEBUG)
        _ftprintf(fp,_T("  Leaving blnCreateSqlServerExpressDatabase()\n\n"));
    #endif
    return(Sql.iNativeErrPtr);
}
else
{
    SQLFreeHandle(SQL_HANDLE_STMT,hStmt);
    #if defined(MY_DEBUG)
        _ftprintf(fp,_T("  Leaving blnCreateSqlServerExpressDatabase()\n\n"));
    #endif
    return(TRUE);
}
}


long blnCreateSqlServerTable(SQL& Sql)
{
TCHAR szQuery[256];
SQLHSTMT hStmt;

#if defined(MY_DEBUG)
    _ftprintf(fp,_T("  Entering blnCreateSqlServerTable()\n\n"));
#endif
_tcscpy(szQuery,_T("CREATE TABLE Table1 (Id int  NOT NULL, Float_Point float NULL, Date_Field smalldatetime NULL, Text_Field nvarchar(32) NULL);"));
SQLAllocHandle(SQL_HANDLE_STMT,Sql.hConn,&hStmt);
if(SQLExecDirect(hStmt,(SQLTCHAR*)szQuery,SQL_NTS)==0)
{
    SQLFreeHandle(SQL_HANDLE_STMT,hStmt);
    #if defined(MY_DEBUG)
        _ftprintf(fp,_T("  Leaving blnCreateSqlServerTable()\n\n"));
    #endif
    return(TRUE);
}
else
{
    SQLGetDiagRec(SQL_HANDLE_STMT,hStmt,1,Sql.szErrCode,&Sql.iNativeErrPtr,Sql.szErrMsg,512,&Sql.iTextLenPtr);
    #if defined(MY_DEBUG)
        _ftprintf(fp,_T("  Sql.szErrCode = %s\n"),Sql.szErrCode);
        _ftprintf(fp,_T("  Sql.szErrMsg  = %s\n"),Sql.szErrMsg);
    #endif
    SQLFreeHandle(SQL_HANDLE_STMT,hStmt);
    #if defined(MY_DEBUG)
        _ftprintf(fp,_T("  Leaving blnCreateSqlServerTable()\n\n"));
    #endif
    return(FALSE);
}
}


void SqlServerThread(void* pVoid)
{
unsigned int iLine=0,iScreenLinesNeeded=30;
int iRecCt=0,iReturn=0;
HWND hMainWnd,hOutput;
TCHAR** ptrLines=NULL;
TCHAR szOutput[512];
TCHAR szBuffer[512];
TCHAR lpBuffer[512];
DWORD nSize=512;
String s1;
SQL Sql;

#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Entering SqlServerThread()\n"));
#endif
hOutput=FindWindowEx(0,0,_T("frmOutput"),_T("ODBC Demo With Sql Server"));
hMainWnd=FindWindowEx(0,0,TEXT("SqlDemo"),TEXT("ODBC Demo"));
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("  hMainWnd   = %u\n"),hMainWnd);
     _ftprintf(fp,_T("  hOutput    = %u\n"),hOutput);
#endif
Sql.strDriver=_T("SQL Server");

//For SQL Server Express
GetComputerName(lpBuffer,&nSize);                     //If you want this
Sql.strServer=lpBuffer;                               //to work for MSDE
Sql.strServer = Sql.strServer + _T("\\SQLEXPRESS");   //then REM these 3
//End For SQL Server Express                          //lines out.

//For MSDE                                            //For MSDE just use
//Sql.strServer=_T("localhost");                      //"localhost" for SERVER
//End MSDE

Sql.ODBCConnect();   //To connect to SQL Server without connecting to any particular database, all you
if(Sql.blnConnected) //need is "SQL Server" for the DRIVER= attribute, and YourComputerName\SQLEXPRESS
{                    //for the SERVER= attribute.
    iReturn=blnCreateSqlServerExpressDB(Sql,_T("TestData"));
    #if defined(MY_DEBUG)
        _ftprintf(fp,_T("  iReturn(blnCreateSQLServerExpressDB()) = %d\n"),iReturn);
    #endif
    switch(iReturn)
    {
     case 1:   //If blnCreateSQLServerExpressDB() returns 1, the database was successfully created
       {
          iScreenLinesNeeded=25;
          SetWindowLong(hOutput,0,iScreenLinesNeeded);
          ptrLines=(TCHAR**)GlobalAlloc(GPTR,iScreenLinesNeeded*sizeof(TCHAR*));
          SetWindowLong(hOutput,4,(long)ptrLines);
          _stprintf(szOutput,_T("Sql.strDriver            = %s"),Sql.strDriver.lpStr());
          if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,szOutput))
             return;
          _stprintf(szOutput,_T("Sql.strServer            = %s"),Sql.strServer.lpStr());
          if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,szOutput))
             return;
          _stprintf(szOutput,_T("Sql.strConnectionString  = %s"),Sql.strConnectionString.lpStr());
          if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,szOutput))
             return;
          Sql.ODBCDisconnect();
          iReturn=GetCurrentDirectory(512,szBuffer);
          _stprintf(szOutput,_T("Current Directory = %s"),szBuffer);
          if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,szOutput))
             return;
          Sql.strDBQ=szBuffer;
          Sql.strDBQ = Sql.strDBQ +_T("\\") + _T("TestData.mdf");
          Sql.strDatabase = _T("TestData");
          Sql.ODBCConnect();
          if(Sql.blnConnected)
          {
             if(blnCreateSqlServerTable(Sql))
             {
                iRecCt++;
                if(blnInsert(Sql,ptrLines,iLine,hMainWnd,iRecCt))
                {
                   iLine++;
                   if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,_T("blnInsert() Succeeded!")))
                      return;
                }
                else
                {
                   iLine++;
                   if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,_T("blnInsert() Failed!")))
                      return;
                }
                iLine++;
                blnDumpData(Sql,ptrLines,iLine,hMainWnd);
             }
             Sql.ODBCDisconnect();
          }
          break;
       }
     case 1801:    //database already exists, so we just will add some more data to it.  If
       {           //TestData already exists, the Sql.dr.iNativeErrPtr return is 1801.
          Sql.ODBCDisconnect();
          iReturn=GetCurrentDirectory(512,szBuffer);
          Sql.strDBQ=szBuffer;
          Sql.strDBQ = Sql.strDBQ + _T("\\TestData.mdf");
          Sql.strDatabase = _T("TestData");
          Sql.ODBCConnect();
          if(Sql.blnConnected)
          {
             iRecCt=GetRecordCount(Sql,iLine);
             #if defined(MY_DEBUG)
                 _ftprintf(fp,_T("  iRecCt = %u\n"),iRecCt);
             #endif
             if(iRecCt!=-1)
             {
                iScreenLinesNeeded=25+iRecCt;
                SetWindowLong(hOutput,0,iScreenLinesNeeded);
                ptrLines=(TCHAR**)GlobalAlloc(GPTR,iScreenLinesNeeded*sizeof(TCHAR*));
                SetWindowLong(hOutput,4,(long)ptrLines);
                if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,_T("Failed To Create SQL Server Database!")))
                   return;
                if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,_T("TestData Already Exists In Sql Server!")))
                   return;
                _stprintf(szOutput,_T("Sql.strDriver           = %s"),Sql.strDriver.lpStr());
                if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,szOutput))                      //output cn.szDriver
                   return;
                _stprintf(szOutput,_T("Sql.strServer           = %s"),Sql.strServer.lpStr());
                if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,szOutput))                      //output cn.szServer
                   return;
                _stprintf(szOutput,_T("Sql.strConnectionString = %s"),Sql.strConnectionString.lpStr());
                if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,szOutput))                   //output Sql.szCnIn
                   return;
                iRecCt++;
                if(blnInsert(Sql,ptrLines,iLine,hMainWnd,iRecCt))
                {
                   iLine++;
                   if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,_T("blnInsert() Succeeded!")))
                      return;
                }
                else
                {
                   iLine++;
                   if(blnLineFailed(hMainWnd,Sql,ptrLines,iLine,_T("blnInsert() Failed!")))
                      return;
                }
             }
             iLine++;
             blnDumpData(Sql,ptrLines,iLine,hMainWnd);
             Sql.ODBCDisconnect();
          }
          break;
       }
     case 5133:       //Can't create database here!!!!!!!!!!!!!!!!!!!!  It doesn't appear that SQL Server
       {              //is willing to create databases just anywhere.  It flat out refuses to create any
          MessageBox  //for example under the Documents And Settings folder.  That includes your My Documents,
          (           //Desktop, etc.  The same also appears to be true about Program Files, except for
           hMainWnd,  //the Sql Server installation folder.
           _T("There is a problem.  You may be trying to run SqlDemo from a directory ")
           _T("where Windows Won't allow databases to be created.  Documents and Settings ")
           _T("and Program Files Are two such places.  Try putting SqlDemo.exe into some.")
           _T("folder which isn't one of Windows 'Special' folders."),
           _T("Couldn't Open Or Create Database.  It May Not Exist!"), MB_ICONERROR
          );
          break;
       }
     default:
       {
          MessageBox
          (
           hMainWnd,
           _T("There is a problem.  An unknown error for which this application can provide no further")
           _T("resolution has occurred.  I'm very sorry about it.  My programs are like that, unfortunately."),
           _T("Unspecified Error (The Worst Kind)"),
           MB_ICONERROR
          );
       }
    }
    Sql.ODBCDisconnect();
}
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Leaving SqlServerThread()\n\n"));
#endif
}


void btnSqlServerExpress_OnClick(lpWndEventArgs Wea)
{
HCURSOR hHourGlass,hArrow;
unsigned long hThread;
TCHAR szVar[32];
HWND hOutput;

#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Entering btnSqlServerExpress_OnClick()\n"));
     _ftprintf(fp,_T("  GetTickCount() = %u\n"),GetTickCount());
#endif
hHourGlass=LoadCursor(NULL,IDC_WAIT);
SetClassLong(Wea->hWnd, GCL_HCURSOR, (LONG) hHourGlass);
SetCursor(hHourGlass);
EnableWindow(GetDlgItem(Wea->hWnd,IDC_SQL_SERVER_EXPRESS),FALSE);
_tcscpy(szVar,_T("ODBC Demo With Sql Server"));
hOutput=CreateWindow(_T("frmOutput"),szVar,WS_OVERLAPPEDWINDOW|WS_VSCROLL,200,500,775,275,0,0,GetModuleHandle(NULL),Wea->hWnd);
hThread=_beginthread(SqlServerThread,0,0);
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("  hThread = %u\n"),hThread);
#endif
WaitForSingleObject((HANDLE)hThread,INFINITE);
ShowWindow(hOutput,SW_SHOWNORMAL);
hArrow=LoadCursor(NULL,IDC_ARROW);
SetClassLong(Wea->hWnd, GCL_HCURSOR, (LONG) hArrow);
SetCursor(hArrow);
EnableWindow(GetDlgItem(Wea->hWnd,IDC_SQL_SERVER_EXPRESS),TRUE);
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("  GetTickCount() = %u\n"),GetTickCount());
     _ftprintf(fp,_T("Leaving btnSqlServerExpress_OnClick()\n\n"));
#endif
}


Frederick J. Harris


//SqlDrivers.h
#ifndef SQLDRIVERS_H
#define SQLDRIVERS_H
void btnSqlDrivers_OnClick(lpWndEventArgs);
#endif




//SqlDrivers.cpp
#include   <windows.h>
//#define    _UNICODE
#include   <tchar.h>
#include   <stdio.h>
#include   <string.h>
#include   <sql.h>                        //ODBC header file
#include   <sqlext.h>                     //ODBC header file
#include   "WinTypes.h"
#include   "SqlProcs.h"
#include   "Strings.h"
#include   "Main.h"
#include   "Sql.h"
#include   "Access.h"
#include   "SqlDrivers.h"
#if defined(MY_DEBUG)
    extern FILE* fp;
#endif


void btnSqlDrivers_OnClick(lpWndEventArgs Wea)
{
TCHAR szDriver[256], szAttributes[256], lpBuffer[64];
int i,iCount,iLnCt=1,iLine=0;
HCURSOR hHourGlass, hArrow;
String* ptrAttributes;
TCHAR** ptrLines=NULL;
short iLen1,iLen2;
SQLHENV hEnvr;
HWND hWnd=0;
TCHAR* pCh;
String s1;

#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Entering btnSqlDrivers_OnClick()\n"));
#endif
hHourGlass=LoadCursor(NULL,IDC_WAIT);
SetClassLong(Wea->hWnd, GCL_HCURSOR, (LONG) hHourGlass);
if(SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &hEnvr)!= SQL_ERROR)
{
    SQLSetEnvAttr(hEnvr,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)SQL_OV_ODBC3,SQL_IS_INTEGER);
    while(SQLDrivers(hEnvr,SQL_FETCH_NEXT,(SQLTCHAR*)szDriver,256,&iLen1,(SQLTCHAR*)szAttributes,256,&iLen2)!=SQL_NO_DATA)
    {
          iLnCt=iLnCt+2;
          pCh=szAttributes;
          for(i=0;i<iLen2;i++)
          {
              if(*pCh==0)
                 *pCh=_T(',');
              pCh++;
          }
          *--pCh=0;
          s1=szAttributes;
          iCount=s1.ParseCount(_T(','));
          iLnCt=iLnCt+iCount+2;
    };
    ptrLines=(TCHAR**)GlobalAlloc(GPTR,(iLnCt*sizeof(TCHAR*)));
    if(ptrLines)
    {
       _tcscpy(lpBuffer,_T("ODBC Database Drivers On Your System"));
       hWnd=CreateWindowEx(0,_T("frmOutput"),lpBuffer,WS_OVERLAPPEDWINDOW|WS_VSCROLL,700,150,350,475,HWND_DESKTOP,0,GetModuleHandle(NULL),NULL);
       SetWindowLong(hWnd,0,(long)iLnCt);
       SetWindowLong(hWnd,4,(long)ptrLines);
       while(SQLDrivers(hEnvr,SQL_FETCH_NEXT,(SQLTCHAR*)szDriver,256,&iLen1,(SQLTCHAR*)szAttributes,256,&iLen2)!=SQL_NO_DATA)
       {
             #if defined(MY_DEBUG)
                 _ftprintf(fp,_T("  szDriver     = %s\n\n"),szDriver);
             #endif
             ptrLines[iLine]=(TCHAR*)GlobalAlloc(GPTR,(_tcslen(szDriver)+1)*sizeof(TCHAR));
             if(!ptrLines[iLine])
             {
                MessageBox(Wea->hWnd,_T("Memory Allocation Error!"),_T("Not Good!"),MB_ICONERROR);
                ErrorMemFree(ptrLines,iLine);
                return;
             }
             _tcscpy(ptrLines[iLine],szDriver);
             iLine=iLine+2;
             pCh=szAttributes;
             for(i=0;i<iLen2;i++)
             {
                 if(*pCh==0)
                    *pCh=_T(',');
                 pCh++;
             }
             *--pCh=0;
             s1=szAttributes;
             iCount=s1.ParseCount(_T(','));
             ptrAttributes = new String[iCount];
             s1.Parse(ptrAttributes,_T(','));
             for(i=0;i<iCount;i++)
             {
                 #if defined(MY_DEBUG)
                     _ftprintf(fp,_T("  ptrAttributes[%u]=%s\n"),i,ptrAttributes[i].lpStr());
                 #endif
                 _stprintf(lpBuffer,_T("%s"),ptrAttributes[i].lpStr());
                 ptrLines[iLine]=(TCHAR*)GlobalAlloc(GPTR,(_tcslen(lpBuffer)+1)*sizeof(TCHAR));
                 if(!ptrLines[iLine])
                 {
                    MessageBox(Wea->hWnd,_T("Memory Allocation Error!"),_T("Not Good!"),MB_ICONERROR);
                    ErrorMemFree(ptrLines,iLine);
                    return;
                 }
                 _tcscpy(ptrLines[iLine],lpBuffer);
                 iLine++;
             }
             delete [] ptrAttributes;
             iLine=iLine+2;
             #if defined(MY_DEBUG)
                 _ftprintf(fp,_T("\n\n"));
             #endif
       };
    }
    if(hWnd)
       ShowWindow(hWnd,SW_SHOWNORMAL);
    SQLFreeHandle(SQL_HANDLE_ENV,hEnvr);
}
hArrow=LoadCursor(NULL,IDC_ARROW);
SetClassLong(Wea->hWnd, GCL_HCURSOR, (LONG) hArrow);
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Leaving btnSqlDrivers_OnClick()\n\n"));
#endif
}

Frederick J. Harris


//frmOutput.h
#if !defined(FRMOUTPUT_H)
#define FRMOUTPUT_H
#define ID_PANE 2000
long __stdcall fnPaneProc(HWND, unsigned int, WPARAM, LPARAM);
long __stdcall frmOutput(HWND, unsigned int, WPARAM, LPARAM);
long frmOutput_OnCreate(lpWndEventArgs);
long frmOutput_OnPaint(lpWndEventArgs);
long frmOutput_OnSize(lpWndEventArgs);
long frmOutput_OnVScroll(lpWndEventArgs);
long frmOutput_OnClose(lpWndEventArgs);

#endif




//frmOutput.cpp                  //This file contains code to display the Sql Drivers and database
#include  <windows.h>            //data created in SqlDrivers.cpp, Excel.cpp, Access.cpp, and
//#define   _UNICODE
#include  <tchar.h>              //SqlServer.cpp.  Back in fnWndProc_OnCreate() in Main.cpp the
#include  <stdio.h>              //Window Class frmOutput (say Form Output or Output Form or
#include  "Main.h"               //Window) was RegisterClassEx()'ed with the .cbWndExtra bytes set
#include  "WinTypes.h"           //to 16.  You can see below in frmOutput_OnCreate() what is stored
#include  "frmOutput.h"          //there.  Basically, all that information is 'state' data that needs
#if defined(MY_DEBUG)            //to persist across function calls/Windows messages to allow for
    extern FILE* fp;             //the proper scrolling functionality of the output display window.
#endif
extern EVENTHANDLER  frmOutputHdlr[5];


long frmOutput_OnCreate(lpWndEventArgs Wea)              //Offset    What's Stored There
{                                                        //================================
HFONT hFont,hTmp;                                       //0  -  3   iLineCount
TEXTMETRIC tm;                                          //4  -  7   ptrPtrBuffer
HDC hDC;                                                //8  -  11  cyChar
                                                         //12 -  15  si.nPos, i.e., iStart
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("  Entering frmOutput_OnCreate()\n"));
#endif
hDC=GetDC(Wea->hWnd);
hFont=CreateFont(16,0,0,0,FW_BOLD,0,0,0,0,0,0,2,0,_T("Courier New"));  //For tabular data you need
hTmp=(HFONT)SelectObject(hDC,hFont);                                   //a fixed pitch font, and the
GetTextMetrics(hDC,&tm);                                               //size of a char in this font
SetWindowLong(Wea->hWnd,8,(long)tm.tmHeight);                          //is information you have to
DeleteObject(SelectObject(hDC,hTmp));                                  //have to set up scrolling.
ReleaseDC(Wea->hWnd,hDC);                                              //Another piece of information
SetWindowLong(Wea->hWnd,12,0);                                         //that has to persist is the
#if defined(MY_DEBUG)                                                  //scrolling position of the
     _ftprintf(fp,_T("  Leaving frmOutput_OnCreate()\n"));              //window.  Here its initially
#endif                                                                 //set to zero and stored in the
                                                                        //.cbWndExtra bytes at offset 12.
return 0;
}


long frmOutput_OnSize(lpWndEventArgs Wea)
{
int iLinesVisible,iLineCount;
unsigned int cyChar;
SCROLLINFO si;

#if defined(MY_DEBUG)
     _ftprintf(fp,_T("\n\n  Entering frmOutput_OnSize()\n"));
#endif
iLineCount=(unsigned int)GetWindowLong(Wea->hWnd,0);
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("    iLineCount = %u\n"),iLineCount);
#endif
ZeroMemory(&si, sizeof(SCROLLINFO));
si.cbSize =sizeof(SCROLLINFO);
si.fMask = SIF_POS | SIF_RANGE;
GetScrollInfo(Wea->hWnd,SB_VERT,&si);                //When a WM_SIZE message comes through, the
cyChar=(unsigned int)GetWindowLong(Wea->hWnd,8);     //HIWORD(lParam) contains the client height
iLinesVisible=HIWORD(Wea->lParam)/cyChar;            //of the Window in pixels.  This, in conjunc-
si.cbSize = sizeof(SCROLLINFO);                      //tion with the cyChar info stored in the
si.fMask =   SIF_POS | SIF_RANGE;                    //.cbWndExtra bytes, can be used to determine
si.nMin = 0;                                         //the number of lines visible in the window.
si.nMax = iLineCount-iLinesVisible;
si.nPos=GetWindowLong(Wea->hWnd,12);
if(si.nMax<0)
    si.nMax=0;
SetScrollInfo(Wea->hWnd,SB_VERT,&si,TRUE);
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("  Leaving frmOutput_OnSize()\n\n"));
#endif

return 0;
}


long frmOutput_OnVScroll(lpWndEventArgs Wea)
{
SCROLLINFO si;

#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Entering frmOutput_OnVScroll()\n"));
#endif
si.nPos=(int)GetWindowLong(Wea->hWnd,12);
switch(LOWORD(Wea->wParam))
{
  case SB_LINEUP:
    if(si.nPos)
    {
       si.cbSize = sizeof(SCROLLINFO);
       si.nPos--;
       SetWindowLong(Wea->hWnd,12,(long)si.nPos);
       si.fMask = SIF_POS;
       SetScrollInfo(Wea->hWnd,SB_VERT,&si,TRUE);
       ScrollWindow(Wea->hWnd,0,GetWindowLong(Wea->hWnd,8),0,0);
    }
    break;
  case SB_PAGEUP:
    {
       unsigned int iLinesVisible;
       RECT rc;
       GetClientRect(Wea->hWnd,&rc);
       iLinesVisible=rc.bottom/GetWindowLong(Wea->hWnd,8);
       si.cbSize = sizeof(SCROLLINFO);
       si.nPos=si.nPos-iLinesVisible;
       if(si.nPos<0)
          si.nPos=0;
       SetWindowLong(Wea->hWnd,12,(long)si.nPos);
       si.fMask = SIF_POS;
       SetScrollInfo(Wea->hWnd,SB_VERT,&si,TRUE);
       ScrollWindow(Wea->hWnd,0,rc.bottom,0,0);
    }
    break;
  case SB_LINEDOWN:
    si.cbSize =sizeof(SCROLLINFO);
    si.fMask = SIF_POS | SIF_RANGE;
    GetScrollInfo(Wea->hWnd,SB_VERT,&si);
    if(si.nPos<si.nMax)
    {
       si.nPos++;
       SetWindowLong(Wea->hWnd,12,(long)si.nPos);
       si.fMask = SIF_POS;
       SetScrollInfo(Wea->hWnd,SB_VERT,&si,TRUE);
       ScrollWindow(Wea->hWnd,0,-GetWindowLong(Wea->hWnd,8),0,0);
    }
    break;
  case SB_PAGEDOWN:
    {
       unsigned int iLinesVisible;
       RECT rc;

       si.cbSize =sizeof(SCROLLINFO);
       si.fMask = SIF_POS | SIF_RANGE;
       GetScrollInfo(Wea->hWnd,SB_VERT,&si);
       GetClientRect(Wea->hWnd,&rc);
       iLinesVisible=rc.bottom/GetWindowLong(Wea->hWnd,8);
       if(int(iLinesVisible+si.nPos)<=si.nMax)
          si.nPos = si.nPos + iLinesVisible;
       else
          si.nPos=si.nMax;
       SetWindowLong(Wea->hWnd,12,(long)si.nPos);
       si.fMask = SIF_POS;
       SetScrollInfo(Wea->hWnd,SB_VERT,&si,TRUE);
       ScrollWindow(Wea->hWnd,0,rc.bottom,0,0);
    }
    break;
}
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Leaving frmOutput_OnVScroll()\n\n"));
#endif

return 0;
}


long frmOutput_OnPaint(lpWndEventArgs Wea)
{
unsigned int iLineCount,i,iStart,iFinish,iLine,iPos;
TCHAR** ptrPtrBuffer=NULL;
HFONT hFont,hTmp;
PAINTSTRUCT ps;
long cyChar;
HDC hDC;

#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Entering frmOutput_OnPaint()\n"));
#endif
iLineCount=(unsigned int)GetWindowLong(Wea->hWnd,0);  //How Many Lines In Scroll Buffer?
ptrPtrBuffer=(TCHAR**)GetWindowLong(Wea->hWnd,4);     //Buffer Holding String Pointers
cyChar=GetWindowLong(Wea->hWnd,8);                    //How High Is Each Line?
iPos=GetWindowLong(Wea->hWnd,12);                     //What's The Top Line Number?

hDC=BeginPaint(Wea->hWnd,&ps);                        //Need A Device Context To Print In Windows!
iStart=ps.rcPaint.top/cyChar;                         //Gotta Find Bounds Of Invalid Region In
iFinish=ps.rcPaint.bottom/cyChar;                     //Terms Of Top & Bottom.
hFont=CreateFont(16,0,0,0,FW_BOLD,0,0,0,0,0,0,2,0,_T("Courier New"));
hTmp=(HFONT)SelectObject(hDC,hFont);
SetBkMode(hDC,TRANSPARENT);
for(i=iStart;i<=iFinish;i++)
{
     iLine=iPos+i;
     if(iLine<iLineCount)
     {
        if(ptrPtrBuffer[iLine])
           TextOut(hDC,0,i*cyChar,ptrPtrBuffer[iLine],_tcslen(ptrPtrBuffer[iLine]));
     }
}
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("  iLineCount      = %u\n"),iLineCount);
     _ftprintf(fp,_T("  ptrPtrBuffer    = %u\n"),ptrPtrBuffer);
     _ftprintf(fp,_T("  cyChar          = %u\n"),cyChar);
     _ftprintf(fp,_T("  iPos            = %u\n"),iPos);
#endif
DeleteObject(SelectObject(hDC,hTmp));
EndPaint(Wea->hWnd,&ps);
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Leaving frmOutput_OnPaint()\n\n"));
#endif

return 0;
}


long frmOutput_OnClose(lpWndEventArgs Wea)                    //The logic this app uses to display the database data
{                                                             //is a bit on the complex side.  First, in all the procedures
unsigned int blnFree=0,iLineCount;                           //that generate output, an estimate is made of the maximum
TCHAR** ptrPtrBuffer=0;                                      //number of lines that will be needed to display the data.
unsigned int i;                                              //A memory allocation is then made for TCHAR** pointers of
                                                              //a number equal to this maximum number of lines.  The bytes
#if defined(MY_DEBUG)                                        //needed for this TCHAR** buffer to contain a TCHAR* for each
     _ftprintf(fp,_T("Entering frmOutput_OnClose()\n"));      //line would be the sizeof(TCHAR*) times the number of lines
#endif                                                       //needed.  Back in those routines the obtained pointer to the
iLineCount=(unsigned int)GetWindowLong(Wea->hWnd,0);         //memory is stored at offset 4 in frmOutput's .cbWndExtra bytes.
ptrPtrBuffer=(TCHAR**)GetWindowLong(Wea->hWnd,4);            //The number of lines is stored at offset zero.  Back within
#if defined(MY_DEBUG)                                        //those various routines that generate output, another memory
     _ftprintf(fp,_T("  ptrPtrBuffer = %u\n"),ptrPtrBuffer);  //allocation has to be made for each line's characters, and the
#endif                                                       //address of that line's memory allocation has to be stored in
if(ptrPtrBuffer)                                             //the proper slot in the TCHAR** buffer.  So, when any instance
{                                                            //of frmOutput is destroyed, frmOutput_OnClose() has to clean
    for(i=0;i<iLineCount;i++)                                 //up, i.e., deallocate, the memory for each line of text, and
    {                                                         //when all the lines of text have been deallocated, finally
        if(ptrPtrBuffer[i])                                   //deallocate the main memory buffer where all the TCHAR* have
        {                                                     //been stored, i.e., the TCHAR** buffer whose address is stored
           blnFree=(unsigned int)GlobalFree(ptrPtrBuffer[i]); //in offset bytes 4 - 7.
           #if defined(MY_DEBUG)
               _ftprintf(fp,_T("  %u\t%u\n"),i,blnFree);
           #endif
        }
        else
        {
           #if defined(MY_DEBUG)
               _ftprintf(fp,_T("  %u\tN.A.\n"),i);
           #endif
        }
    }
    blnFree=(unsigned int)GlobalFree(ptrPtrBuffer);
    #if defined(MY_DEBUG)
        _ftprintf(fp,_T("\n  GlobalFree(ptrPtrBuffer) = %u\n"),blnFree);
    #endif
}
DestroyWindow(Wea->hWnd);
#if defined(MY_DEBUG)
     _ftprintf(fp,_T("Leaving frmOutput_OnClose()\n\n"));
#endif

return 0;
}


long __stdcall frmOutput(HWND hwnd, unsigned int msg, WPARAM wParam,LPARAM lParam)
{
WndEventArgs Wea;                                           //Most folks who do Sdk style
                                                             //Windows Api coding probable
for(unsigned int i=0;i<5 ;i++)                              //use a switch construct to map
{                                                           //messages to procedures to
     if(frmOutputHdlr[i].Code==msg)                          //handle them, but I prefer to
     {                                                       //do this mapping with a for
        Wea.hWnd=hwnd,Wea.lParam=lParam,Wea.wParam=wParam;   //loop.  It produces the smallest
        return (*frmOutputHdlr[i].fnPtr)(&Wea);              //possible Window Procedure.  It
     }                                                       //requires though my
}                                                           //AttachMessageHandlers() routine,
                                                             //and a struct to associate message
return (DefWindowProc(hwnd,msg,wParam,lParam));             //handling function pointers with
}                                                            //the message to be handled.

Frederick J. Harris


//Sql.h
#if !defined(SQL_H)
#define SQL_H

class SQL
{
public:
SQL();
~SQL();
void MakeConnectionString(void);
void ODBCConnect(void);
void ODBCDisconnect(void);

public:
String            strConnectionString;
String            strDatabase;
String            strDriver;
String            strServer;
String            strDBQ;
TCHAR             szCnOut[512];
short int         iBytes;
SWORD             swStrLen;
SQLHENV           hEnvr;
SQLHDBC           hConn;
SQLINTEGER        iNativeErrPtr;
SQLSMALLINT       iTextLenPtr;
SQLTCHAR          szErrMsg[512];
SQLTCHAR          szErrCode[8];
unsigned int      blnConnected;
};

#endif



//Sql.cpp
#include  <windows.h>
//#define   _UNICODE
#include  <tchar.h>
#include  <stdio.h>
#include  <odbcinst.h>
#include  <sql.h>
#include  <sqlext.h>
#include  "Main.h"
#include  "Strings.h"
#include  "Sql.h"
#if defined(MY_DEBUG)
    extern FILE* fp;
#endif


SQL::SQL() //Constructor
{
ZeroMemory(szCnOut, 512);
ZeroMemory(szErrMsg,512);
strDBQ=_T("");
}


SQL::~SQL()
{
//Sql Destructor
}


void SQL::MakeConnectionString(void)
{
if(strDriver==_T("SQL Server"))
{
    if(strDBQ==_T(""))
    {
       strConnectionString=_T("DRIVER=");
       strConnectionString=strConnectionString+strDriver+_T(";")+_T("SERVER=")+strServer+_T(";");
    }
    else
    {
       strConnectionString=_T("DRIVER=");
       strConnectionString=strConnectionString+strDriver+_T(";")+_T("SERVER=")+strServer+_T(";")+ \
       _T("DATABASE=") + strDatabase + _T(";") + _T("DBQ=") + strDBQ + _T(";");
    }
}
else if(strDriver==_T("Microsoft Access Driver (*.mdb)"))
{
    strConnectionString=_T("DRIVER=");
    strConnectionString=strConnectionString+strDriver+_T(";")+_T("DBQ=")+strDBQ+_T(";");
}
else if(strDriver==_T("Microsoft Excel Driver (*.xls)"))
{
    strConnectionString=_T("DRIVER=");
    strConnectionString=strConnectionString+strDriver+_T(";")+_T("DBQ=")+strDBQ+_T(";");
}
}


void SQL::ODBCConnect(void)
{
TCHAR szCnIn[512];
UINT iResult;

MakeConnectionString();
SQLAllocHandle(SQL_HANDLE_ENV,SQL_NULL_HANDLE,&hEnvr);
SQLSetEnvAttr(hEnvr,SQL_ATTR_ODBC_VERSION,(SQLPOINTER)SQL_OV_ODBC3,SQL_IS_INTEGER);
SQLAllocHandle(SQL_HANDLE_DBC,hEnvr,&hConn);
_tcscpy(szCnIn,strConnectionString.lpStr());
iResult=SQLDriverConnect(hConn,NULL,(SQLTCHAR*)szCnIn,(SQLSMALLINT)_tcslen(szCnIn),(SQLTCHAR*)szCnOut,512,&swStrLen,SQL_DRIVER_NOPROMPT);
if(iResult==0 || iResult==1)
    blnConnected=TRUE;
else
{
    SQLGetDiagRec(SQL_HANDLE_DBC,hConn,1,szErrCode,&iNativeErrPtr,szErrMsg,512,&iTextLenPtr);
    blnConnected=FALSE;
    SQLDisconnect(hConn);
    SQLFreeHandle(SQL_HANDLE_DBC,hConn);
    SQLFreeHandle(SQL_HANDLE_ENV,hEnvr);
}
}


void SQL::ODBCDisconnect(void)
{
if(blnConnected==TRUE)
{
    SQLDisconnect(hConn);
    SQLFreeHandle(SQL_HANDLE_DBC,hConn);
    SQLFreeHandle(SQL_HANDLE_ENV,hEnvr);
    blnConnected=FALSE;
}
}


I think that's it!!!