Опубликован: 17.08.2010 | Доступ: свободный | Студентов: 1000 / 59 | Оценка: 4.11 / 3.89 | Длительность: 29:38:00
Самостоятельная работа 10:

Поддержка баз данных с помощью API ODBC

Код инициализации ODBC и взаимодействие с базой данных

Разработаем код, который получает данные из базы данных и заполняет ими список. Код упакуем в функцию FillDbToListBox().


  • Добавьте в класс CAPI_ODBCDlg объявление функции
    Добавление в файл API_ODBCDlg.h
    class CAPI_ODBCDlg : public CDialog
    {
    ................................................
    public:
      // Переменная управления списком
      CListBox m_ListBox;
      // Переменная поля ввода № п/п
      int m_NumEdit;
      // Переменная поля ввода ФИО
      CString m_NameEdit;
      // Переменная поля ввода Год
      int m_YearEdit;
      // Функция заполнения списка данными DB
      void FillDbToListBox(void);
    };
    Добавление в файл API_ODBCDlg.cpp
    
    // Функция заполнения списка данными DB
    void CAPI_ODBCDlg::FillDbToListBox(void)
    {
    }
  • Добавьте в начало файла API_ODBCDlg.cpp следующее определение структуры
    Добавление структуры в файл API_ODBCDlg.cpp
    // API_ODBCDlg.cpp : implementation file
    //
      
    #include "stdafx.h"
    #include "API_ODBC.h"
    #include "API_ODBCDlg.h"
    #include ".\api_odbcdlg.h"
      
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
      
    struct RECORD
    {
      int recNumEdit;// Переменная поля ввода № п/п
      CString recNameEdit;// Переменная поля ввода ФИО
      int recYearEdit;// Переменная поля ввода Год
    };
    .......................................................
  • Добавьте после определения структуры RECORD следующую директиву препроцессора
    Добавление директивы препроцессора в файл API_ODBCDlg.cpp
    ......................................................
    struct RECORD
    {
      int recNumEdit;// Переменная поля ввода № п/п
      CString recNameEdit;// Переменная поля ввода ФИО
      int recYearEdit;// Переменная поля ввода Год
    };
    
    #define LEN_BUF 31 // Длина буфера
    .......................................................
  • Найдите функцию OnInitDialog(), сгенерированную мастером, и непосредственно перед оператором return добавьте вызов функции FillDbToListBox(). Эта функция заполнит список на последнем этапе инициализации диалогового окна
    Добавление вызова в файл API_ODBCDlg.cpp
    BOOL CAPI_ODBCDlg::OnInitDialog()
    {
      CDialog::OnInitDialog();
    .............................................................
      
      // TODO: Add extra initialization here
      FillDbToListBox();
      
      return TRUE;  // return TRUE  unless you set the focus to a control
    }
  • Наполните функцию FillDbToListBox() следующим кодом, реализующим то, о чем было сказано выше
    Функция FillDbToListBox() в файле API_ODBCDlg.cpp
    // Функция заполнения списка данными DB
    void CAPI_ODBCDlg::FillDbToListBox(void)
    {
    // Объявления дескрипторов и вспомогательных переменных
      SQLHENV henv = NULL;
      SQLHDBC hdbc = NULL;
      SQLHSTMT hstmt = NULL;
      bool bIsConnected = false;
      SQLRETURN rc;
      
    // Инициализация механизма ODBC среды
      rc = SQLAllocHandle(SQL_HANDLE_ENV,
                          SQL_NULL_HANDLE,
                          &henv);
      if(rc != SQL_SUCCESS){
        MessageBox("Не могу инициализировать ODBC",
               "", MB_OK | MB_ICONSTOP);
        return;
      }
      
    // Выбор версии ODBC-3
      rc = SQLSetEnvAttr(henv,
            SQL_ATTR_ODBC_VERSION,
            (SQLPOINTER) SQL_OV_ODBC3,
            SQL_IS_INTEGER);
      if(rc != SQL_SUCCESS){
        MessageBox("Не могу установить версию ODBC",
               "", MB_OK | MB_ICONSTOP);
        return;
      }
      
    // Получить от ODBC дескриптор подключения к базе данных
      rc = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
      if(rc != SQL_SUCCESS){
        MessageBox("Не могу получить дескриптор подключения к БД",
               "", MB_OK | MB_ICONSTOP);
        return;
      }
      
    // Подключить приложение к источнику данных
      rc = SQLConnect(hdbc, // Дескриптор подключения
              (SQLCHAR*) "MyBase", SQL_NTS, // DSN
              (SQLCHAR*) "", SQL_NTS, // Идентификатор пользователя
              (SQLCHAR*) "", SQL_NTS); // Пароль для подключения
      if(rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO){
        MessageBox("Не могу подключиться к источнику данных",
               "", MB_OK | MB_ICONSTOP);
        return;
      }
      bIsConnected = true; // Подняли флаг, что соединение существует
      
    // Получить дескриптор оператора (statement handle)
    // для возможности передачи SQL-запросов 
      rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
      if(rc != SQL_SUCCESS){
        MessageBox("Не могу получить дескриптор оператора",
               "", MB_OK | MB_ICONSTOP);
        return;
      }
      
    // Подготовить и выполнить SQL-запрос (имя таблицы можно без расширения)
      LPCSTR szSQL = "SELECT number, name, birth FROM MyTable.dbf";
      rc = SQLExecDirect(hstmt, (unsigned char*)szSQL, SQL_NTS);
      if(rc != SQL_SUCCESS){
        MessageBox("Не могу выполнить SQL-запрос к БД",
               "", MB_OK | MB_ICONSTOP);
        return;
      }
      
    // Связывание полей с переменными приложения
      long int cb; // Вспомогательная типа SDWORD
      int iNumber; 
      char strName[LEN_BUF];
      int iBirth; 
      SQLBindCol(hstmt, 1, SQL_INTEGER, &iNumber, 0, &cb);// Field1
      SQLBindCol(hstmt, 2, SQL_CHAR, strName, LEN_BUF, &cb);// Field2
      SQLBindCol(hstmt, 3, SQL_INTEGER, &iBirth, 0, &cb);// Field3
      
    // Заполнение списка
      rc = SQLFetch(hstmt);
      while(rc == SQL_SUCCESS){
        RECORD* pRecord = new RECORD;
        pRecord->recNumEdit = iNumber;
        pRecord->recNameEdit = strName;
        pRecord->recYearEdit = iBirth;
        CString strTerm;
        strTerm.Format("%d    %s    %d", iNumber, strName, iBirth);
        int iIndex = m_ListBox.AddString(strTerm);// Добавить в список
        // Сохранить указатель на неименованный экземпляр структуры в списке
        m_ListBox.SetItemData(iIndex, (DWORD_PTR)pRecord);
                          
        rc = SQLFetch(hstmt); // Дать следующую запись
      }
        
    // Освободить все ресурсы ODBC
      if(henv){
        if(hdbc){
          if(bIsConnected){
            if(hstmt){
              SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
            }
            SQLDisconnect(hdbc);
            bIsConnected = false;
          }
          SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
          hdbc = NULL;
        }
        SQLFreeHandle(SQL_HANDLE_ENV, henv);
        henv = NULL;
      }
    }

