본 장은 Delphi 인터페이스에서 사용하는 함수와 예제를 설명한다.
Delphi 인터페이스에는 Visual Basic과 비슷한 형태로, 클라이언트 라이브러리에서 제공하는 함수를 호출하기 위한 인터페이스 모듈이 존재한다. 개발자는 인터페이스 모듈을 설치하면 모든 함수를 사용할 수 있다. Delphi는 Power Builder나 Visual Basic과 달리 포인터 개념을 가지고 있고 값의 전달 방법이 C 언어와 비슷하며 파스칼의 문법을 따르고 있기 때문에 기존의 언어에 익숙한 사람이라면 쉽게 접근할 수 있다. 따라서 별도의 매크로나 컴포넌트가 없다.
Delphi 인터페이스 컴포넌트는 다음과 같다.
모듈 | 설명 |
---|---|
atmi.pas | atmi 함수에 대한 프로토타입 정의 파일이다. |
fdl.pas | 필드키 함수에 대한 프로토타입 정의 파일이다. |
atmi 함수와 필드키 함수에 대한 프로토타입 및 기능에 대한 설명은 "Tmax Reference Guide" 및 "Tmax Application Development Guide"를 참고하고, 예제 프로그램으로 Delphi 인터페이스 사용법의 설명을 대신한다.
사원번호를 입력하면 이름과 소속, 부서명 등을 Oracle 데이터베이스에서 찾아 읽어 오는 프로그램이다.
atmi.pas, fdl.pas, TuxSvc.pas를 모듈로 프로젝트에서 추가한다.
공통 프로그램
프로그램 파일 | 설명 |
---|---|
demo.f | 필드키 버퍼를 정의한 파일이다. |
tmax | 라이브러리 파일이다. |
클라이언트 프로그램
프로그램 파일 | 설명 |
---|---|
Atmi.dcu | atmi 소스 파일을 컴파일해서 생성되는 object 파일이다. |
Atmi.pas | atmi 함수에 대한 프로토타입을 정의한 파일이다. |
EmployeeMgr.bpg | 프로젝트 그룹 파일이다. |
EmployeeMgr.cfg | 프로젝트 환경설정 파일이다. |
EmployeeMgr.dof | Delphi 옵션 파일이다. |
EmployeeMgr.dpr | 여러 개의 pas 파일과 dfm 파일의 정보를 가지고 있는 프로젝트 파일이다. |
EmployeeMgr.exe | object 파일을 실행 가능한 파일로 만든 것이다. |
EmployeeMgr.res | 컴파일된 이진 리소스 파일이다. |
Fdl.dcu | Fdl 소스 파일을 컴파일해서 생성되는 object 파일이다. |
Fdl.pas | 필드키 함수에 대한 프로토타입을 정의한 파일이다. |
TuxSvc.pas | Tuxedo 전환용 소스 파일이다. |
main.dcu | 메인 소스 파일을 컴파일해서 생기는 object 파일이다. |
main.dfm | 메인 Form 파일이다. |
main.pas | 클라이언트 프로그램이다. |
서버 프로그램
프로그램 파일 | 설명 |
---|---|
emp_c.mk | Makefile이다. |
emp_c.pc | 서버 프로그램으로 AIX와 Oracle 9i를 사용한다. |
employee.m | Tmax 환경설정 파일이다. |
클라이언트 프로그램
기능 | 설명 |
---|---|
Tmax 연결 | Tmax 사용자 계정과 애플리케이션 정의 유저명을 인자로 연결한다. |
버퍼 유형 | FIELD KEY 버퍼, 필드키 파일을 fdlc 유틸리티로 컴파일하여 'fdl'파일의 생성이 필요하다. |
통신 유형 | tpcall()을 이용한 동기 통신, 보내는 버퍼와 받는 버퍼가 같다. |
트랜잭션 여부 | TMS에서 AutoTransaction을 부여한다. |
서버 프로그램
기능 | 설명 |
---|---|
서비스 | FDLSELECT, FDLDELETE, FDLUPDATE, FDLINSERT를 작성한다. |
데이터베이스 지정 | Oracle 데이터베이스를 사용한다. 시스템 구성 파일의 SVRGROUP에 데이터베이스 정보를 지정한다. |
다음은 DB operation을 위한 기본 테이블의 예제이다.
EMPNO NUMBER NOT NULL P1 ENAME VARCHAR(16) JOB VARCHAR(16) MGR NUMBER HIREDATE DATE SAL NUMBER(7,2) COMM NUMBER(7,2) DEPTNO NUMBER
다음은 필드키 버퍼를 정의한 파일의 예제이다.
<demo.f>
#For tmax demo employee program EMPNO 7500 long - - ENAME 7501 string - - JOB 7502 string - - MGR 7503 long - - DATE 7504 string - - SAL 7505 float - - COMM 7506 float - - DEPTNO 7507 long - - E_TYPE 9009 long - - E_CODE 9010 long - - E_MSG 9011 string - - E_TMP 9012 long - -
다음은 Tmax 환경설정 파일의 예제이다.
<tmconfig.m>
*DOMAIN dom1 SHMKEY = 70000, MAXUSER = 200, MINCLH = 1, MAXCLH = 5, TPORTNO = 8888, BLOCKTIME = 200, TXTIME = 200 *NODE tmax1 TMAXDIR = "/home/tmax", APPDIR = "/home/tmax/appbin", PATHDIR = "/home/tmax/path", TLOGDIR = "/home/tmax/log/tlog", SLOGDIR = "/home/tmax/log/slog" ULOGDIR = "/home/tmax/log/ulog" *SVRGROUP svg1 NODENAME = tmax1, DBNAME = ORACLE, OPENINFO = "ORACLE_XA+Acc=P/scott/tiger+SesTm=60", TMSNAME = svg1_tms *SERVER emp_c SVGNAME = svg1, MIN = 1 *SERVICE FDLSELECT SVRNAME = emp_c FDLUPDATE SVRNAME = emp_c FDLDELETE SVRNAME = emp_c FDLINSERT SVRNAME = emp_c
다음은 메인화면 Form 디자인 화면이다.
다음은 예제에서 사용할 메인 화면 Form 디자인 구성표이다.
컨트롤 이름 | 컨트롤 종류 | 비고 |
---|---|---|
EmployeeMgrForm | Form | Caption="사원관리 프로그램" |
LabelErr | TLabel | Caption="오류" |
BtnExit | TButton | Caption="종료" |
BtnIns | TButton | Caption="입력" |
BtnDel | TButton | Caption="삭제" |
BtnUdt | TButton | Caption="수정" |
BtnSel | TButton | Caption="조회" |
EditName | TEdit | |
EditEmpNo | TEdit | |
EditDept | TEdit | |
EditComm | TEdit | |
EditSal | TEdit | |
EditDate | TEdit | |
EditMgr | TEdit | |
EditJob | TEdit | |
MList | TEdit | |
BtnReturn | TEdit |
다음은 메인 화면 Form 디자인 화면을 실행하는 예제이다.
<main.pas>
unit main; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TEmployeeMgrForm = class(TForm) LabelEmpNo: TLabel; LabelName: TLabel; EditEmpNo: TEdit; EditName: TEdit; GroupBoxInfo: TGroupBox; LabelJob: TLabel; LabelMgr: TLabel; LabelDate: TLabel; LabelSal: TLabel; EditJob: TEdit; EditMgr: TEdit; EditDate: TEdit; EditSal: TEdit; EditComm: TEdit; EditDept: TEdit; LabelComm: TLabel; LabelDept: TLabel; BtnSel: TButton; BtnUdt: TButton; BtnDel: TButton; BtnIns: TButton; BtnExit: TButton; LabelErr: TLabel; MList: TMemo; BtnReturn: TButton; procedure BtnExitClick(Sender: TObject); procedure BtnSelClick(Sender: TObject); procedure BtnUdtClick(Sender: TObject); procedure BtnDelClick(Sender: TObject); procedure BtnInsClick(Sender: TObject); procedure tmaxStart(); procedure BtnReturnClick(Sender: TObject); procedure ViewErr(a:Pointer); { Private declarations } public { Public declarations } end; var EmployeeMgrForm: TEmployeeMgrForm; implementation // Atmi, Fdl 사용 uses Atmi, Fdl; const BufferSize = 1024; {$R *.DFM} // String에 길이값 생략 {$H+} procedure TEmployeeMgrForm.tmaxStart(); var tpinfo: pTPSTART; ret: integer; begin // atmi.pas 파일에 정의되어 있습니다. // Function tmaxreadenv(a:PChar; b:PChar):Integer; cdecl; external TmaxDLL; // 자세한 설명은 Tmax Reference Guide를 참고하십시오. ret := tmaxreadenv('C:\tmax.env', 'aix5l389'); if ret < 0 then begin ShowMessage('tmaxreadenv Error'); Exit; end; // tpstart시 사용자 정보를 보내기 위한 버퍼 할당 tpinfo := tpalloc('TPSTART', NIL, 0); if tpinfo = Nil then begin ShowMessage('tpinfo tpalloc failed,' + StrPas(tpstrerror(gettperrno))); tpfree(tpinfo); Exit; end; // // 다음과 같이 옵션을 지정할 수 있습니다. // // 사용자 인증 보안을 위한 사용자 계정 // tpinfo.usrname := 'tmax'; // 자발적인 메시지 수신시 사용되는 구별 이름 // tpinfo.cltname := 'tmax'; // 자발적인 메시지를 허락 // tpinfo.flags := TPUNSOL_POLL; // Tmax 접속 ret := tpstart(tpinfo); if ret < 0 then begin ShowMessage('tpstart failed' + StrPas(tpstrerror(gettperrno))); tpfree(tpinfo); tpend(); Exit; end; // 사용자 정보를 보내기 위한 버퍼 해제 tpfree(tpinfo); end; // 종료 버튼을 눌렀을 때. procedure TEmployeeMgrForm.BtnExitClick(Sender: TObject); begin tpend(); close; end; procedure TEmployeeMgrForm.BtnUdtClick(Sender: TObject); var sndbuf, revbuf: Pointer; ret, empno_l, mgr_l, deptno_l: longint; sal_f, comm_f: single; job_s, ename_s: pointer; rlen: integer; date_s: string[100]; begin tmaxStart(); // 입력할 버퍼 할당 sndbuf := fballoc(1000, 10000); if sndbuf = Nil then begin ShowMessage('sndbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno))); fbfree(sndbuf); tpend(); Exit; end; // 받을 버퍼 할당 revbuf := fballoc(1000, 10000); if revbuf = Nil then begin ShowMessage('revbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno))); fbfree(revbuf); tpend(); Exit; end; empno_l := StrToInt(EditEmpNo.text); fbput(sndbuf, fbget_fldkey('EMPNO'), @empno_l, 0); ename_s := PChar(EditName.text); fbput(sndbuf, fbget_fldkey('ENAME'), ename_s, 0); job_s := PChar(EditJob.text); fbput(sndbuf, fbget_fldkey('JOB'), job_s, 0); mgr_l := StrToInt(EditMgr.text); fbput(sndbuf, fbget_fldkey('MGR'), @mgr_l, 0); // date_s := PChar(EditDate.text); // fbput(sndbuf, fbget_fldkey('DATE'), @date_s, 0); date_s := EditDate.text; rlen := length(EditDate.text); ret := fbchg_tu(sndbuf, fbget_fldkey('DATE'),0 ,@date_s[1], 0); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'DATE error!!!'; Exit; end; sal_f := StrToFloat(EditSal.text); fbput(sndbuf, fbget_fldkey('SAL'), @sal_f, 0); comm_f := StrToFloat(EditComm.text); fbput(sndbuf, fbget_fldkey('COMM'), @comm_f, 0); deptno_l := StrToInt(EditDept.text); fbput(sndbuf, fbget_fldkey('DEPTNO'), @deptno_l, 0); // 트랜잭션 시작 ret := tx_begin(); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'tx_begin error'; Exit; end; // 서비스를 호출 ret := tpcall('FDLUPDATE', sndbuf, 0, @revbuf, @rlen, 0); if ret < 0 then begin ViewErr(revbuf); tx_rollback(); fbfree(sndbuf); fbfree(revbuf); tpend(); Exit; end; ret := tx_commit(); if ret < 0 then begin ShowMessage('tx_commit failed! ' + StrPas(tpstrerror(gettperrno))); tx_rollback(); fbfree(sndbuf); fbfree(revbuf); tpend(); Exit; end; // 할당했던 버퍼를 해제 fbfree(sndbuf); fbfree(revbuf); tpend(); end; procedure TEmployeeMgrForm.BtnDelClick(Sender: TObject); var sndbuf, revbuf: Pointer; empno_l, ret: longint; rlen: integer; begin tmaxStart(); // 서버로 보낼 버퍼 할당 sndbuf := fballoc(1000, 10000); if sndbuf = Nil then begin ShowMessage('sndbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno))); tpend(); Exit; end; // 서버에서 받을 버퍼 할당 revbuf := fballoc(1000, 10000); if sndbuf = Nil then begin ShowMessage('sndbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno))); tpend(); Exit; end; empno_l := StrToInt(EditEmpNo.text); fbput(sndbuf, fbget_fldkey('EMPNO'), @empno_l, 0); // 트랜잭션 시작 ret := tx_begin(); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'tx_begin error'; Exit; end; // 서버에 서비스를 호출 ret := tpcall('FDLDELETE', sndbuf, 0, @revbuf, @rlen, 0); if ret < 0 then begin ViewErr(revbuf); tx_rollback(); fbfree(sndbuf); fbfree(revbuf); tpend(); Exit; end; ret := tx_commit(); if ret < 0 then begin ViewErr(revbuf); tx_rollback(); fbfree(sndbuf); fbfree(revbuf); tpend(); Exit; end; // 할당했던 버퍼를 해제 fbfree(sndbuf); fbfree(revbuf); tpend(); end; procedure TEmployeeMgrForm.BtnInsClick(Sender: TObject); var sndbuf, revbuf: Pointer; empno_l, mgr_l, deptno_l: longint; sal_f, comm_f: single; job_s, ename_s, date_s: string[100]; ret, rlen: integer; begin // tpstart 실행 함수 tmaxStart(); // 입력할 버퍼 할당 LabelErr.Caption := ''; sndbuf := fballoc(1000,10000); if sndbuf = Nil then begin LabelErr.Caption := 'sndbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno)); fbfree(sndbuf); tpend(); Exit; end; revbuf := fballoc(100,1000); if revbuf = Nil then begin LabelErr.Caption := 'revbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno)); fbfree(revbuf); tpend(); Exit; end; empno_l := StrToInt(EditEmpNo.text); ret := fbput(sndbuf, fbget_fldkey('EMPNO'), @empno_l, 0); if ret = -1 then begin LabelErr.Caption := 'EMPNO error!!!'; Exit; end; ename_s := EditName.text; rlen := length(EditName.text); ret := fbchg_tu(sndbuf, fbget_fldkey('ENAME'),0 ,@ename_s[1], 0); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'ENAME error!!!'; Exit; end; job_s := EditJob.text; rlen := length(EditJob.text); ret := fbchg_tu(sndbuf, fbget_fldkey('JOB'),0 ,@job_s[1], 0); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'JOB error!!!'; Exit; end; mgr_l := StrToInt(EditMgr.text); ret := fbput(sndbuf, fbget_fldkey('MGR'), @mgr_l, 0); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'MGR error!!!'; Exit; end; date_s := EditDate.text; rlen := length(EditDate.text); ret := fbchg_tu(sndbuf, fbget_fldkey('DATE'),0 ,@date_s[1], 0); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'DATE error!!!'; Exit; end; sal_f := StrToFloat(EditSal.text); ret := fbput(sndbuf, fbget_fldkey('SAL'), @sal_f, 0); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'SAL error!!!'; Exit; end; comm_f := StrToFloat(EditComm.text); ret := fbput(sndbuf, fbget_fldkey('COMM'), @comm_f, 0); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'COMM error!!!'; Exit; end; deptno_l := StrToInt(EditDept.text); ret := fbput(sndbuf, fbget_fldkey('DEPTNO'), @deptno_l, 0); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'DEPTNO error!!!'; Exit; end; // 트랜잭션 시작 ret := tx_begin(); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'tx_begin error'; Exit; end; // 서비스를 호출 ret := tpcall('FDLINSERT', sndbuf, 0, @revbuf, @rlen, 0); if ret < 0 then begin ViewErr(revbuf); tx_rollback(); fbfree(sndbuf); fbfree(revbuf); tpend(); Exit; end; ret := tx_commit(); if ret < 0 then begin ViewErr(revbuf); tx_rollback(); fbfree(sndbuf); fbfree(revbuf); tpend(); Exit; end; // 할당했던 버퍼를 해제 fbfree(revbuf); fbfree(sndbuf); tpend(); end; procedure TEmployeeMgrForm.BtnReturnClick(Sender: TObject); begin BtnReturn.Visible := false; MList.Visible := false; end; procedure TEmployeeMgrForm.ViewErr( a:Pointer ); var typeS: PChar; codeS: PChar; msgS: PChar; tmpS: PChar; // Iret: Integer; begin // E_TYPE 9009 long - - // E_CODE 9010 long - - // E_MSG 9011 string - - // E_TMP 9012 long - - typeS := fbgetvals(a, fbget_fldkey('E_TYPE'), 0); codeS := fbgetvals(a, fbget_fldkey('E_CODE'), 0); msgS := fbgetvals(a, fbget_fldkey('E_MSG'), 0); tmpS := fbgetvals(a, fbget_fldkey('E_TMP'), 0); ShowMessage('Error Type : ' + typeS + ' ,Code : ' + codeS + ' ,Message : ' + msgS + ' ,Tmp : ' + tmpS); end;
다음은 조회 화면 Form 디자인 화면이다.
다음은 조회 화면 Form 디자인 화면을 실행하는 예제이다.
unit main; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TEmployeeMgrForm = class(TForm) LabelEmpNo: TLabel; LabelName: TLabel; EditEmpNo: TEdit; EditName: TEdit; GroupBoxInfo: TGroupBox; LabelJob: TLabel; LabelMgr: TLabel; LabelDate: TLabel; LabelSal: TLabel; EditJob: TEdit; EditMgr: TEdit; EditDate: TEdit; EditSal: TEdit; EditComm: TEdit; EditDept: TEdit; LabelComm: TLabel; LabelDept: TLabel; BtnSel: TButton; BtnUdt: TButton; BtnDel: TButton; BtnIns: TButton; BtnExit: TButton; LabelErr: TLabel; MList: TMemo; BtnReturn: TButton; procedure BtnExitClick(Sender: TObject); procedure BtnSelClick(Sender: TObject); procedure BtnUdtClick(Sender: TObject); procedure BtnDelClick(Sender: TObject); procedure BtnInsClick(Sender: TObject); procedure tmaxStart(); procedure BtnReturnClick(Sender: TObject); procedure ViewErr( a:Pointer ); { Private declarations } public { Public declarations } end; var EmployeeMgrForm: TEmployeeMgrForm; implementation // Atmi, Fdl 사용 uses Atmi, Fdl; const BufferSize = 1024; {$R *.DFM} // String에 길이값 생략 {$H+} procedure TEmployeeMgrForm.tmaxStart(); var tpinfo: pTPSTART; ret: integer; begin // atmi.pas 파일에 정의되어 있습니다. // Function tmaxreadenv(a:PChar; b:PChar):Integer; cdecl; external TmaxDLL; // 자세한 설명은 Tmax Reference Guide를 참고하십시오. ret := tmaxreadenv('C:\tmax.env', 'aix5l389'); if ret < 0 then begin ShowMessage('tmaxreadenv Error'); Exit; end; // tpstart시 사용자 정보를 보내기 위한 버퍼 할당 tpinfo := tpalloc('TPSTART', NIL, 0); if tpinfo = Nil then begin ShowMessage('tpinfo tpalloc failed,' + StrPas(tpstrerror(gettperrno))); tpfree(tpinfo); Exit; end; // // 다음과 같이 옵션을 지정할 수 있습니다. // // 사용자 인증 보안을 위한 사용자 계정 // tpinfo.usrname := 'tmax'; // 자발적인 메시지 수신시 사용되는 구별 이름 // tpinfo.cltname := 'tmax'; // 자발적인 메시지를 허락 // tpinfo.flags := TPUNSOL_POLL; // Tmax 접속 ret := tpstart(tpinfo); if ret < 0 then begin ShowMessage('tpstart failed' + StrPas(tpstrerror(gettperrno))); tpfree(tpinfo); tpend(); Exit; end; // 사용자 정보를 보내기 위한 버퍼 해제 tpfree(tpinfo); end; // 종료 버튼을 눌렀을 때. procedure TEmployeeMgrForm.BtnExitClick(Sender: TObject); begin tpend(); close; end; procedure TEmployeeMgrForm.BtnUdtClick(Sender: TObject); var sndbuf, revbuf: Pointer; ret, empno_l, mgr_l, deptno_l: longint; sal_f, comm_f: single; job_s, ename_s: pointer; rlen: integer; date_s: string[100]; begin tmaxStart(); // 입력할 버퍼 할당 sndbuf := fballoc(1000, 10000); if sndbuf = Nil then begin ShowMessage('sndbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno))); fbfree(sndbuf); tpend(); Exit; end; // 받을 버퍼 할당 revbuf := fballoc(1000, 10000); if revbuf = Nil then begin ShowMessage('revbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno))); fbfree(revbuf); tpend(); Exit; end; empno_l := StrToInt(EditEmpNo.text); fbput(sndbuf, fbget_fldkey('EMPNO'), @empno_l, 0); ename_s := PChar(EditName.text); fbput(sndbuf, fbget_fldkey('ENAME'), ename_s, 0); job_s := PChar(EditJob.text); fbput(sndbuf, fbget_fldkey('JOB'), job_s, 0); mgr_l := StrToInt(EditMgr.text); fbput(sndbuf, fbget_fldkey('MGR'), @mgr_l, 0); // date_s := PChar(EditDate.text); // fbput(sndbuf, fbget_fldkey('DATE'), @date_s, 0); date_s := EditDate.text; rlen := length(EditDate.text); ret := fbchg_tu(sndbuf, fbget_fldkey('DATE'),0 ,@date_s[1], 0); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'DATE error!!!'; Exit; end; sal_f := StrToFloat(EditSal.text); fbput(sndbuf, fbget_fldkey('SAL'), @sal_f, 0); comm_f := StrToFloat(EditComm.text); fbput(sndbuf, fbget_fldkey('COMM'), @comm_f, 0); deptno_l := StrToInt(EditDept.text); fbput(sndbuf, fbget_fldkey('DEPTNO'), @deptno_l, 0); // 트랜잭션 시작 ret := tx_begin(); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'tx_begin error'; Exit; end; // 서비스를 호출 ret := tpcall('FDLUPDATE', sndbuf, 0, @revbuf, @rlen, 0); if ret < 0 then begin ViewErr(revbuf); tx_rollback(); fbfree(sndbuf); fbfree(revbuf); tpend(); Exit; end; ret := tx_commit(); if ret < 0 then begin ShowMessage('tx_commit failed! ' + StrPas(tpstrerror(gettperrno))); tx_rollback(); fbfree(sndbuf); fbfree(revbuf); tpend(); Exit; end; // 할당했던 버퍼를 해제 fbfree(sndbuf); fbfree(revbuf); tpend(); end; procedure TEmployeeMgrForm.BtnDelClick(Sender: TObject); var sndbuf, revbuf: Pointer; empno_l, ret: longint; rlen: integer; begin tmaxStart(); // 서버로 보낼 버퍼 할당 sndbuf := fballoc(1000, 10000); if sndbuf = Nil then begin ShowMessage('sndbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno))); tpend(); Exit; end; // 서버에서 받을 버퍼 할당 revbuf := fballoc(1000, 10000); if sndbuf = Nil then begin ShowMessage('sndbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno))); tpend(); Exit; end; empno_l := StrToInt(EditEmpNo.text); fbput(sndbuf, fbget_fldkey('EMPNO'), @empno_l, 0); // 트랜잭션 시작 ret := tx_begin(); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'tx_begin error'; Exit; end; // 서버에 서비스를 호출 ret := tpcall('FDLDELETE', sndbuf, 0, @revbuf, @rlen, 0); if ret < 0 then begin ViewErr(revbuf); tx_rollback(); fbfree(sndbuf); fbfree(revbuf); tpend(); Exit; end; ret := tx_commit(); if ret < 0 then begin ViewErr(revbuf); tx_rollback(); fbfree(sndbuf); fbfree(revbuf); tpend(); Exit; end; // 할당했던 버퍼를 해제 fbfree(sndbuf); fbfree(revbuf); tpend(); end; procedure TEmployeeMgrForm.BtnInsClick(Sender: TObject); var sndbuf, revbuf: Pointer; empno_l, mgr_l, deptno_l: longint; sal_f, comm_f: single; job_s, ename_s, date_s: string[100]; ret, rlen: integer; begin // tpstart 실행 함수 tmaxStart(); // 입력할 버퍼 할당 LabelErr.Caption := ''; sndbuf := fballoc(1000,10000); if sndbuf = Nil then begin LabelErr.Caption := 'sndbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno)); fbfree(sndbuf); tpend(); Exit; end; revbuf := fballoc(100,1000); if revbuf = Nil then begin LabelErr.Caption := 'revbuf tpalloc failed, ' + StrPas(tpstrerror(gettperrno)); fbfree(revbuf); tpend(); Exit; end; empno_l := StrToInt(EditEmpNo.text); ret := fbput(sndbuf, fbget_fldkey('EMPNO'), @empno_l, 0); if ret = -1 then begin LabelErr.Caption := 'EMPNO error!!!'; Exit; end; ename_s := EditName.text; rlen := length(EditName.text); ret := fbchg_tu(sndbuf, fbget_fldkey('ENAME'),0 ,@ename_s[1], 0); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'ENAME error!!!'; Exit; end; job_s := EditJob.text; rlen := length(EditJob.text); ret := fbchg_tu(sndbuf, fbget_fldkey('JOB'),0 ,@job_s[1], 0); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'JOB error!!!'; Exit; end; mgr_l := StrToInt(EditMgr.text); ret := fbput(sndbuf, fbget_fldkey('MGR'), @mgr_l, 0); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'MGR error!!!'; Exit; end; date_s := EditDate.text; rlen := length(EditDate.text); ret := fbchg_tu(sndbuf, fbget_fldkey('DATE'),0 ,@date_s[1], 0); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'DATE error!!!'; Exit; end; sal_f := StrToFloat(EditSal.text); ret := fbput(sndbuf, fbget_fldkey('SAL'), @sal_f, 0); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'SAL error!!!'; Exit; end; comm_f := StrToFloat(EditComm.text); ret := fbput(sndbuf, fbget_fldkey('COMM'), @comm_f, 0); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'COMM error!!!'; Exit; end; deptno_l := StrToInt(EditDept.text); ret := fbput(sndbuf, fbget_fldkey('DEPTNO'), @deptno_l, 0); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'DEPTNO error!!!'; Exit; end; // 트랜잭션 시작 ret := tx_begin(); if ret = -1 then begin LabelErr.Caption := LabelErr.Caption + 'tx_begin error'; Exit; end; // 서비스를 호출 ret := tpcall('FDLINSERT', sndbuf, 0, @revbuf, @rlen, 0); if ret < 0 then begin ViewErr(revbuf); tx_rollback(); fbfree(sndbuf); fbfree(revbuf); tpend(); Exit; end; ret := tx_commit(); if ret < 0 then begin ViewErr(revbuf); tx_rollback(); fbfree(sndbuf); fbfree(revbuf); tpend(); Exit; end; // 할당했던 버퍼를 해제 fbfree(revbuf); fbfree(sndbuf); tpend(); end; procedure TEmployeeMgrForm.BtnReturnClick(Sender: TObject); begin BtnReturn.Visible := false; MList.Visible := false; end; procedure TEmployeeMgrForm.ViewErr( a:Pointer ); var typeS: PChar; codeS: PChar; msgS: PChar; tmpS: PChar; // Iret: Integer; begin // E_TYPE 9009 long - - // E_CODE 9010 long - - // E_MSG 9011 string - - // E_TMP 9012 long - - typeS := fbgetvals(a, fbget_fldkey('E_TYPE'), 0); codeS := fbgetvals(a, fbget_fldkey('E_CODE'), 0); msgS := fbgetvals(a, fbget_fldkey('E_MSG'), 0); tmpS := fbgetvals(a, fbget_fldkey('E_TMP'), 0); ShowMessage('Error Type : ' + typeS + ' ,Code : ' + codeS + ' ,Message : ' + msgS + ' ,Tmp : ' + tmpS); end;
다음은 Oracle 에러 출력 화면이다.
다음은 Oracle 에러 출력 화면을 실행하는 예제이다.
procedure TEmployeeMgrForm.ViewErr( a:Pointer ); var typeS: PChar; codeS: PChar; msgS: PChar; tmpS: PChar; // Iret: Integer; begin // E_TYPE 9009 long - - // E_CODE 9010 long - - // E_MSG 9011 string - - // E_TMP 9012 long - - typeS := fbgetvals(a, fbget_fldkey('E_TYPE'), 0); codeS := fbgetvals(a, fbget_fldkey('E_CODE'), 0); msgS := fbgetvals(a, fbget_fldkey('E_MSG'), 0); tmpS := fbgetvals(a, fbget_fldkey('E_TMP'), 0); ShowMessage('Error Type : ' + typeS + ' ,Code : ' + codeS + ' ,Message : ' + msgS + ' ,Tmp : ' + tmpS); end;
다음은 서비스 프로그램의 예제이다.
<em4p_c.pc>
#include <stdio.h> #include <ctype.h> #include <tuxinc/macro.h> #include "../../fdl/demo_fdl.h" EXEC SQL include sqlca.h; EXEC SQL INCLUDE ORACA; EXEC ORACLE OPTION (ORACA=YES); EXEC ORACLE OPTION (RELEASE_CURSOR=YES); #define INP 1 #define ORA 2 #define TMX 3 #define APP 4 EXEC SQL begin declare section; int h_empno; char h_ename[11]; char h_job[10]; int h_mgr; char h_date[11]; float h_sal; float h_comm; int h_deptno; EXEC SQL end declare section; void svc_error(long type, long err_code, char *msg, long tmp); FDLINSERT( TPSVCINFO *msg ) { FBUF *rcvbuf; int i, occurrence; rcvbuf = (FBUF *)msg->data; h_empno = h_mgr = h_sal = h_comm = h_deptno = 0; memset( h_ename, 0x00, sizeof( h_ename ) ); memset( h_job, 0x00, sizeof( h_job ) ); memset( h_date, 0x00, sizeof( h_date ) ); occurrence = fbkeyoccur(rcvbuf, EMPNO); for (i=0; i< occurrence; i++){ fbget_tu ( rcvbuf, EMPNO, i, (char *)&h_empno, 0 ); fbget_tu ( rcvbuf, MGR, i, (char *)&h_mgr, 0 ); fbget_tu ( rcvbuf, SAL, i, (char *)&h_sal, 0 ); fbget_tu ( rcvbuf, COMM, i, (char *)&h_comm, 0 ); fbget_tu ( rcvbuf, DEPTNO,i, (char *)&h_deptno, 0 ); fbget_tu ( rcvbuf, ENAME, i, (char *)h_ename, 0 ); fbget_tu ( rcvbuf, JOB , i, (char *)h_job, 0 ); fbget_tu ( rcvbuf, DATE , i, (char *)h_date, 0 ); EXEC SQL INSERT INTO emp(empno, ename, job, mgr, hiredate, sal,comm, deptno) VALUES (:h_empno, :h_ename, :h_job, :h_mgr, to_date(:h_date,'yyyymmdd'), :h_sal, :h_comm, :h_deptno ); } if(sqlca.sqlcode != 0) goto LB_DBERROR; EXEC SQL WHENEVER SQLERROR goto LB_DBERROR; tpreturn(TPSUCCESS, 0L, (char *)rcvbuf, 0L, 0L); LB_DBERROR : EXEC SQL WHENEVER SQLERROR CONTINUE; svc_error(ORA, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc, 0); } FDLDELETE( TPSVCINFO *msg ) { FBUF *rcvbuf; int i, occurrence; rcvbuf = ( FBUF *)msg->data; occurrence = fbkeyoccur(rcvbuf, EMPNO); for (i=0; i< occurrence; i++){ fbget_tu (rcvbuf, EMPNO, i, (char *)&h_empno , 0); EXEC SQL DELETE FROM emp WHERE empno = :h_empno; } if(sqlca.sqlcode != 0) goto LB_DBERROR; EXEC SQL WHENEVER SQLERROR goto LB_DBERROR ; EXEC SQL WHENEVER NOT FOUND goto LB_DBERROR ; tpreturn(TPSUCCESS, 0L, (char *)rcvbuf, 0L, 0L); LB_DBERROR : EXEC SQL WHENEVER SQLERROR CONTINUE; svc_error(ORA, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc, 0) ; } FDLSELECT( TPSVCINFO *msg ) { FBUF *rcvbuf; FLDLEN fldlen; int i=0, ROWMEM=200, CHKROW=500; rcvbuf = (FBUF *)msg->data; if((rcvbuf=(FBUF *)tprealloc((char *)rcvbuf,ROWMEM*CHKROW))==NULL){ rcvbuf=(FBUF *)msg->data; goto LB_TMAXERROR ; } h_empno = h_mgr = h_sal = h_comm = h_deptno = 0; memset( h_ename, 0x00, sizeof( h_ename ) ); memset( h_job, 0x00, sizeof( h_job ) ); memset( h_date, 0x00, sizeof( h_date ) ); fbget_tu( rcvbuf, EMPNO, 0, (char *)&h_empno, &fldlen); EXEC SQL DECLARE DB_CUR CURSOR FOR SELECT nvl(empno,0), nvl(ename,' '), nvl(job,' '), nvl(to_char(hiredate,'yyyymmdd'),' '), nvl(mgr,0), nvl(sal,0), nvl(comm,0), nvl(deptno,0) FROM emp WHERE empno >= :h_empno-50 AND empno <= :h_empno+50; EXEC SQL OPEN DB_CUR; if(sqlca.sqlcode != 0) goto LB_DBERROR; EXEC SQL WHENEVER SQLERROR goto LB_DBERROR ; EXEC SQL WHENEVER NOT FOUND Do break ; while(1) { EXEC SQL FETCH DB_CUR INTO :h_empno, :h_ename, :h_job, :h_date, :h_mgr, :h_sal, :h_comm, :h_deptno; fbchg_tu(rcvbuf, EMPNO, i,(char *)&h_empno, 0); fbchg_tu(rcvbuf, MGR, i ,(char *)&h_mgr, 0); fbchg_tu(rcvbuf, SAL, i ,(char *)&h_sal, 0); fbchg_tu(rcvbuf, DEPTNO, i,(char *)&h_deptno, 0); fbchg_tu(rcvbuf, COMM, i,(char *)&h_comm, 0); fbchg_tu(rcvbuf, ENAME, i,(char *)h_ename, 0); fbchg_tu(rcvbuf, JOB, i,(char *)h_job, 0); fbchg_tu(rcvbuf, DATE, i,(char *)h_date, 0); i++; } if (i < 1) goto LB_DBERROR; EXEC SQL CLOSE DB_CUR; tpreturn(TPSUCCESS, 0L, (char *)rcvbuf, 0L, 0L); LB_DBERROR : EXEC SQL WHENEVER SQLERROR CONTINUE; EXEC SQL CLOSE DB_CUR ; svc_error(ORA, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc, 0L); LB_TMAXERROR : EXEC SQL WHENEVER SQLERROR CONTINUE; EXEC SQL CLOSE DB_CUR ; svc_error(TMX, tperrno, "", 0L) ; } FDLUPDATE( TPSVCINFO *msg ) { FBUF *rcvbuf ; int i, occurrence; rcvbuf = (FBUF *)msg->data; h_empno = h_mgr = h_sal = h_comm = h_deptno = 0; memset( h_ename, 0x00, sizeof( h_ename ) ); memset( h_job, 0x00, sizeof( h_job ) ); memset( h_date, 0x00, sizeof( h_date ) ); occurrence = fbkeyoccur(rcvbuf, EMPNO); for (i=0; i< occurrence; i++){ fbget_tu ( rcvbuf, EMPNO, i, (char *)&h_empno, 0); fbget_tu ( rcvbuf, ENAME, i, (char *)h_ename, 0); fbget_tu ( rcvbuf, JOB, i, (char *)h_job, 0); fbget_tu ( rcvbuf, MGR, i, (char *)&h_mgr, 0); fbget_tu ( rcvbuf, SAL, i, (char *)&h_sal, 0); fbget_tu ( rcvbuf, COMM, i, (char *)&h_comm,0); fbget_tu ( rcvbuf, DEPTNO, i, (char *)&h_deptno,0); fbget_tu ( rcvbuf, DATE , i, (char *)h_date, 0 ); EXEC SQL UPDATE emp SET ename = nvl(:h_ename, ename), job = nvl(:h_job, job), mgr = :h_mgr, hiredate = nvl(to_date(:h_date,'yyyymmdd'),hiredate), sal = :h_sal, comm = :h_comm, deptno = :h_deptno WHERE empno = :h_empno; if(sqlca.sqlcode != 0) goto LB_DBERROR; EXEC SQL WHENEVER SQLERROR goto LB_DBERROR; EXEC SQL WHENEVER NOT FOUND goto LB_DBERROR; } tpreturn(TPSUCCESS, 0L, (char *)rcvbuf, 0L, 0L); LB_DBERROR : EXEC SQL WHENEVER SQLERROR CONTINUE; svc_error(ORA, sqlca.sqlcode, sqlca.sqlerrm.sqlerrmc, 0L); } /********************************************************************* * 에러처리 : 서비스에서 오류가 발생하면 그 오류를 버퍼에 넣어 클라이언트로 보내준다. ********************************************************************/ void svc_error(long type, long err_code, char *msg, long tmp) { FBUF *transf; char *svcname; char err_msg[100]; char temp[100]; int i = 0; printf("type ==>[%ld]\n", type); printf("err_code ==>[%ld]\n", err_code); printf("msg ==>[%s]\n", msg); strcpy(err_msg, msg); if ((transf = (FBFR *)tpalloc("FML", NULL, 0)) == NULL) { printf("tpalloc failed! errno = %d\n", tperrno); } switch(type) { case INP: switch(err_code) { case -1000: strcpy(err_msg, "해당 사용자에게 권한이 없습니다."); break; default: strcpy(err_msg, "Input Error Message가 등록되어 있지 않습니다."); } break; case ORA: if (strlen(err_msg)== 0) strcpy(err_msg, sqlca.sqlerrm.sqlerrmc); break; case TMX: if (strlen(err_msg)== 0) strcpy(err_msg, tpstrerror(tperrno)); break; case APP: if (strlen(err_msg)== 0) { /* err_mssg=""이면 오류메시지를 setting한다. ******/ switch(err_code) { case -500: /* SYSTEM 관련 오류 */ strcpy(err_msg,"File 생성 오류입니다."); break; case -502: strcpy(err_msg,"내부 서비스를 호출하지 못했습니다."); break; case -504: strcpy(err_msg, "소켓 통신 오류입니다."); break; case -505: /* 다른 transation에서 수정되었을 경우 MSG처리*/ /* "조회 이후에 다른 곳에서 [%s]자료가 변경되었습니다. \n\n다시 조회한 후 처리하십시오.": CLIENT에서 처리*/ strcpy(err_msg, "이 없습니다."); break; case -5002: strcpy(err_msg, "전산실에 문의하십시오."); break; default: strcpy(err_msg, "Application Error Message가 등록되어 있지 않습니다."); } } break; } /* ROLLBACK */ EXEC SQL WHENEVER SQLERROR CONTINUE; EXEC SQL ROLLBACK; fbchg_tu ( transf, E_TYPE, i, (char *)&type,0); fbchg_tu ( transf, E_CODE, i, (char *)&err_code,0); fbchg_tu ( transf, E_MSG, i, (char *)err_msg,0); fbchg_tu ( transf, E_TMP, i, (char *)&tmp,0); tpreturn(TPFAIL, 0, (char *)transf, 0L, 0L); }
다음은 emp_c.pc 소스를 Tmax 애플리케이션으로 만드는 Makefile의 예제이다.
<emp_c.mk>
include $(ORACLE_HOME)/precomp/lib/env32.mk ORALIBDIR = $(LIBHOME) ORALIB = -L/home/oracle/OraHome/lib32/ -lclntsh -lld -lm `cat /home/oracle/OraHome/lib32/sysliblist` -lm -lc_r -lpthreads TARGET = emp_c APOBJS = emp_c.o NSDLOBJ = $(TMAXDIR)/lib/sdl.o #CC CC=cc #Oracle LIBS = -lsvr -loras OBJS = $(APOBJS) $(SVCTOBJ) SVCTOBJ = $(TARGET)_svctab.o CFLAGS = -q32 -O -I$(TMAXDIR) LDFLAGS = -brtl APPDIR = $(TMAXDIR)/appbin SVCTDIR = $(TMAXDIR)/svct TMAXLIBDIR = $(TMAXDIR)/lib # .SUFFIXES : .c .c.o: $(CC) $(CFLAGS) $(LDFLAGS) -c $< all: $(TARGET) $(TARGET): $(OBJS) $(CC) $(CFLAGS) $(LDFLAGS) -L$(TMAXLIBDIR) -o $(TARGET) -L$(ORALIBDIR) $(ORALIB) $(OBJS) $(LIBS) $(NSDLOBJ) mv $(TARGET) $(TMAXDIR)/appbin $(APOBJS): $(TARGET).pc proc iname=emp_c include=$(TMAXDIR) define=__LINUX_ORACLE_PROC__ $(CC) $(CFLAGS) $(LDFLAGS) -c $(TARGET).c $(SVCTOBJ): touch $(SVCTDIR)/$(TARGET)_svctab.c $(CC) $(CFLAGS) $(LDFLAGS) -c $(SVCTDIR)/$(TARGET)_svctab.c # clean: -rm -f *.o core $(TARGET) $(TARGET).lis