ODBC. Урок 5. ODBC пример — Архив WASM.RU

Все статьи

ODBC. Урок 5. ODBC пример — Архив WASM.RU

  На этой консультации, мы обобщим всё, что мы знаем на данный момент. Мы напишем программу, которая использует ODBC API. Для простоты я выбирал базу данных Microsoft Access (Microsoft Access 97) для этой программы.

  Загрузите пример.

  Примечание: Если вы используете windows.inc версии 1.18 или ниже, вы должны прежде устранить небольшой баг. Запустив поиск в файле windows.inc для SQL_NULL_HANDLE, вы найдете строку:

SQL_NULL_HANDLE equ 0L

  Удалите символ "L" после нуля, вот так:

SQL_NULL_HANDLE equ 0

  Эта программа построена на базе диалогового окна с простым меню. Когда пользователь выбирает пункт меню "connect", он пытается подключиться к test.mdb, т.е. своей базе данных. После того, как связь будет установленна, программа отобразит конечную полную строку связи возвращенную драйвером ODBC. После этого, пользователь может выбрать пункт меню "View All Records", чтобы заполнить контрол listview данными из базы. Кроме того, пользователь может выбрать "Query", чтобы найти специфическую запись. Программа представит небольшой диалоговый блок, предлагающий пользователю набрать имя человека, которого он хочет искать. Когда пользователь нажимает кнопку OK или клавишу Enter, программа выполняет запрос выбрать запись(записи), которые сопоставлены имени. Когда пользователь сделал всё, что ему было необходимо, он может выбрать пункт меню "disconnect", чтобы отключиться от базы данных.

  Теперь давайте посмотрим на исходный код:

.386
.model flat,stdcall
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\odbc32.inc
include \masm32\include\comctl32.inc
include \masm32\include\user32.inc
includelib \masm32\lib\odbc32.lib
includelib \masm32\lib\comctl32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

IDD_MAINDLG      equ 101
IDR_MAINMENU     equ  102
IDC_DATALIST     equ  1000
IDM_CONNECT      equ 40001
IDM_DISCONNECT   equ 40002
IDM_QUERY        equ 40003
IDC_NAME         equ 1000
IDC_OK           equ 1001
IDC_CANCEL       equ 1002
IDM_CUSTOMQUERY  equ 40004
IDD_QUERYDLG     equ 102


DlgProc proto hDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
QueryProc proto hDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
SwitchMenuState proto :DWORD
ODBCConnect proto :DWORD
ODBCDisconnect proto :DWORD
RunQuery proto :DWORD

.data?
hInstance dd ?
hEnv dd ?
hConn dd ?
hStmt dd ?
Conn db 256 dup(?)
StrLen dd ?
hMenu dd ?  ; handle to the main menu
hList dd ?    ; handle to the listview control
TheName db 26 dup(?)
TheSurname db 26 dup(?)
TelNo db 21 dup(?)
NameLength dd ?
SurnameLength dd ?
TelNoLength dd ?
SearchName db 26 dup(?)
ProgPath db 256 dup(?)
ConnectString db 1024 dup(?)

.data
SQLStatement db "select * from main",0
WhereStatement db " where name=?",0
strConnect db  "DRIVER={Microsoft Access Driver (*.mdb)};DBQ=",0
DBName db "test.mdb",0
ConnectCaption db "Complete Connection String",0
Disconnect db "Disconnect successful",0
AppName db "ODBC Test",0
AllocEnvFail db "Environment handle allocation failed",0
AllocConnFail db "Connection handle allocation failed",0
SetAttrFail db "Cannot set desired ODBC version",0
NoData db "You must type the name in the edit box",0
ExecuteFail db "Execution of SQL statement failed",0
ConnFail db "Connection attempt failed",0
AllocStmtFail db "Statement handle allocation failed",0
Heading1 db "Name",0
Heading2 db "Surname",0
Heading3 db "Telephone No.",0

.code
start:
  invoke GetModuleHandle, NULL
  mov hInstance,eax
  call GetProgramPath
  invoke DialogBoxParam, hInstance, IDD_MAINDLG,0,addr DlgProc,0
  invoke ExitProcess,eax
  invoke InitCommonControls
