2007. 11. 23. 16:23

::: 예제 분석 :::
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의 여러기능을
사용하기 쉽게 만든것임을 잊지 말기 바란다.

Posted by openserver