ODBC. Урок 5. ODBC пример

Дата публикации 6 июн 2002

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

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

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

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

Код (Text):
  1.  
  2. SQL_NULL_HANDLE equ 0L
  3.  

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

Код (Text):
  1.  
  2. SQL_NULL_HANDLE equ 0
  3.  

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

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

Код (Text):
  1.  
  2. .386
  3. .model flat,stdcall
  4. include \masm32\include\windows.inc
  5. include \masm32\include\kernel32.inc
  6. include \masm32\include\odbc32.inc
  7. include \masm32\include\comctl32.inc
  8. include \masm32\include\user32.inc
  9. includelib \masm32\lib\odbc32.lib
  10. includelib \masm32\lib\comctl32.lib
  11. includelib \masm32\lib\kernel32.lib
  12. includelib \masm32\lib\user32.lib
  13.  
  14. IDD_MAINDLG      equ 101
  15. IDR_MAINMENU     equ  102
  16. IDC_DATALIST     equ  1000
  17. IDM_CONNECT      equ 40001
  18. IDM_DISCONNECT   equ 40002
  19. IDM_QUERY        equ 40003
  20. IDC_NAME         equ 1000
  21. IDC_OK           equ 1001
  22. IDC_CANCEL       equ 1002
  23. IDM_CUSTOMQUERY  equ 40004
  24. IDD_QUERYDLG     equ 102
  25.  
  26.  
  27. DlgProc proto hDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
  28. QueryProc proto hDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
  29. SwitchMenuState proto :DWORD
  30. ODBCConnect proto :DWORD
  31. ODBCDisconnect proto :DWORD
  32. RunQuery proto :DWORD
  33.  
  34. .data?
  35. hInstance dd ?
  36. hEnv dd ?
  37. hConn dd ?
  38. hStmt dd ?
  39. Conn db 256 dup(?)
  40. StrLen dd ?
  41. hMenu dd ?  ; handle to the main menu
  42. hList dd ?    ; handle to the listview control
  43. TheName db 26 dup(?)
  44. TheSurname db 26 dup(?)
  45. TelNo db 21 dup(?)
  46. NameLength dd ?
  47. SurnameLength dd ?
  48. TelNoLength dd ?
  49. SearchName db 26 dup(?)
  50. ProgPath db 256 dup(?)
  51. ConnectString db 1024 dup(?)
  52.  
  53. .data
  54. SQLStatement db "select * from main",0
  55. WhereStatement db " where name=?",0
  56. strConnect db  "DRIVER={Microsoft Access Driver (*.mdb)};DBQ=",0
  57. DBName db "test.mdb",0
  58. ConnectCaption db "Complete Connection String",0
  59. Disconnect db "Disconnect successful",0
  60. AppName db "ODBC Test",0
  61. AllocEnvFail db "Environment handle allocation failed",0
  62. AllocConnFail db "Connection handle allocation failed",0
  63. SetAttrFail db "Cannot set desired ODBC version",0
  64. NoData db "You must type the name in the edit box",0
  65. ExecuteFail db "Execution of SQL statement failed",0
  66. ConnFail db "Connection attempt failed",0
  67. AllocStmtFail db "Statement handle allocation failed",0
  68. Heading1 db "Name",0
  69. Heading2 db "Surname",0
  70. Heading3 db "Telephone No.",0
  71.  
  72. .code
  73. start:
  74.   invoke GetModuleHandle, NULL
  75.   mov hInstance,eax
  76.   call GetProgramPath
  77.   invoke DialogBoxParam, hInstance, IDD_MAINDLG,0,addr DlgProc,0
  78.   invoke ExitProcess,eax
  79.   invoke InitCommonControls
  80. DlgProc proc hDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD
  81.   .if uMsg==WM_INITDIALOG
  82.     invoke GetMenu, hDlg
  83.     mov hMenu,eax
  84.     invoke GetDlgItem, hDlg, IDC_DATALIST
  85.     mov hList,eax
  86.     call InsertColumn
  87.   .elseif uMsg==WM_CLOSE
  88.     invoke GetMenuState, hMenu, IDM_CONNECT,MF_BYCOMMAND
  89.     .if eax==MF_GRAYED
  90.       invoke ODBCDisconnect, hDlg
  91.     .endif
  92.     invoke EndDialog,hDlg, 0
  93.   .elseif uMsg==WM_COMMAND
  94.     .if lParam==0
  95.       mov eax,wParam
  96.       .if ax==IDM_CONNECT
  97.         invoke ODBCConnect,hDlg
  98.       .elseif ax==IDM_DISCONNECT
  99.         invoke ODBCDisconnect,hDlg
  100.       .elseif ax==IDM_QUERY
  101.         invoke RunQuery,hDlg
  102.       .elseif ax==IDM_CUSTOMQUERY
  103.         invoke DialogBoxParam, hInstance, IDD_QUERYDLG,hDlg,
  104.                addr QueryProc, 0
  105.       .endif
  106.     .endif
  107.   .else
  108.     mov eax,FALSE
  109.     ret
  110.   .endif
  111.   mov eax,TRUE
  112.   ret
  113. DlgProc endp
  114.  
  115. GetProgramPath proc
  116.   invoke GetModuleFileName, NULL,addr ProgPath,sizeof ProgPath
  117.   std
  118.   mov edi,offset ProgPath
  119.   add edi,sizeof ProgPath-1
  120.   mov al,"\"
  121.   mov ecx,sizeof ProgPath
  122.   repne scasb
  123.   cld
  124.   mov byte ptr [edi+2],0
  125.   ret
  126. GetProgramPath endp
  127.  
  128. SwitchMenuState proc Flag:DWORD
  129.   .if Flag==TRUE
  130.     invoke EnableMenuItem, hMenu, IDM_CONNECT, MF_GRAYED
  131.     invoke EnableMenuItem, hMenu, IDM_DISCONNECT, MF_ENABLED
  132.     invoke EnableMenuItem, hMenu, IDM_QUERY, MF_ENABLED
  133.     invoke EnableMenuItem, hMenu, IDM_CUSTOMQUERY, MF_ENABLED
  134.   .else
  135.     invoke EnableMenuItem, hMenu, IDM_CONNECT, MF_ENABLED
  136.     invoke EnableMenuItem, hMenu, IDM_DISCONNECT, MF_GRAYED
  137.     invoke EnableMenuItem, hMenu, IDM_QUERY, MF_GRAYED
  138.     invoke EnableMenuItem, hMenu, IDM_CUSTOMQUERY, MF_GRAYED
  139.   .endif
  140.   ret
  141. SwitchMenuState endp
  142.  
  143. ODBCConnect proc hDlg:DWORD
  144.   invoke SQLAllocHandle, SQL_HANDLE_ENV, SQL_NULL_HANDLE, addr hEnv
  145.   .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  146.     invoke SQLSetEnvAttr, hEnv,SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3,0
  147.     .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  148.       invoke SQLAllocHandle, SQL_HANDLE_DBC, hEnv, addr hConn
  149.       .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  150.         invoke lstrcpy,addr ConnectString,addr strConnect
  151.         invoke lstrcat,addr ConnectString, addr ProgPath
  152.         invoke lstrcat, addr ConnectString,addr DBName
  153.         invoke SQLDriverConnect, hConn, hDlg, addr ConnectString,
  154.            sizeof ConnectString, addr Conn, sizeof Conn,addr StrLen,
  155.          SQL_DRIVER_COMPLETE
  156.         .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  157.           invoke SwitchMenuState,TRUE
  158.           invoke MessageBox,hDlg, addr Conn,addr ConnectCaption,
  159.              MB_OK+MB_ICONINFORMATION
  160.         .else
  161.           invoke SQLFreeHandle, SQL_HANDLE_DBC, hConn
  162.           invoke SQLFreeHandle, SQL_HANDLE_ENV, hEnv
  163.           invoke MessageBox, hDlg, addr ConnFail, addr AppName,
  164.              MB_OK+MB_ICONERROR
  165.         .endif
  166.       .else
  167.         invoke SQLFreeHandle, SQL_HANDLE_ENV, hEnv
  168.         invoke MessageBox, hDlg, addr AllocConnFail,
  169.            addr AppName, MB_OK+MB_ICONERROR
  170.       .endif
  171.     .else
  172.       invoke SQLFreeHandle, SQL_HANDLE_ENV, hEnv
  173.       invoke MessageBox, hDlg, addr SetAttrFail, addr AppName,
  174.            MB_OK+MB_ICONERROR
  175.     .endif
  176.   .else
  177.     invoke MessageBox, hDlg, addr AllocEnvFail, addr AppName,
  178.            MB_OK+MB_ICONERROR  
  179.   .endif
  180.   ret
  181. ODBCConnect endp
  182.  
  183. ODBCDisconnect proc hDlg:DWORD
  184.   invoke SQLDisconnect, hConn
  185.   invoke SQLFreeHandle, SQL_HANDLE_DBC, hConn
  186.   invoke SQLFreeHandle, SQL_HANDLE_ENV, hEnv
  187.   invoke SwitchMenuState, FALSE
  188.   invoke ShowWindow,hList, SW_HIDE
  189.   invoke MessageBox,hDlg,addr Disconnect, addr AppName,
  190.          MB_OK+MB_ICONINFORMATION
  191.   ret
  192. ODBCDisconnect endp
  193.  
  194. InsertColumn proc
  195.   LOCAL lvc:LV_COLUMN
  196.   mov lvc.imask,LVCF_TEXT+LVCF_WIDTH
  197.   mov lvc.pszText,offset Heading1
  198.   mov lvc.lx,150
  199.   invoke SendMessage,hList, LVM_INSERTCOLUMN,0,addr lvc
  200.   mov lvc.pszText,offset Heading2
  201.   invoke SendMessage,hList, LVM_INSERTCOLUMN, 1 ,addr lvc  
  202.   mov lvc.pszText,offset Heading3
  203.   invoke SendMessage,hList, LVM_INSERTCOLUMN, 3 ,addr lvc  
  204.   ret    
  205. InsertColumn endp
  206.  
  207. FillData proc
  208.   LOCAL lvi:LV_ITEM
  209.   LOCAL row:DWORD
  210.  
  211.   invoke SQLBindCol, hStmt,1,SQL_C_CHAR, addr TheName,
  212.          sizeof TheName,addr NameLength
  213.   invoke SQLBindCol, hStmt,2,SQL_C_CHAR, addr TheSurname,
  214.          sizeof TheSurname,addr SurnameLength
  215.   invoke SQLBindCol, hStmt,3,SQL_C_CHAR, addr TelNo,
  216.          sizeof TelNo,addr TelNoLength
  217.   mov row,0
  218.   .while TRUE
  219.     mov byte ptr ds:[TheName],0
  220.     mov byte ptr ds:[TheSurname],0
  221.     mov byte ptr ds:[TelNo],0
  222.     invoke SQLFetch, hStmt
  223.     .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  224.       mov lvi.imask,LVIF_TEXT+LVIF_PARAM
  225.       push row
  226.       pop lvi.iItem  
  227.       mov lvi.iSubItem,0
  228.       mov lvi.pszText, offset TheName
  229.       push row
  230.       pop lvi.lParam
  231.       invoke SendMessage,hList, LVM_INSERTITEM,0, addr lvi
  232.       mov lvi.imask,LVIF_TEXT
  233.       inc lvi.iSubItem
  234.       mov lvi.pszText,offset TheSurname
  235.       invoke SendMessage,hList,LVM_SETITEM, 0,addr lvi
  236.       inc lvi.iSubItem
  237.       mov lvi.pszText,offset TelNo
  238.       invoke SendMessage,hList,LVM_SETITEM, 0,addr lvi
  239.       inc row
  240.     .else
  241.       .break
  242.     .endif
  243.   .endw
  244.   ret
  245. FillData endp
  246.  
  247. RunQuery proc hDlg:DWORD
  248.   invoke ShowWindow, hList, SW_SHOW
  249.   invoke SendMessage, hList, LVM_DELETEALLITEMS,0,0
  250.   invoke SQLAllocHandle, SQL_HANDLE_STMT, hConn, addr hStmt
  251.   .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  252.     invoke SQLExecDirect, hStmt, addr SQLStatement, sizeof SQLStatement
  253.     .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  254.       invoke FillData
  255.     .else
  256.       invoke ShowWindow, hList, SW_HIDE
  257.       invoke MessageBox,hDlg,addr ExecuteFail, addr AppName,
  258.              MB_OK+MB_ICONERROR
  259.     .endif
  260.     invoke SQLCloseCursor, hStmt
  261.     invoke SQLFreeHandle, SQL_HANDLE_STMT, hStmt
  262.   .else
  263.     invoke ShowWindow, hList, SW_HIDE
  264.     invoke MessageBox,hDlg,addr AllocStmtFail, addr AppName,
  265.            MB_OK+MB_ICONERROR
  266.   .endif
  267.   ret
  268. RunQuery endp
  269. QueryProc proc hDlg:DWORD, uMsg:DWORD, wParam:DWORD, lParam:DWORD  
  270.   .if uMsg==WM_CLOSE
  271.     invoke SQLFreeHandle, SQL_HANDLE_STMT, hStmt
  272.     invoke EndDialog, hDlg,0
  273.   .elseif uMsg==WM_INITDIALOG
  274.     invoke ShowWindow, hList, SW_SHOW
  275.     invoke SQLAllocHandle, SQL_HANDLE_STMT, hConn, addr hStmt
  276.     .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  277.       invoke lstrcpy, addr Conn, addr SQLStatement
  278.       invoke lstrcat, addr Conn, addr WhereStatement
  279.       invoke SQLBindParameter,hStmt, 1, SQL_PARAM_INPUT,
  280.              SQL_C_CHAR, SQL_CHAR,25,0, addr SearchName,25,
  281.              addr StrLen
  282.       invoke SQLPrepare, hStmt, addr Conn, sizeof Conn
  283.     .else
  284.       invoke ShowWindow, hList, SW_HIDE
  285.       invoke MessageBox,hDlg,addr AllocStmtFail, addr AppName,
  286.              MB_OK+MB_ICONERROR
  287.       invoke EndDialog, hDlg,0
  288.     .endif  
  289.   .elseif uMsg==WM_COMMAND
  290.     mov eax, wParam
  291.     shr eax,16
  292.     .if ax==BN_CLICKED
  293.       mov eax,wParam
  294.       .if ax==IDC_OK
  295.         invoke GetDlgItemText, hDlg, IDC_NAME, addr SearchName, 25
  296.         .if ax==0
  297.           invoke MessageBox, hDlg,addr NoData, addr AppName,
  298.                  MB_OK+MB_ICONERROR
  299.           invoke GetDlgItem, hDlg, IDC_NAME
  300.           invoke SetFocus, eax
  301.         .else
  302.           invoke lstrlen,addr SearchName
  303.           mov StrLen,eax
  304.           invoke SendMessage, hList, LVM_DELETEALLITEMS,0,0
  305.           invoke SQLExecute, hStmt
  306.           invoke FillData
  307.           invoke SQLCloseCursor, hStmt
  308.         .endif
  309.       .else
  310.         invoke SQLFreeHandle, SQL_HANDLE_STMT, hStmt
  311.         invoke EndDialog, hDlg,0
  312.       .endif
  313.     .endif
  314.   .else
  315.     mov eax,FALSE
  316.     ret
  317.   .endif
  318.   mov eax,TRUE
  319.   ret
  320. QueryProc endp
  321. end start
  322.  