После завершения работы приложения необходимо вернуть системе память, выделенную динамически под хранение экземпляров структуры RECORD.

  • В классе CAPI_ODBCDlg переопределите виртуальную функцию DestroyWindow() базового класса CDialog


    и разместить в ней следующий код

    Код функции DestroyWindow() в файл API_ODBCDlg.cpp
    BOOL CAPI_ODBCDlg::DestroyWindow()
    {
      RECORD* pRecord;
      int nCount = m_ListBox.GetCount();
      for(int i = 0; i < nCount; i++){
        pRecord = (RECORD*)m_ListBox.GetItemData(i);
        if(pRecord)
          delete pRecord;
      }
      
      return CDialog::DestroyWindow();
    }

Установим связь между полями ввода Edit Control и списком List Box, добавив обработчик в класс CAPI_ODBCDlg. Для этого:

  • В панели Class View выделите класс CAPI_ODBCDlg и в панели Properties включите режим Events

  • Добавьте обработчик для сообщения LBN_SELCHANGE, который заполните таким кодом
    Код обработчика для списка в файле API_ODBCDlg.cpp
    void CAPI_ODBCDlg::OnLbnSelchangeListbox()
    {
      int iIndex = m_ListBox.GetCurSel();
      if(iIndex == LB_ERR) return;
      
      RECORD* pRecord = 
                   (RECORD*)m_ListBox.GetItemData(iIndex);
      if(pRecord){
        m_NumEdit = pRecord->recNumEdit;
        m_NameEdit = pRecord->recNameEdit;
        m_YearEdit = pRecord->recYearEdit;
      
        UpdateData(FALSE);
      }
    }
  • Добавьте в функцию OnInitDialog() класса CAPI_ODBCDlg после вызова функции FillDbToListBox() заполнение полей ввода значениями первой записи
    Инициализация полей ввода в файле API_ODBCDlg.cpp
    BOOL CAPI_ODBCDlg::OnInitDialog()
    {
    ...............................................
      // TODO: Add extra initialization here
      FillDbToListBox();
      m_ListBox.SetCurSel(0);
      OnLbnSelchangeListbox();
      
      return TRUE;  // return TRUE  unless you set the focus to a control
    }
  • Оформите About со своими фамилиями и номером лабораторной работы
  • Постройте приложение и проверьте его работу. Результат должен быть примерно таким


Александр Даниленко
Александр Даниленко
Стоит Windows 8 Pro, Visual Studio 2010 Express Edition .