DlgProc proc hDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
  .if uMsg==WM_INITDIALOG
    invoke GetMenu, hDlg
    mov hMenu,eax
    invoke GetDlgItem, hDlg, IDC_DATALIST
    mov hList,eax
    call InsertColumn
  .elseif uMsg==WM_CLOSE
    invoke GetMenuState, hMenu, IDM_CONNECT,MF_BYCOMMAND
    .if eax==MF_GRAYED
      invoke ODBCDisconnect, hDlg
    .endif
    invoke EndDialog,hDlg, 0
  .elseif uMsg==WM_COMMAND
    .if lParam==0
      mov eax,wParam
      .if ax==IDM_CONNECT
        invoke ODBCConnect,hDlg
      .elseif ax==IDM_DISCONNECT
        invoke ODBCDisconnect,hDlg
      .elseif ax==IDM_QUERY
        invoke RunQuery,hDlg
      .elseif ax==IDM_CUSTOMQUERY
        invoke DialogBoxParam, hInstance, IDD_QUERYDLG,hDlg, 
		       addr QueryProc, 0
      .endif
    .endif
  .else
    mov eax,FALSE
    ret
  .endif
  mov eax,TRUE
  ret
DlgProc endp

GetProgramPath proc
  invoke GetModuleFileName, NULL,addr ProgPath,sizeof ProgPath
  std
  mov edi,offset ProgPath
  add edi,sizeof ProgPath-1
  mov al,"\"
  mov ecx,sizeof ProgPath
  repne scasb
  cld
  mov byte ptr [edi+2],0
  ret
GetProgramPath endp

SwitchMenuState proc Flag:DWORD
  .if Flag==TRUE 
    invoke EnableMenuItem, hMenu, IDM_CONNECT, MF_GRAYED
    invoke EnableMenuItem, hMenu, IDM_DISCONNECT, MF_ENABLED
    invoke EnableMenuItem, hMenu, IDM_QUERY, MF_ENABLED
    invoke EnableMenuItem, hMenu, IDM_CUSTOMQUERY, MF_ENABLED
  .else
    invoke EnableMenuItem, hMenu, IDM_CONNECT, MF_ENABLED
    invoke EnableMenuItem, hMenu, IDM_DISCONNECT, MF_GRAYED
    invoke EnableMenuItem, hMenu, IDM_QUERY, MF_GRAYED
    invoke EnableMenuItem, hMenu, IDM_CUSTOMQUERY, MF_GRAYED
  .endif
  ret
SwitchMenuState endp

ODBCConnect proc hDlg:DWORD
  invoke SQLAllocHandle, SQL_HANDLE_ENV, SQL_NULL_HANDLE, addr hEnv
  .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
    invoke SQLSetEnvAttr, hEnv,SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3,0
    .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
      invoke SQLAllocHandle, SQL_HANDLE_DBC, hEnv, addr hConn
      .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
        invoke lstrcpy,addr ConnectString,addr strConnect
        invoke lstrcat,addr ConnectString, addr ProgPath
        invoke lstrcat, addr ConnectString,addr DBName
        invoke SQLDriverConnect, hConn, hDlg, addr ConnectString, 
           sizeof ConnectString, addr Conn, sizeof Conn,addr StrLen, 
         SQL_DRIVER_COMPLETE
        .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
          invoke SwitchMenuState,TRUE
          invoke MessageBox,hDlg, addr Conn,addr ConnectCaption,
             MB_OK+MB_ICONINFORMATION
        .else
          invoke SQLFreeHandle, SQL_HANDLE_DBC, hConn
          invoke SQLFreeHandle, SQL_HANDLE_ENV, hEnv
          invoke MessageBox, hDlg, addr ConnFail, addr AppName, 
             MB_OK+MB_ICONERROR
        .endif
      .else
        invoke SQLFreeHandle, SQL_HANDLE_ENV, hEnv
        invoke MessageBox, hDlg, addr AllocConnFail, 
           addr AppName, MB_OK+MB_ICONERROR
      .endif
    .else
      invoke SQLFreeHandle, SQL_HANDLE_ENV, hEnv
      invoke MessageBox, hDlg, addr SetAttrFail, addr AppName, 
           MB_OK+MB_ICONERROR
    .endif
  .else
    invoke MessageBox, hDlg, addr AllocEnvFail, addr AppName, 
           MB_OK+MB_ICONERROR  
  .endif
  ret