АНАЛИЗ

Код (Text):
  1.  
  2. start:
  3.  invoke GetModuleHandle, NULL
  4.  mov hInstance,eax
  5.  call GetProgramPath
  6.  

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

Код (Text):
  1.  
  2. GetProgramPath proc
  3.   invoke GetModuleFileName, NULL,addr ProgPath,sizeof ProgPath
  4.   std
  5.   mov edi,offset ProgPath
  6.   add edi,sizeof ProgPath-1
  7.   mov al,"\"
  8.   mov ecx,sizeof ProgPath
  9.   repne scasb
  10.   cld
  11.   mov byte ptr [edi+2],0
  12.   ret
  13. GetProgramPath endp
  14.  

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

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

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

Код (Text):
  1.  
  2. ODBCConnect proc hDlg:DWORD
  3.   invoke SQLAllocHandle, SQL_HANDLE_ENV,
  4.          SQL_NULL_HANDLE, addr hEnv
  5.   .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  6.  

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

Код (Text):
  1.  
  2.     invoke SQLSetEnvAttr, hEnv,SQL_ATTR_ODBC_VERSION,
  3.            SQL_OV_ODBC3,0
  4.     .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  5.  

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

Код (Text):
  1.  
  2. invoke SQLAllocHandle, SQL_HANDLE_DBC, hEnv, addr hConn
  3.       .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  4.  

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

