ODBC Example for C++ 5.0 and later

Take the following steps to create and use a simple database, containing only images. (For a more sophisticated approach, refer to Using ODBC to Access Image Data.)

Note:

The LEADTOOLS COM requires ODBC drivers that can properly handle long binary fields. Examples have been tested and will work with version 3.0 of the Microsoft ODBC Desktop Driver Pack. (This driver pack ships with Access 95 and works on 32-bit systems.) You can download drivers from the LEAD BBS or CompuServe forum. The CompuServe forum is at GO LEADTECH. The web page is ftp://ftp.leadtools.com/pub/utils/ODBC32.zip. The file to download is ODBC32.ZIP.

 

1.

Start with a copy of the project that you created in Loading and Displaying an Image.

 

Note: This example changes the initialization of the window; so using a copy of the original project prevents problems with later reuse of the original project.

2.

Create an ODBC data source, and name it LEADACCESS. The data source should have one long binary field, the table name should be ltvcpic and the field name should be photo. For a description of how to create an appropriate data source, refer to Using ODBC to Access Image Data.

3.

Add #import statements to your program so you can access the LEADRasterODBC constants and classes:

 

a.

In the Project Workspace, click the FileView tab.

 

b.

Double-click the tutor files folder to open it.

 

c.

Double-click the Header Files folder to open it.

 

d.

Double-click the StdAfx.h file to edit it.

 

e.

