::: 예제 분석 :::
1-4파트에서 다루었던 부분을 모두 총괄하는 예제를 작성해 보도록 하겠다. 본 예제는 DevX.mdb라는 액세스 데이터베이스에 접속해서 모든 레코드를 리스트컨트롤에 표시하고, 파라미터 마커를 사용해서 이름으로 결과셋을 가져오는 기능을 수행 할 수 있다. 이전장에서 배웠던 모든 내용이 나오기도 하지만, 각종 윈도우 컨트롤과 대화상자를 다루는 부분이 나오기 때문에 소스가 길어 보인다. 이전장들을 참고해서 어떻게 구현하는지에 대해서 살펴보기 바란다. 또한, 소스에 주석을 달아서 설명해 두었으므로, 천천히 살펴보기 바란다.
::: DevXODBC 프로젝트파일 다운로드 :::
http://www.openserver.co.kr/DevXODBC.exe 를 다운로드해서 실행하시기 바랍니다.
소스에 주석을 달아놓았습니다. DevX환경으로 살펴보시기 바랍니다. (자동압축해제 파일입니다)
::: 소스 :::
;########## 시스템 선언 ##############################
.386
.model flat,stdcall
;########## 라이브러리 선언 ##############################
include windows.inc
include kernel32.inc
include odbc32.inc ;ODBC를 사용하기 위한 선언
include comctl32.inc
include user32.inc
includelib odbc32.lib ;ODBC 임포트 라이브러리
includelib comctl32.lib
includelib kernel32.lib
includelib user32.lib
;########## 대화상자 ID 설정 ##############################
IDD_MAINDLG equ 101 ; 메인 대화상자
IDR_MAINMENU equ 102 ; 메인메뉴
IDC_DATALIST equ 1000 ; 데이터리스트
IDM_CONNECT equ 40001 ; "연결" 메뉴ID
IDM_DISCONNECT equ 40002 ; "연결해제" 메뉴ID
IDM_QUERY equ 40003 ; "쿼리" 메뉴ID
IDC_NAME equ 1000 ; 이름 입력용 텍스트박스 ID
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 ; ODBC연결 함수
ODBCDisconnect proto :DWORD ; ODBC연결해제 함수
RunQuery proto :DWORD ; 쿼리전송 함수
;########## 데이터 선언 ##############################
.data?
hInstance dd ? ; 인스턴스 핸들
hEnv dd ? ; 환경 핸들
hConn dd ? ; 연결 핸들
hStmt dd ? ; 명령 핸들
Conn db 256 dup(?) ; 연결 문자열
StrLen dd ? ; 문자열 길이저장용 변수
hMenu dd ? ; 메인메뉴 핸들
hList dd ? ; 리스트뷰 핸들
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 ; Where절 파라미터 사용
strConnect db "DRIVER={Microsoft Access Driver (*.mdb)};DBQ=",0 ; ODBC드라이버 설정
DBName db "DevX.mdb",0 ; 데이터베이스 파일이름
ConnectCaption db "연결 문자열 성공",0 ; 연결성공시 표시 문자열
Disconnect db "접속이 성공적으로 해제되었습니다",0 ; 접속해제시 표시 문자열
AppName db "DevXODBC",0 ; 프로그램 이름
AllocEnvFail db "환경핸들 할당에 실패하였습니다",0 ; 환경핸들 할당 실패 문자열
AllocConnFail db "연결핸들 할당에 실패하였습니다",0 ; 접속핸들 할당 실패 문자열
SetAttrFail db "ODBC버전이 맞지 않습니다",0 ; ODBC버전 설정 실패 문자열
NoData db "이름을 반드시 입력하셔야 합니다",0 ; 쿼리문에 쿼리 공백시 처리 문자열
ExecuteFail db "SQL명령 수행에 실패하였습니다",0 ; 쿼리문 실패 문자열
ConnFail db "접속이 실패했습니다",0 ; 접속실패 문자열
AllocStmtFail db "명령핸들 할당에 실패하였습니다",0 ; 명령핸들 할당 실패 문자열
Heading1 db "이름",0 ; 리스트 헤더 문자열 1
Heading2 db "성씨",0 ; 리스트 헤더 문자열 2
Heading3 db "전화번호.",0 ; 리스트 헤더 문자열 3
;########## 코드 선언 ##############################
.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 ; 환경핸들 생성, hEnv 핸들생성
.if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
invoke SQLSetEnvAttr, hEnv,SQL_ATTR_ODBC_VERSION, SQL_OV_ODBC3,0 ; ODBC버전3으로 환경 설정!!
.if ax==SQL_SUCCESS || ax==SQL_SUCCESS_WITH_INFO
invoke SQLAllocHandle, SQL_HANDLE_DBC, hEnv, addr hConn ; 연결핸들 생성, 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 ; OBDC핸들 해제
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
::: 마치며 :::
이상으로 DevX환경에서 ODBC DataBase Programming에 대하여 알아보았다. 현재 예제는
기초중의 기초예제일 뿐이다. 보다 사용하기 쉽게 자신만의 루틴으로 변화 하는 기능을
넣어서 자신만의 라이브러리로 만들어서 사용하기 바란다. ODBC방법은 처리속도가 늦다는
단점으로 지적되어 왔지만, 본 가이드를 기초로 최적화 해서 사용한다면 사용 라이브러리와
버금가는 수준으로 끌어올릴 수 있다. 상용라이브러리들도 결국은 ODBC의 여러기능을
사용하기 쉽게 만든것임을 잊지 말기 바란다.