ODBCConnect endp

ODBCDisconnect proc hDlg:DWORD
  invoke SQLDisconnect, hConn
  invoke SQLFreeHandle, SQL_HANDLE_DBC, hConn
  invoke SQLFreeHandle, SQL_HANDLE_ENV, hEnv
  invoke SwitchMenuState, FALSE
  invoke ShowWindow,hList, SW_HIDE
  invoke MessageBox,hDlg,addr Disconnect, addr AppName,
         MB_OK+MB_ICONINFORMATION
  ret
ODBCDisconnect endp

InsertColumn proc
  LOCAL lvc:LV_COLUMN
  mov lvc.imask,LVCF_TEXT+LVCF_WIDTH
  mov lvc.pszText,offset Heading1
  mov lvc.lx,150
  invoke SendMessage,hList, LVM_INSERTCOLUMN,0,addr lvc
  mov lvc.pszText,offset Heading2
  invoke SendMessage,hList, LVM_INSERTCOLUMN, 1 ,addr lvc  
  mov lvc.pszText,offset Heading3
  invoke SendMessage,hList, LVM_INSERTCOLUMN, 3 ,addr lvc  
  ret    
InsertColumn endp

FillData proc
  LOCAL lvi:LV_ITEM
  LOCAL row:DWORD

  invoke SQLBindCol, hStmt,1,SQL_C_CHAR, addr TheName, 
         sizeof TheName,addr NameLength
  invoke SQLBindCol, hStmt,2,SQL_C_CHAR, addr TheSurname, 
         sizeof TheSurname,addr SurnameLength
  invoke SQLBindCol, hStmt,3,SQL_C_CHAR, addr TelNo, 
         sizeof TelNo,addr TelNoLength
  mov row,0
  .while TRUE
    mov byte ptr ds:[TheName],0
    mov byte ptr ds:[TheSurname],0
    mov byte ptr ds:[TelNo],0
    invoke SQLFetch, hStmt
    .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
      mov lvi.imask,LVIF_TEXT+LVIF_PARAM
      push row
      pop lvi.iItem  
      mov lvi.iSubItem,0
      mov lvi.pszText, offset TheName
      push row
      pop lvi.lParam
      invoke SendMessage,hList, LVM_INSERTITEM,0, addr lvi
      mov lvi.imask,LVIF_TEXT
      inc lvi.iSubItem
      mov lvi.pszText,offset TheSurname
      invoke SendMessage,hList,LVM_SETITEM, 0,addr lvi
      inc lvi.iSubItem
      mov lvi.pszText,offset TelNo
      invoke SendMessage,hList,LVM_SETITEM, 0,addr lvi
      inc row
    .else
      .break
    .endif
  .endw
  ret
FillData endp

RunQuery proc hDlg:DWORD
  invoke ShowWindow, hList, SW_SHOW
  invoke SendMessage, hList, LVM_DELETEALLITEMS,0,0
  invoke SQLAllocHandle, SQL_HANDLE_STMT, hConn, addr hStmt
  .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
    invoke SQLExecDirect, hStmt, addr SQLStatement, sizeof SQLStatement
    .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
      invoke FillData
    .else
      invoke ShowWindow, hList, SW_HIDE
      invoke MessageBox,hDlg,addr ExecuteFail, addr AppName, 
             MB_OK+MB_ICONERROR
    .endif
    invoke SQLCloseCursor, hStmt
    invoke SQLFreeHandle, SQL_HANDLE_STMT, hStmt
  .else
    invoke ShowWindow, hList, SW_HIDE
    invoke MessageBox,hDlg,addr AllocStmtFail, addr AppName, 
           MB_OK+MB_ICONERROR
  .endif
  ret