Add the following line to the end of the file (keep in mind, you may have to change the path to where the dll's reside):

 

 

#import "c:\\winnt\\system32\\ LTRBC14n.dll" no_namespace, named_guids

4.

Edit TUTORDLG.H and insert the following line in the definition of CTutorDlg after DECLARE_MESSAGE_MAP():

// These variables let us synchronize the LEADRasterODBC control's ODBC 
ILEADRasterODBC *m_pRastODBC=NULL;

5.

Edit the TUTORDLG.CPP member function CTutorDlg::OnInitDialog. After the comment // TODO: Add extra initialization here, replace the code with the following:

   CoCreateInstance(CLSID_LEADRasterODBC, NULL, CLSCTX_ALL, IID_ILEADRasterODBC, (void**)& m_pRastODBC);
// Position and size the main form so that it takes up most of the screen.
float Width = GetSystemMetrics(SM_CXSCREEN) * 0.9f;
float Height = GetSystemMetrics(SM_CYSCREEN) * 0.8f;
float Left = (GetSystemMetrics(SM_CXSCREEN) - Width) / 2.0f;
float Top = (GetSystemMetrics(SM_CYSCREEN) - Height) / 2.0f;
MoveWindow((int) Left, (int) Top, (int) Width, (int) Height);
m_LEADRasView1.SetAutoRepaint(FALSE); // Take control of when the image paints
m_LEADRasterView1.SetPaintPalette(PAINTPALETTE_AUTO);
m_LEADRasterView1.SetEnableMethodErrors(FALSE);
// Make first param "ODBC;" to select data source from a listbox.
int nRet = m_pRastODBC->dbOpen("ODBC;DSN=LEADACCESS", "ltvcpic", "photo", 0);
if (nRet)
{
  GetDlgItem(IDC_FIRST)->EnableWindow(FALSE);
  GetDlgItem(IDC_PREV)->EnableWindow(FALSE);
  if (m_pRastODBC->GetdbIsEOF() && m_pRastODBC->GetdbIsBOF())
  {
    GetDlgItem(IDC_LAST)->EnableWindow(FALSE);
    GetDlgItem(IDC_NEXT)->EnableWindow(FALSE);
  }
  m_pRastODBC->dbMoveFirst(); 
  m_pRastODBC->PutdbLoadBits(0);
  m_pRastODBC->PutdbLockingMode(DB_LOCKINGMODE_OPTIMISTIC);
  ImageInit();
}
return TRUE;

6.

Edit the TUTORDLG.H file. After the line DECLARE_MESSAGE_MAP() in the class CTutorDlg, add the following line:

VOID ImageInit();

7.

Edit the TUTORDLG.CPP file. Add the following member function.

VOID CTutorDlg::ImageInit()
{
CWaitCursor wait;  // display hourglass
float HeightFactor;
float WidthFactor;
float HeightAllowed;
float WidthAllowed;
float Width = GetSystemMetrics(SM_CXSCREEN) * 0.9f;
float Height = GetSystemMetrics(SM_CYSCREEN) * 0.8f;
float Left = (GetSystemMetrics(SM_CXSCREEN) - Width) / 2.0f;
float Top = (GetSystemMetrics(SM_CYSCREEN) - Height) / 2.0f;
if (m_LEADRasterView1.GetRaster().GetBitmap() == 0)
{
  m_LEADRasterView1.ForceRepaint();
  return;
}
// Set the variables used for preserving the aspect ratio.
// Allow for a border of 1/8 of the form size.
// The units of measure do not matter, since we are calculating proportions.
HeightFactor = m_LEADRasterView1.GetRaster().GetBitmapHeight();
WidthFactor = m_LEADRasterView1.GetRaster().GetBitmapWidth();
CRect rcWindow;
GetClientRect(rcWindow);
HeightAllowed = rcWindow.Height() * 3.0f / 4;
WidthAllowed = rcWindow.Width() * 3.0f / 4;
// Center the LEADRasterView control on the form, preserving the aspect ratio.
// Check to see if using the maximum width will make the image too tall.
// Set the dimensions based on the result.
if(((WidthAllowed * HeightFactor) / WidthFactor) < HeightAllowed)
{
  Left = rcWindow.Width() / 8.0f;
  Width = WidthAllowed;
  Height = (Width * HeightFactor) / WidthFactor;
  Top = (rcWindow.Height() - Height) / 2;
}
else
{
  Top = rcWindow.Height() / 8.0f;
  Height = HeightAllowed;
  Width = (Height * WidthFactor) / HeightFactor;
  Left = (rcWindow.Width() - Width) / 2;
}
rcWindow.SetRect(0, 0, (int) Width, (int) Height);
rcWindow.OffsetRect((int) Left, (int) Top);
m_LEADRasterView1.MoveWindow(rcWindow);
// Turn off scroll bars to make sure we use the full client area.
m_LEADRasterView1.SetAutoScroll(FALSE);
// Set the image display size to match the LEAD control
m_LEADRasterView1.SetDstRect(0.0f, 0.0f, m_LEADRasterView1.GetScaleWidth(), m_LEADRasterView1.GetScaleHeight());
m_LEADRasterView1.SetDstClipRect(0.0f, 0.0f, m_LEADRasterView1.GetScaleWidth(), m_LEADRasterView1.GetScaleHeight());
// Display the image
m_LEADRasterView1.ForceRepaint(); // Repaint the image
}

8.

In this step and the next one, change the resource file to add some buttons. For the first button, do the following:

 

a.

Click on the ResourceView tab of the Project Workspace.

 

b.

Double-click on the tutor resources folder.

 

c.

Double-click on Dialog.

 

d.

Double-click on IDD_TUTOR_DIALOG.

 

e.

Select the command button control on the Controls toolbar.

 

f.

Click and drag on the dialog window to create and position the button.

 

g.

Double-click the button just created to bring up the Push Button Properties window.

 

h.

Change the ID to IDC_ADDNEW and the caption to Add New.

 

i.

Close the window.

9.

For the other buttons, repeat Step 6 with the following differences for each button:

- With an ID of IDC_LOAD and caption Load

- With an ID of IDC_UPDATE and caption Update

- With an ID of IDC_DELETE and caption Delete

- With an ID of IDC_FIRST and caption |<

- With an ID of IDC_PREV and caption <

- With an ID of IDC_NEXT and caption >

- With an ID of IDC_LAST and caption >|

10.

Select the CANCEL button and press DEL to remove it.

11.

Double-click the OK button and rename it to Exit.

12.

Press CTRL-F4 twice and answer YES to the message to save.

13.

Add code for the IDC_FIRST button as follows:

 

a.

Press Ctrl-W to go to the MFC Class Wizard.

 

b.

In the Class name combo box, select CTutorDlg.

 

c.

In the Object IDs list box, select IDC_FIRST.

 

d.

In the Messages list box, select BN_CLICKED.

 

e.

Click the Add function button. Choose OK for the default function name (OnFirst).

 

f.

Click the Edit Code button to start entering the code.

 

g.

Edit the function to appear as follows:

void CTutorDlg::OnFirst() 
{
  CWaitCursor wait;  // display hourglass
  if (m_pRastODBC->GetdbIsEOF() && m_Lead1.GetDbIsBOF())
  {
  MessageBox(TEXT("The database is empty"), TEXT("NOTICE"), MB_OK);
  GetDlgItem(IDC_FIRST)->EnableWindow(FALSE);
  GetDlgItem(IDC_PREV)->EnableWindow(FALSE);
  GetDlgItem(IDC_LAST)->EnableWindow(FALSE);
  GetDlgItem(IDC_NEXT)->EnableWindow(FALSE);
  }
  else
  {
  m_pRastODBC->dbMoveFirst();
  // Viewed image is in the database (after repaint)
  CWnd *pButton = GetDlgItem(IDC_UPDATE);
  pButton->SetWindowText(TEXT("Update"));
  GetDlgItem(IDC_FIRST)->EnableWindow(FALSE);
  GetDlgItem(IDC_PREV)->EnableWindow(FALSE);
  GetDlgItem(IDC_LAST)->EnableWindow(TRUE);
  GetDlgItem(IDC_NEXT)->EnableWindow(TRUE);
  }
  ImageInit();
}

14.

Add code for the IDC_PREV button as follows:

 

a.

Press Ctrl-W to go to the MFC Class Wizard.

 

b.

In the Class name combo box, select CTutorDlg.

 

c.

In the Object IDs list box, select IDC_PREV.

 

d.

In the Messages list box, select BN_CLICKED.

 

e.

Click the Add function button. Choose OK for the default function name (OnPrev).

 

f.

Click the Edit Code button to start entering the code.

 

g.

Edit the function to appear as follows:

void CTutorDlg::OnPrev() 
{
  CWaitCursor wait;  // display hourglass
  if (!m_pRastODBC->GetdbIsBOF())
  {
  m_pRastODBC->dbMovePrev();
  // Viewed image is in the database (after repaint)
  CWnd *pButton = GetDlgItem(IDC_UPDATE);
  pButton->SetWindowText(TEXT("Update"));
  }
  // don't combine with above as an else case!
  if (m_pRastODBC->GetdbIsBOF())     // BOF
  {
  GetDlgItem(IDC_FIRST)->EnableWindow(FALSE);
  GetDlgItem(IDC_PREV)->EnableWindow(FALSE);
  if (!m_pRastODBC->GetdbIsEOF())
  {
    m_pRastODBC->dbMoveNext(); // move off BOF
    MessageBox(TEXT("Already at first record"), TEXT("NOTICE"), MB_OK);
  }
  else  // BOF && EOF
  {
     GetDlgItem(IDC_LAST)->EnableWindow(FALSE);
     GetDlgItem(IDC_NEXT)->EnableWindow(FALSE);
  }
  }
  else
  {
  GetDlgItem(IDC_LAST)->EnableWindow(TRUE);
  GetDlgItem(IDC_NEXT)->EnableWindow(TRUE);
  ImageInit();  
  }
}

15.

Add code for the IDC_NEXT button as follows:

 

a.

Press Ctrl-W to go to the MFC Class Wizard.

 

b.

In the Class name combo box, select CTutorDlg.

 

c.

In the Object IDs list box, select IDC_NEXT.

 

d.

In the Messages list box, select BN_CLICKED.

 

e.

Click the Add function button. Choose OK for the default function name (OnNext).

 

f.

Click the Edit Code button to start entering the code.

 

g.

Edit the function to appear as follows:

void CTutorDlg::OnNext() 
{
  CWaitCursor wait;  // display hourglass
  if (!m_pRastODBC->GetdbIsEOF())
  {
  m_pRastODBC->dbMoveNext();
  // Viewed image is in the database (after repaint)
  CWnd *pButton = GetDlgItem(IDC_UPDATE);
  pButton->SetWindowText(TEXT("Update"));
  }
  // don't combine with above as an else case!
  if (m_pRastODBC->GetdbIsEOF())   // EOF
  {
   GetDlgItem(IDC_LAST)->EnableWindow(FALSE);
   GetDlgItem(IDC_NEXT)->EnableWindow(FALSE);
   if (!m_pRastODBC->GetdbIsBOF())
   {
    m_pRastODBC->dbMovePrev(); // move back from EOF
    MessageBox(TEXT("Already at last record"), TEXT("NOTICE"), MB_OK);
   }
   else    // EOF && BOF
   {
    GetDlgItem(IDC_FIRST)->EnableWindow(FALSE);
    GetDlgItem(IDC_PREV)->EnableWindow(FALSE);
   }
  }
  else
  {
    GetDlgItem(IDC_FIRST)->EnableWindow(TRUE);
    GetDlgItem(IDC_PREV)->EnableWindow(TRUE);
    ImageInit();    
  }
}

16.

Add code for the IDC_LAST button as follows:

 

a.

Press Ctrl-W to go to the MFC Class Wizard.

 

b.

In the Class name combo box, select CTutorDlg.

 

c.

In the Object IDs list box, select IDC_LAST.

 

d.

In the Messages list box, select BN_CLICKED.

 

e.

Click the Add function button. Choose OK for the default function name (OnLast).

 

f.

Click the Edit Code button to start entering the code.

 

g.

Edit the function to appear as follows:

void CTutorDlg::OnLast() 
{
  CWaitCursor wait;  // display hourglass 
  if (m_pRastODBC->GetdbIsEOF() && m_Lead1.GetDbIsBOF())
  {
    MessageBox(TEXT("The database is empty"), TEXT("NOTICE"), MB_OK);
    GetDlgItem(IDC_LAST)->EnableWindow(FALSE);
    GetDlgItem(IDC_NEXT)->EnableWindow(FALSE);
    GetDlgItem(IDC_FIRST)->EnableWindow(FALSE);
    GetDlgItem(IDC_PREV)->EnableWindow(FALSE);
  }
  else
  {
    m_pRastODBC->dbMoveLast();
    
    // Viewed image is in the database (after repaint)
    CWnd *pButton = GetDlgItem(IDC_UPDATE);
    pButton->SetWindowText(TEXT("Update"));
    
    GetDlgItem(IDC_LAST)->EnableWindow(FALSE);
    GetDlgItem(IDC_NEXT)->EnableWindow(FALSE);
    GetDlgItem(IDC_PREV)->EnableWindow(TRUE);
    GetDlgItem(IDC_FIRST)->EnableWindow(TRUE);
  }
  ImageInit();
}

17.

Add code for the IDC_DELETE button as follows:

 

a.

Press Ctrl-W to go to the MFC Class Wizard.

 

b.

In the Class name combo box, select CTutorDlg.

 

c.

In the Object IDs list box, select IDC_DELETE.

 

d.

In the Messages list box, select BN_CLICKED.

 

e.

Click the Add function button. Choose OK for the default function name (OnDelete).

 

f.

Click the Edit Code button to start entering the code.

 

g.

Edit the function to appear as follows:

void CTutorDlg::OnDelete() 
{
  CWaitCursor wait;  // display hourglass 
  TCHAR buffer[10];
  if (m_pRastODBC->GetdbCanUpdate() == FALSE)
  {
    MessageBox(TEXT("You cannot update this database."), TEXT("ERROR"));
    return; 
  }
  CWnd *pButton = GetDlgItem(IDC_UPDATE);
  pButton->GetWindowText(buffer, 7);
  // Update button's text will be UPDATE if an image has been loaded but
  // not updated.
  if (isupper(buffer[1]))
  {
    MessageBox(TEXT("Viewed image is not in database"), TEXT("ERROR"), MB_OK);
    // User can do a MoveNext and MovePrev to see what the current image
    // currently is.
    return;
  }
  if (m_pRastODBC->GetdbIsEOF() && m_pRastODBC->GetdbIsBOF())
  {
    MessageBox(TEXT("The database is empty"), TEXT("NOTICE"), MB_OK);
    GetDlgItem(IDC_LAST)->EnableWindow(FALSE);
    GetDlgItem(IDC_NEXT)->EnableWindow(FALSE);
    GetDlgItem(IDC_FIRST)->EnableWindow(FALSE);
    GetDlgItem(IDC_PREV)->EnableWindow(FALSE);
  }
  else if (!m_pRastODBC->GetdbIsBOF() && !m_pRastODBC->GetdbIsEOF())
  {
    if (m_pRastODBC->GetdbEditMode() == DB_EDITMODE_ADDNEW)
    {
        // User selected Add New and then Delete without an Update.
        // Update the empty record first before deleting it.
        int nRet = m_pRastODBC->dbUpdate(FILE_CMP, 24, QFACTOR_PQ1);
        if (!nRet)
        {
            m_pRastODBC->dbMoveLast(); // new records are added to the end
            GetDlgItem(IDC_LAST)->EnableWindow(FALSE);
            GetDlgItem(IDC_NEXT)->EnableWindow(FALSE);
            GetDlgItem(IDC_FIRST)->EnableWindow(TRUE);
            GetDlgItem(IDC_PREV)->EnableWindow(TRUE);
        }
    }
    m_pRastODBC->dbDelete();
    if (m_pRastODBC->GetdbIsDeleted() == FALSE)
    {
         MessageBox(TEXT("Unable to delete record!"), TEXT("ERROR"));
         return;
    }
    if (m_pRastODBC->GetdbCanRestart() == TRUE)
    {
          m_pRastODBC->dbRequery();     // moves you to the first image in the database
          GetDlgItem(IDC_PREV)->EnableWindow(FALSE);
          GetDlgItem(IDC_FIRST)->EnableWindow(FALSE);
          if (m_pRastODBC->GetdbIsEOF())
          {
             GetDlgItem(IDC_NEXT)->EnableWindow(FALSE);
             GetDlgItem(IDC_LAST)->EnableWindow(FALSE);
          }
          else
          {
             GetDlgItem(IDC_NEXT)->EnableWindow(TRUE);
             GetDlgItem(IDC_LAST)->EnableWindow(TRUE);
          }
    }
    else
    {
        m_pRastODBC->dbMovePrev();
      GetDlgItem(IDC_LAST)->EnableWindow(TRUE);
      GetDlgItem(IDC_NEXT)->EnableWindow(TRUE);
        if (m_pRastODBC->GetdbIsBOF()) // just before first record
        {
            m_pRastODBC->dbMoveNext(); // move forward to first record
            GetDlgItem(IDC_FIRST)->EnableWindow(FALSE);
            GetDlgItem(IDC_PREV)->EnableWindow(FALSE);
            if (m_pRastODBC->GetdbIsEOF())    // just past last record
            {
                // no more records left
                MessageBox(TEXT("The database is empty"), TEXT("NOTICE"), MB_OK);
                m_LEADRasterView1.ForceRepaint();
                GetDlgItem(IDC_LAST)->EnableWindow(FALSE);
                GetDlgItem(IDC_NEXT)->EnableWindow(FALSE);
            }
        }
    }
    ImageInit();
    // Viewed image is in the database (after repaint)
    pButton->SetWindowText(TEXT("Update"));
  }
}

18.

Add code for the IDC_LOAD button as follows:

 

a.

Press Ctrl-W to go to the MFC Class Wizard.

 

b.

In the Class name combo box, select CTutorDlg.

 

c.

In the Object IDs list box, select IDC_LOAD.

 

d.

In the Messages list box, select BN_CLICKED.

 

e.

Click the Add function button. Choose OK for the default function name (OnLoad).

 

f.

Click the Edit Code button to start entering the code.

 

g.

Edit the function to appear as follows:

void CTutorDlg::OnLoad() 
{
  CFileDialog  OpenFile(TRUE, NULL, TEXT("*.*"));
  if (OpenFile.DoModal() == IDOK)
  {
        CWaitCursor wait;  // display hourglass 
        int nRet = pRasterIO->Load(m_LEADRasterView1.GetRaster(), OpenFile.GetPathName(), 0, 0, 1);
        if (!nRet)
        {
            m_pRastODBC->PutRaster((ILEADRaster *)m_LEADRasterView1.GetRaster().m_lpDispatch());
          ImageInit();
          // Remind user that the image has not been updated in the database.
          CWnd *pButton = GetDlgItem(IDC_UPDATE);
          pButton->SetWindowText(TEXT("UPDATE"));
        }
      else
      {
            AfxMessageBox(TEXT("Error loading image."));
            if (m_pRastODBC->GetdbEditMode() == DB_EDITMODE_ADDNEW)
                 OnLast(); // cancel Add New
        }
  }
}

19.

Add code for the IDC_UPDATE button as follows:

 

a.

Press Ctrl-W to go to the MFC Class Wizard.

 

b.

In the Class name combo box, select CTutorDlg.

 

c.

In the Object IDs list box, select IDC_UPDATE.

 

d.

In the Messages list box, select BN_CLICKED.

 

e.

Click the Add function button. Choose OK for the default function name (OnUpdate).

 

f.

Click the Edit Code button to start entering the code.

 

g.

Edit the function to appear as follows:

void CTutorDlg::OnUpdate() 
{
  CWaitCursor wait;  // display hourglass 
  int  nRet, nEditMode;
  if (m_pRastODBC->GetdbCanUpdate() == FALSE)
  {
      MessageBox(TEXT("You cannot update this database."), TEXT("ERROR"));
      return; 
  }
  // Save Edit mode in a temp variable since dbUpdate() clears the edit mode.
  nEditMode = m_pRastODBC->GetdbEditMode();
  if ((nEditMode == DB_EDITMODE_NONE) &&
      ! m_pRastODBC->GetdbIsBOF() && ! m_pRastODBC->GetdbIsEOF())
          m_pRastODBC->dbEdit();
  if (m_pRastODBC->GetdbEditMode() != DB_EDITMODE_NONE)
  {
    // dbUpdate() clears the EditMode flag.
    nRet = m_pRastODBC->dbUpdate(FILE_CMP, 24, QFACTOR_PQ1);
    // Viewed image is in the database (after repaint)
    CWnd *pButton = GetDlgItem(IDC_UPDATE);
    pButton->SetWindowText(TEXT("Update"));
    if (!nRet)
    {
        if (nEditMode == DB_EDITMODE_ADDNEW)
        {
            m_pRastODBC->dbMoveLast();
            GetDlgItem(IDC_LAST)->EnableWindow(FALSE);
            GetDlgItem(IDC_NEXT)->EnableWindow(FALSE);
            GetDlgItem(IDC_FIRST)->EnableWindow(TRUE);
            GetDlgItem(IDC_PREV)->EnableWindow(TRUE);
        }
    }
    else
    {
        TCHAR  buffer[50];
        wsprintf(buffer, TEXT("LEAD Error %d saving image to database."), nRet);
        MessageBox(buffer, TEXT("ERROR"), MB_OK);
    }
  }
  else
  {
    MessageBox(TEXT("Sequence Error - no record to update."), TEXT("ERROR"), MB_OK);
  }
}

20.

Add code for the IDC_ADDNEW button as follows:

 

a.

Press Ctrl-W to go to the MFC Class Wizard.

 

b.

In the Class name combo box, select CTutorDlg.

 

c.

In the Object IDs list box, select IDC_ADDNEW.

 

d.

In the Messages list box, select BN_CLICKED.

 

e.

Click the Add function button. Choose OK for the default function name (OnAddnew).

 

f.

Click the Edit Code button to start entering the code.

 

g.

Edit the function to appear as follows:

void CTutorDlg::OnAddnew() 
{
    CWaitCursor wait;  // display hourglass
    // TODO: Add your control notification handler code here
    if (m_pRastODBC->GetdbCanAppend() == FALSE)
    {
      MessageBox(("You cannot append to this database."), TEXT("ERROR"));
      return; 
    }
    m_pRastODBC->dbAddNew();
    m_LEADRasterView1.ForceRepaint();     // no image for a new record
    wait.Restore();
    OnLoad();
}

21.

Open the ClassWizard (CTRL-W), and do the following:

 

a.

In the Class name combo box, select CTutorDlg.

 

b.

In the Object IDs list box, select CTutorDlg.

 

c.

In the Messages list box, select WM_DESTROY.

 

d.

Click the Add function button. Choose OK for the default function name (OnDestroy).

 

e.

Click the Edit Code button to start entering the code.

 

f.

Edit the function to appear as follows:

void CTutorDlg::OnDestroy() 
{
    CDialog::OnDestroy();
    // TODO: Add your message handler code here
    m_pRastODBC->dbClose();
    m_pRastODBC->Release();
}

22.

Rebuild and run the application.