一个例子

类别:数据库 点击:0 评论:0 推荐:
一个例子

这一章,我们要把我们已学的知识集合起来。具体来讲,我们来写一个使用ODBC APIs的程序.为简单起见,这个程序中我使用Microsoft的Access数据库(Microsoft Access 97) .

下载例子源程序.

注意:如果你使用的windows.inc 是1.18及其以下版本,在开始编译之前要修改其中的一个小bug.在windows.inc中查找 "SQL_NULL_HANDLE",将得到下面这行:

SQL_NULL_HANDLE equ 0L

将0后面的"L"删除,象这样:

SQL_NULL_HANDLE equ 0

这个程序是一个基于对话框的程序,有一个简单的菜单.当用户选择"connect"时,它将试图连接test.mdb数据库,如果连接成功,将显示由ODBC驱动程序返回的完整连接字符串.接下来,用户可选择"View All Records"命令,程序会使用listview control来显示数据库中的所有数据.用户还可以选择"Query"命令来查询特定的记录.例子程序将会显示一个小对话框提示用户输入想找的人名.当用户按下OK钮或回车键,程序将执行一个查询来查找符合条件的记录.当用户完成对数据库的操作时,可以选择"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 ? ; 主菜单句柄 hList dd ? ; 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来获得程序的全路径名.接着在路径中查找最后一个"\"符",通过将文件名的第一个字符置为0获得(truncate)" 文件名. 因此我们在ProgPath中获得了程序的路径名.

然后程序将用DialogBoxParam显示主对话框.当主对话框第一次被载入时,它将获得菜单句柄和listview control句柄.接下来在listview control中插入三列(因为我们已经知道结果集将包含三列.因为是我们先建的表.)

现在,它就等待用户的动作了.如果用户在菜单中选择"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

获得环境句柄后,程序调用SQLSetEnvAttr来表示将要使用ODBC 3.x的语法.

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

接着填写连接字符串.完整的连接字符串将被用在ConnectionString

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来通过MS Access ODBC 驱动程序连接test.mdb数据库.如果文件test.mdb不存在,ODBC driver将提示用户输入该文件的位置,因为我们已经设定了SQL_DRIVER_COMPLETE标志.当SQLDriverConnect成功返回时, Conn 被填入由ODBC驱动程序创建的完整连接字符串.我们通过一个message box来将其显示给用户. SwitchMenuState是一个单纯切换菜单选项可用的函数.

现在,到数据库的连接已经建立并被打开,并一直保持打开状态直到用户选择关闭.

当用户选择了"View All Records"命令, 对话框过程将调用RunQuery.函数

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

由于listview control在创建时是不可见的,现在我们把它显示出来.还有 要把其中的所有元素(如果有的话)删掉.

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

通过 SQLExecDirect执行已准备好的SQL语句.我这里选择SQLExecDirect 的原因是只须执行一次.

invoke FillData

执行SQL语句后,将返回一个结果集.我们使用 FillData函数来从结果集中解出数据并将其放入listview control中.

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

当列中没有数据时,我们初始化缓冲区为NULLs.更好的方法是用SQLBindCol指定的变量中数据的长度.在我们的例子中,我们可以检查NameLength, SurnameLengthTelNoLength中的值的确切长度.

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 control的缓冲区中.当没有更多的行供检索时(已到达文件尾), 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 control.接下来分配一个语句句柄以创建SQL语句.这个SQL语句有一个"where"子句及一个参数标志符"?". 完整的SQL语句是:

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

当用户在编辑框(edit control)中填入了一些名字并按下回车键, 程序将获得编辑框中的文本并检查是否是空字符串.如果是,则显示一个message box并将键盘焦点设在编辑框上,提示用户输入名字.

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 control显示结果.因为我们不会再用到结果集,调用SQLCloseCursor来关闭它.

本文地址:http://com.8s8s.com/it/it21586.htm