RunQuery endp
QueryProc proc hDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD  
  .if uMsg==WM_CLOSE
    invoke SQLFreeHandle, SQL_HANDLE_STMT, hStmt
    invoke EndDialog, hDlg,0
  .elseif uMsg==WM_INITDIALOG
    invoke ShowWindow, hList, SW_SHOW
    invoke SQLAllocHandle, SQL_HANDLE_STMT, hConn, addr hStmt
    .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
      invoke lstrcpy, addr Conn, addr SQLStatement
      invoke lstrcat, addr Conn, addr WhereStatement
      invoke SQLBindParameter,hStmt, 1, SQL_PARAM_INPUT, 
             SQL_C_CHAR, SQL_CHAR,25,0, addr SearchName,25,
             addr StrLen
      invoke SQLPrepare, hStmt, addr Conn, sizeof Conn
    .else
      invoke ShowWindow, hList, SW_HIDE
      invoke MessageBox,hDlg,addr AllocStmtFail, addr AppName, 
             MB_OK+MB_ICONERROR
      invoke EndDialog, hDlg,0
    .endif  
  .elseif uMsg==WM_COMMAND
    mov eax, wParam
    shr eax,16
    .if ax==BN_CLICKED
      mov eax,wParam
      .if ax==IDC_OK
        invoke GetDlgItemText, hDlg, IDC_NAME, addr SearchName, 25
        .if ax==0
          invoke MessageBox, hDlg,addr NoData, addr AppName, 
                 MB_OK+MB_ICONERROR
          invoke GetDlgItem, hDlg, IDC_NAME
          invoke SetFocus, eax
        .else
          invoke lstrlen,addr SearchName
          mov StrLen,eax
          invoke SendMessage, hList, LVM_DELETEALLITEMS,0,0
          invoke SQLExecute, hStmt
          invoke FillData
          invoke SQLCloseCursor, hStmt
        .endif
      .else
        invoke SQLFreeHandle, SQL_HANDLE_STMT, hStmt
        invoke EndDialog, hDlg,0
      .endif
    .endif
  .else
    mov eax,FALSE
    ret
  .endif
  mov eax,TRUE
  ret
QueryProc endp
end start

АНАЛИЗ

start:
 invoke GetModuleHandle, NULL
 mov hInstance,eax
 call GetProgramPath

  Когда программа стартует, она получает описатель экземпляра, затем обнаруживает свой собственный путь. Это делается с рассчётом на то, что база данных, test.mdb, находится в той же папке, что и программа.

GetProgramPath proc
  invoke GetModuleFileName, NULL,addr ProgPath,sizeof ProgPath
  std
  mov edi,offset ProgPath
  add edi,sizeof ProgPath-1
  mov al,"\"
  mov ecx,sizeof ProgPath
  repne scasb
  cld
  mov byte ptr [edi+2],0
  ret
GetProgramPath endp

  Функция GetProgramPath вызывает GetModuleFileName, чтобы получить полный путь и имя программы. После этого мы ищем последний символ "\" в этом пути, чтобы отделить имя файла от пути заменяя имя файла нулём. Таким образом мы получили путь к программе в ProgPath.

  Программа затем отображает основное диалоговое окно, используя DialogBoxParam. Сначала, как только основное диалоговое окно загружается, мы получаем дескрипторы меню и контрола listview. Затем создаём три столбца в элементе управления listview (поскольку мы знаем заблаговременно, что множество результатов состоит из трех столбцов. Далее мы переходим к заполнению таблицы созданной на первом шаге.)

  После этого мы ждем действия пользователя. Если пользователь выбирает "connect" из меню, он вызывает функцию ODBCConnect

ODBCConnect proc hDlg:DWORD
  invoke SQLAllocHandle, SQL_HANDLE_ENV, 
         SQL_NULL_HANDLE, addr hEnv
  .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO

  Первое, что мы делаем это выделяем память для идентификатора окружения, используя SQLAllocHandle:

    invoke SQLSetEnvAttr, hEnv,SQL_ATTR_ODBC_VERSION, 
           SQL_OV_ODBC3,0
    .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO

  После того как мы получили ид. окружения, мы устанавливаем его параметры сообщая о том, что мы будем использовать синтаксис ODBC 3.x вызывая SQLSetEnvAttr:

invoke SQLAllocHandle, SQL_HANDLE_DBC, hEnv, addr hConn
      .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO

  Если все идет хорошо, то мы можем начать соединение выделив память для идентификатора соединения, используя SQLAllocHandle:

invoke lstrcpy,addr ConnectString,addr strConnect
invoke lstrcat,addr ConnectString, addr ProgPath
invoke lstrcat, addr ConnectString,addr DBName

  Конструируем строку соединения, которую мы будем использовать при подключении:

invoke SQLDriverConnect, hConn, hDlg, addr ConnectString, 
       sizeof ConnectString, addr Conn, sizeof Conn,
       addr StrLen, SQL_DRIVER_COMPLETE
        .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
          invoke SwitchMenuState,TRUE
          invoke MessageBox,hDlg, addr Conn,addr ConnectCaption,
          MB_OK+MB_ICONINFORMATION

  Когда строка соединения - готова, мы вызываем SQLDriverConnect, чтобы попытаться подключаться к test.mdb, используя MS Access драйвер ODBC. Если файл test.mdb не обнаружен, то драйвер ODBC известит пользователя об этом поскольку мы определили флаг SQL_DRIVER_COMPLETE. Когда ф-я SQLDriverConnect успешно выполнится, переменная Conn заполнится полной строкой связи созданной драйвером ODBC. Мы отображаем её используя окно сообщений. SwitchMenuState - простая функция, она прорисовывает серым цветом пункты соответствующего меню.

  Сейчас связь с базой данных будет установлена до тех пор пока пользователь не разорвёт её.

  Когда пользователь выбирает пункт меню "View All Records", процедура диалогового окна вызывает RunQuery:

RunQuery proc hDlg:DWORD
  invoke ShowWindow, hList, SW_SHOW
  invoke SendMessage, hList, LVM_DELETEALLITEMS,0,0

  С Тех пор как элемент управления listview был создан, он оставался невидимым, но теперь самое время, чтобы показать его. Также нам нужно удалить все пункты (если имеются) с контрола listview.

invoke SQLAllocHandle, SQL_HANDLE_STMT, hConn, addr hStmt
  .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO

  Затем, программа выделяет память для идентификатора инструкции.

invoke SQLExecDirect, hStmt, addr SQLStatement, 
       sizeof SQLStatement
    .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO

  Выполняем инструкцию SQL используя SQLExecDirect. Я решаю здесь использовать SQLExecDirect, поскольку эта инструкция SQL исполняется лишь однажды:

invoke FillData

  После выполнения инструкции SQL, набор результатов должен быть возвращен. Мы извлекаем данные результатов в элемент управления listview используя функцию FillData:

FillData proc
  LOCAL lvi:LV_ITEM
  LOCAL row:DWORD

  invoke SQLBindCol, hStmt,1,SQL_C_CHAR, addr TheName, 
         sizeof TheName,addr NameLength
  invoke SQLBindCol, hStmt,2,SQL_C_CHAR, addr TheSurname, 
         sizeof TheSurname,addr SurnameLength
  invoke SQLBindCol, hStmt,3,SQL_C_CHAR, addr TelNo, 
         sizeof TelNo,addr TelNoLength

  Здесь, набор результатов уже возвращен. Нам нужно связать все три колонки набора результатов с буфером. Мы вызываем ф-ю SQLBindCol чтобы сделать это. Имейте в виду, что нам нужен отдельный вызов для каждой колоннки, и мы не должны связывать все столбцы: только столбцы, из которых нам нужно получить данные.

  mov row,0
  .while TRUE
    mov byte ptr ds:[TheName],0
    mov byte ptr ds:[TheSurname],0
    mov byte ptr ds:[TelNo],0

  Мы инициализируем буфер значением NULL в случае, если в столбце(столбцах) нет данных. Лучше использовать длину данных возвращенных в переменных которые мы определили в SQLBindCol. В нашем примере, мы могли бы проверить величины в NameLength, SurnameLength, TelNoLength, чтобы получить фактическую длинну возвращенных строк.