Код (Text):
  1.  
  2. invoke lstrcpy,addr ConnectString,addr strConnect
  3. invoke lstrcat,addr ConnectString, addr ProgPath
  4. invoke lstrcat, addr ConnectString,addr DBName
  5.  

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

Код (Text):
  1.  
  2. invoke SQLDriverConnect, hConn, hDlg, addr ConnectString,
  3.        sizeof ConnectString, addr Conn, sizeof Conn,
  4.        addr StrLen, SQL_DRIVER_COMPLETE
  5.         .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  6.           invoke SwitchMenuState,TRUE
  7.           invoke MessageBox,hDlg, addr Conn,addr ConnectCaption,
  8.           MB_OK+MB_ICONINFORMATION
  9.  

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

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

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

Код (Text):
  1.  
  2. RunQuery proc hDlg:DWORD
  3.   invoke ShowWindow, hList, SW_SHOW
  4.   invoke SendMessage, hList, LVM_DELETEALLITEMS,0,0
  5.  

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

Код (Text):
  1.  
  2. invoke SQLAllocHandle, SQL_HANDLE_STMT, hConn, addr hStmt
  3.   .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  4.  

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

Код (Text):
  1.  
  2. invoke SQLExecDirect, hStmt, addr SQLStatement,
  3.        sizeof SQLStatement
  4.     .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  5.  

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