invoke SQLFetch, hStmt
.if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  mov lvi.imask,LVIF_TEXT+LVIF_PARAM
  push row
  pop lvi.iItem  
  mov lvi.iSubItem,0
  mov lvi.pszText, offset TheName
  push row
  pop lvi.lParam
  invoke SendMessage,hList, LVM_INSERTITEM,0, addr lvi

  Остальное - просто. Вызываем SQLFetch, чтобы извлечь колонку из набора результата а затем сохранить величины в буферах на контроле listview. Когда колонка становится недоступна (мы достигли конца файла), SQLFetch возвращает SQL_NO_DATA и мы выходим из бесконечного цикла.

invoke SQLCloseCursor, hStmt
invoke SQLFreeHandle, SQL_HANDLE_STMT, hStmt

  Когда мы получили набор результатов, мы должны закрыть курсор, используя SQLCloseCursor, а затем освободить идентификатор инструкции используя SQLFreeHandle.

  Когда пользователь выбирает пункт меню "Query", программа отображает другое диалоговое окно, приглашающее пользователя ввести имя по которому будет осуществлён поиск.

  .elseif uMsg==WM_INITDIALOG
    invoke ShowWindow, hList, SW_SHOW
    invoke SQLAllocHandle, SQL_HANDLE_STMT, hConn, addr hStmt
    .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
      invoke lstrcpy, addr Conn, addr SQLStatement
      invoke lstrcat, addr Conn, addr WhereStatement
      invoke SQLBindParameter,hStmt, 1, SQL_PARAM_INPUT, 
             SQL_C_CHAR, SQL_CHAR,25,0, addr SearchName,
             25,addr StrLen
      invoke SQLPrepare, hStmt, addr Conn, sizeof Conn

  Первая вещь, которую делает диалоговое окно - показывает элемент listview управления. Затем распределяет память для идентификатора инструкции и наконец, создает инструкцию SQL. Эта инструкция SQL означает вернуть все значения содержащиеся в таблице main, где имя имеет значение ?.

select * from main where name=?

  Затем, оно вызывает SQLBindParameter, чтобы соединить ид. инструкции с буфером SearchName. Так что когда инструкция SQL выполняется, драйвер ODBC может получить строку, которая ему нужна из SearchName. Потом оно вызывает SQLPrepare, чтобы скомпилировать инструкцию SQL. Здесь отложенное выполнение разумно т.к. мы подготавливаем/компилируем утверждение SQL только раз и затем, мы будем использовать его много раз. Когда инструкция SQL скомпилирована, последующее выполнение - намного быстрее.

.if ax==IDC_OK
  invoke GetDlgItemText, hDlg, IDC_NAME, 
         addr SearchName, 25
  .if ax==0
    invoke MessageBox, hDlg,addr NoData, 
           addr AppName, MB_OK+MB_ICONERROR
    invoke GetDlgItem, hDlg, IDC_NAME
    invoke SetFocus, eax
  .else

  Когда пользователь занес некоторое имя в окно редактирования и нажал ввод, программа извлекает текст из этого окна редактирования и проверяет действительно ли он был введён, если нет - возвращается значение NULL. В этом случае отображается сообщение и устанавливается клавиатурный фокус на окно редактирования, чтобы подсказать пользователю о необходимости ввода имени.

invoke lstrlen,addr SearchName
mov StrLen,eax
invoke SendMessage, hList, LVM_DELETEALLITEMS,0,0
invoke SQLExecute, hStmt
invoke FillData
invoke SQLCloseCursor, hStmt

  Если имя набирается в окне редактирования, мы находим его длину и сохраняем в StrLen для использования драйвером ODBC (вспомните, что фактически мы получили адрес StrLen для ф-ции

SQLBindParameter). Затем мы вызываем ф-ю SQLExecute, передавая ей в качестве входного параметра ид. инструкции, чтобы выполнить подготовленную инструкцию SQL. Когда происходит возврат из ф-ции SQLExecute, мы вызываем FillData, чтобы отобразить результат в элементе управления listview. Поскольку мы не имеем более полезной информации, мы закрываем курсор вызывая SQLCloseCursor.

2002-2013 (c) wasm.ru