Код (Text):
  1.  
  2. invoke FillData
  3.  

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

Код (Text):
  1.  
  2. FillData proc
  3.   LOCAL lvi:LV_ITEM
  4.   LOCAL row:DWORD
  5.  
  6.   invoke SQLBindCol, hStmt,1,SQL_C_CHAR, addr TheName,
  7.          sizeof TheName,addr NameLength
  8.   invoke SQLBindCol, hStmt,2,SQL_C_CHAR, addr TheSurname,
  9.          sizeof TheSurname,addr SurnameLength
  10.   invoke SQLBindCol, hStmt,3,SQL_C_CHAR, addr TelNo,
  11.          sizeof TelNo,addr TelNoLength
  12.  

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

Код (Text):
  1.  
  2.   mov row,0
  3.   .while TRUE
  4.     mov byte ptr ds:[TheName],0
  5.     mov byte ptr ds:[TheSurname],0
  6.     mov byte ptr ds:[TelNo],0
  7.  

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

Код (Text):
  1.  
  2. invoke SQLFetch, hStmt
  3. .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  4.   mov lvi.imask,LVIF_TEXT+LVIF_PARAM
  5.   push row
  6.   pop lvi.iItem  
  7.   mov lvi.iSubItem,0
  8.   mov lvi.pszText, offset TheName
  9.   push row
  10.   pop lvi.lParam
  11.   invoke SendMessage,hList, LVM_INSERTITEM,0, addr lvi
  12.  

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

Код (Text):
  1.  
  2. invoke SQLCloseCursor, hStmt
  3. invoke SQLFreeHandle, SQL_HANDLE_STMT, hStmt
  4.  

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

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

Код (Text):
  1.  
  2.   .elseif uMsg==WM_INITDIALOG
  3.     invoke ShowWindow, hList, SW_SHOW
  4.     invoke SQLAllocHandle, SQL_HANDLE_STMT, hConn, addr hStmt
  5.     .if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
  6.       invoke lstrcpy, addr Conn, addr SQLStatement
  7.       invoke lstrcat, addr Conn, addr WhereStatement
  8.       invoke SQLBindParameter,hStmt, 1, SQL_PARAM_INPUT,
  9.              SQL_C_CHAR, SQL_CHAR,25,0, addr SearchName,
  10.              25,addr StrLen
  11.       invoke SQLPrepare, hStmt, addr Conn, sizeof Conn
  12.  

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

Код (Text):
  1.  
  2. select * from main where name=?
  3.  

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

Код (Text):
  1.  
  2. .if ax==IDC_OK
  3.   invoke GetDlgItemText, hDlg, IDC_NAME,
  4.          addr SearchName, 25
  5.   .if ax==0
  6.     invoke MessageBox, hDlg,addr NoData,
  7.            addr AppName, MB_OK+MB_ICONERROR
  8.     invoke GetDlgItem, hDlg, IDC_NAME
  9.     invoke SetFocus, eax
  10.   .else
  11.  

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

Код (Text):
  1.  
  2. invoke lstrlen,addr SearchName
  3. mov StrLen,eax
  4. invoke SendMessage, hList, LVM_DELETEALLITEMS,0,0
  5. invoke SQLExecute, hStmt
  6. invoke FillData
  7. invoke SQLCloseCursor, hStmt
  8.  

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

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


0 1.490
archive

archive
New Member

Регистрация:
27 фев 2017
Публикаций:
532