제6장 C# .net 인터페이스

내용 목차

6.1. 개요
6.2. 예제 프로그램
6.2.1. 프로그램 구성
6.2.2. 프로그램 특징
6.2.3. 공통 프로그램
6.2.4. 클라이언트 프로그램
6.2.5. 서버 프로그램

본 장에서는 C# .net 인터페이스에서 사용하는 함수와 예제를 설명한다.

참고

유니코드를 지원하는 C# .net 인터페이스는 Visual Basic .net의 인터페이스와 유사하므로 본 장에서 설명하지 않는다. 해당 내용에 대한 자세한 내용은 “제4장 Visual Basic .net 인터페이스(유니코드)”를 참고한다.

6.1. 개요

C# .net 인터페이스에는 클라이언트 라이브러리를 호출할 수 있도록 함수의 프로토 타입을 정의한 인터페이스 모듈이 존재한다.

개발자는 다음의 인터페이스 모듈을 설치하면 편리하게 Tmax 클라이언트 라이브러리에서 제공하는 함수를 호출하여 사용할 수 있다. 함수에 대한 자세한 사용법은 “6.2.4. 클라이언트 프로그램”의 유니코드 지원 사원관리 프로그램 디자인 소스의 예제를 참조한다.

모듈설명
Atmi.csatmi 함수에 대한 프로토타입 정의 모듈이다.
Fdl.cs필드키 함수에 대한 프로토타입 정의 모듈이다.
Tx.cs트랜잭션 관련 함수에 대한 프로토타입 정의 모듈이다.
WinApi.csWindows에서 제공하는 함수를 호출하기 위한 프로토타입 정의 모듈이다.
TmaxApi.csRQ, 비요청 메시지 등의 함수에 대한 프로토타입 정의 모듈이다.

참고

atmi 함수와 필드키 함수에 대한 프로토타입 및 기능에 대한 설명은 "Tmax Reference Guide""Tmax Application Development Guide"를 참고하고, 예제 프로그램으로 C# 인터페이스 사용법의 설명을 대신한다.

6.2. 예제 프로그램

사원번호를 키값으로 이름, 직책, 담당임원, 입사일, 봉급, 계약기간과 부서를 조회, 수정, 삭제 및 입력하는 프로그램이며 사용되는 버퍼는 필드키 버퍼를 사용하였다.

6.2.1. 프로그램 구성

프로그램은 다음과 같이 구성된다.

  • 공통 프로그램

    프로그램 파일설명
    demo.f필드키 버퍼를 정의한 파일이다.
    tmconfig.mTmax 환경설정 파일이다.
  • 클라이언트 프로그램

    프로그램 파일설명
    4GL_Sample.slnForm1.cs와 Tmax Library로 이루어진 클라이언트 프로그램이다.
  • 서버 프로그램

    프로그램 파일설명
    emp_c.pc서비스 프로그램(Oracle 소스)이다.
    emp_c.mkMakefile이다.

6.2.2. 프로그램 특징

다음은 프로그램의 특징이다.

  • 클라이언트 프로그램

    기능설명
    Tmax Library 연결Atmi.cs, Fdl.cs, Tx.cs, WinApi.cs, TmaxApi.cs를 항목에 추가한다.
    Tmax 연결서비스를 수행할 때마다 접속 후, 서비스를 완료할 경우 접속을 해제한다.
    버퍼 유형FIELD KEY 버퍼, 필드키 파일을 fdlc 유틸리티로 컴파일하여 'fdl' 파일의 생성이 필요하다.
    통신 유형pb_tpcall()을 이용한 동기 통신을 사용한다.
    트랜잭션 여부조회, 수정, 삭제, 입력할 경우 모두 트랜잭션으로 처리한다.
  • 서버 프로그램

    기능설명
    서비스FDLSELECT, FDLUPDATE, FDLDELETE, FDLINSERT를 작성한다.
    데이터베이스 지정Oracle 데이터베이스를 사용한다. 시스템 구성 파일의 SVRGROUP에 데이터베이스 정보를 지정한다( XA 방식).

6.2.3. 공통 프로그램

DataBase EMP Table

다음은 데이터베이스에 생성할 EMP 테이블 파일의 예제이다.

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 환경설정

다음은 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

6.2.4. 클라이언트 프로그램

사원관리 프로그램

다음은 프로그램을 실행할 때 나타나는 초기 화면이다. 화면은 8개의 singleline editor와 1개의 데이터 윈도우, 5개의 버튼으로 구성된다.

각 버튼의 기능에 대한 설명이다.

  • 조회

    조회할 경우 '사원번호'를 입력하고 [조회] 버튼을 클릭하면, 사원번호가 ±50 범위 내에 있는 데이터가 데이터 윈도우에 출력된다.

    다음은 조회 화면이다.

  • 수정

    '사원번호'와 바꾸려는 데이터를 입력하고 [수정] 버튼을 클릭하면 입력된 데이터만 변경된다.

  • 삭제

    '사원번호'로만 삭제가 가능하다.

  • 입력

    입력할 경우 모든 데이터를 입력한다.

  • 종료

    프로그램을 종료한다.

본 절에서는 유니코드와 일반 프로그래밍의 2가지 프로그램 디자인 소스의 예제를 설명한다.

먼저, 일반 프로그래밍의 사원관리 프로그램 디자인 소스의 예제는 다음과 같다.

<Form1.cs>

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

using AtmiNS;
using WinApiNS;
using FdlNS;
using TxNS;
using TmaxApiNS;

namespace _4GL_Sample
{
public class EmployeeMgr : System.Windows.Forms.Form
{
        private Atmi atmi = new Atmi();
        private WinApi winApi = new WinApi();
        private Fdl fdl = new Fdl();
        private Tx tx = new Tx();
        private TmaxApi tmaxApi = new TmaxApi();

        bool bTxRun, bConnect;
        int nSndBuf, nRcvBuf;
        public string[] EmpFKey =
        {"EMPNO","ENAME","JOB","MGR","DATE","SAL","COMM","DEPTNO"};
        public string[] ErrFKey = {"E_TYPE","E_CODE","E_MSG","E_TMP"};
        public string[] ColumnName =
        {"사원번호","이름","직책","담당임원","입사일","봉급","COMM","부서명"};

        public const int FIELD_NUM = 8;
        public const int ErrFLD_NUM = 4;
        public enum InputCheck {  Pri_Key, All };

        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.Label label3;
        private System.Windows.Forms.Label label4;
        private System.Windows.Forms.Label label5;
        private System.Windows.Forms.Label label6;
        private System.Windows.Forms.Label label7;
        private System.Windows.Forms.Label label8;
        private System.Windows.Forms.GroupBox groupBox1;
        private System.Windows.Forms.TextBox EditEmpNo;
        private System.Windows.Forms.TextBox EditName;
        private System.Windows.Forms.TextBox EditJob;
        private System.Windows.Forms.TextBox EditMgr;
        private System.Windows.Forms.TextBox EditDate;
        private System.Windows.Forms.TextBox EditSal;
        private System.Windows.Forms.TextBox EditComm;
        private System.Windows.Forms.TextBox EditDept;
        private System.Windows.Forms.Label LabelErr;
        private System.Windows.Forms.Button BtnSel;
        private System.Windows.Forms.Button BtnUpt;
        private System.Windows.Forms.Button BtnDel;
        private System.Windows.Forms.Button BtnIns;
        private System.Windows.Forms.Button BtnExit;
        private System.Windows.Forms.Button BtnBack;
        private System.Windows.Forms.DataGrid dataGrid;

        private DataSet ResultData;
        private DataTable tEmp;
        private DataColumn cEmpNo;
        private DataColumn cEName;
        private DataColumn cJob;
        private DataColumn cMgr;
        private DataColumn cDate;
        private DataColumn cSal;
        private DataColumn cComm;
        private DataColumn cDept;
        private System.ComponentModel.Container components = null;                

        public EmployeeMgr()
        {
            bTxRun = false;
            bConnect =  false;
            nSndBuf = 0;
            nRcvBuf = 0;
            InitializeComponent();
            MakeDataSet();
        }        

        private void ErrorProcess(string ErrMsg)
        {
            LabelErr.Text = ErrMsg;
            if( bTxRun == true )
            {
                 tx.TX_ROLLBACK();
                 bTxRun = false;
            }
            if( nRcvBuf != 0 ) 
                atmi.TPFREE(ref nRcvBuf);
            if( nSndBuf != 0 )                
                atmi.TPFREE(ref nSndBuf);
                    
                    if( bConnect == true )
                    {
                        atmi.TPEND();
                        bConnect = false;
                    }
        }

        private void SuccessProcess(string SucMsg)
        {
             LabelErr.Text = SucMsg;
                    if( bTxRun == true ) {
                        tx.TX_COMMIT();
                        bTxRun = false;
                     }
                     if( nRcvBuf != 0 )                 
                         atmi.TPFREE(ref nRcvBuf);
                     if( nSndBuf != 0 )          
                         atmi.TPFREE(ref nSndBuf);
                     if( bConnect == true ) {
                         atmi.TPEND();
                         bConnect = false;
                     }
        }

        private void ClearWindow() {
             EditEmpNo.Clear();
             EditName.Clear();
             EditJob.Clear();
             EditMgr.Clear();
             EditDate.Clear();
             EditSal.Clear();
             EditComm.Clear();
             EditDept.Clear();
             tEmp.Clear();
        }

        private bool TmaxConnect(){
             tpstart_t  tpinfo = new tpstart_t();
             tpinfo.cltname = "star";
             tpinfo.usrname = "star";
             tpinfo.usrpwd  = "star";
             tpinfo.dompwd  = "star";
             tpinfo.flags   =  atmi.TPUNSOL_IGN;
                        
             nRcvBuf = atmi.TPALLOC("TPSTART", null, 0);
             if( nRcvBuf == 0 )        
                 return false;
                        
             atmi.FilltpstartBuf(ref nRcvBuf, tpinfo);
                        
             if( atmi.TPSTART(nRcvBuf) == -1 )        
                 return false;

             return true;
       }
       private bool CheckInputData(InputCheck Level) {
             if(EditEmpNo.Text.Length == 0)           
                return false;
             if(Level == InputCheck.Pri_Key )        
                return true;
             else {
                if(EditName.Text.Length == 0)  
                   return false;
                if(EditJob.Text.Length == 0)  
                   return false;
                if(EditMgr.Text.Length == 0)  
                   return false;
                if(EditDate.Text.Length != 8)  
                   return false;
                if(EditSal.Text.Length == 0)  
                   return false;
                if(EditComm.Text.Length == 0)  
                   return false;
                if(EditDept.Text.Length == 0)  
                   return false;
                return true;
              }
        }

        private bool PutMyData(InputCheck Level) {
             int nData;
             float fData;

             if(EditEmpNo.Text.Length != 0) {
                   nData = int.Parse(EditEmpNo.Text);
                   if( fdl.FBPUT(nSndBuf,fdl.FBGET_FLDKEY(EmpFKey[0]),
                                   ref nData,0)< 0)
                        return false;
              }
             if(Level == InputCheck.Pri_Key )     
                   return true;

             if(EditName.Text.Length != 0)
                   if(fdl.FBPUT(nSndBuf, fdl.FBGET_FLDKEY(EmpFKey[1]), 
                                  EditName.Text, EditName.Text.Length) < 0)
                      return false;
             if(EditJob.Text.Length != 0)
                   if(fdl.FBPUT( nSndBuf, fdl.FBGET_FLDKEY(EmpFKey[2]), 
                                      EditJob.Text, EditJob.Text.Length) < 0 )        
                      return false;
             if(EditMgr.Text.Length != 0) {
                    nData = int.Parse(EditMgr.Text);
                    if(fdl.FBPUT(nSndBuf,fdl.FBGET_FLDKEY(EmpFKey[3]),
                                 ref nData,0)< 0 )
                       return false;
             }
             if(EditDate.Text.Length == 8)
                    if(fdl.FBPUT( nSndBuf, fdl.FBGET_FLDKEY(EmpFKey[4]), 
                       EditDate.Text, EditDate.Text.Length) < 0 )        
                          return false;
             if(EditSal.Text.Length != 0){
                    fData = float.Parse(EditSal.Text);
                    if(fdl.FBPUT(nSndBuf,fdl.FBGET_FLDKEY(EmpFKey[5]),
                                  ref fData,0)< 0 )
                       return false;
             }
             if(EditComm.Text.Length != 0) {
                    fData = float.Parse(EditComm.Text);
                    if(fdl.FBPUT(nSndBuf,fdl.FBGET_FLDKEY(EmpFKey[6]),
                                 ref fData,0)< 0 )
                       return false;
             }
             if(EditDept.Text.Length != 0) {
                    nData = int.Parse(EditDept.Text);
                    if(fdl.FBPUT(nSndBuf,fdl.FBGET_FLDKEY(EmpFKey[7]),
                                 ref nData,0)< 0)
                       return false;
             }
             return true;
        }

        private void MakeDataSet() {
             ResultData = new DataSet("dataGrid");

             tEmp = new DataTable("Employee");
             cEmpNo = new DataColumn(ColumnName[0],typeof(int));;
             cEName = new DataColumn(ColumnName[1],typeof(string));
             cJob = new DataColumn(ColumnName[2],typeof(string));
             cMgr = new DataColumn(ColumnName[3],typeof(int));
             cDate = new DataColumn(ColumnName[4],typeof(string));
             cSal = new DataColumn(ColumnName[5],typeof(float));
             cComm = new DataColumn(ColumnName[6],typeof(float));
             cDept = new DataColumn(ColumnName[7],typeof(int));

             tEmp.Columns.Add(cEmpNo);
             tEmp.Columns.Add(cEName);
             tEmp.Columns.Add(cJob);
             tEmp.Columns.Add(cMgr);
             tEmp.Columns.Add(cDate);
             tEmp.Columns.Add(cSal);
             tEmp.Columns.Add(cComm);
             tEmp.Columns.Add(cDept);

             ResultData.Tables.Add(tEmp);

             //DataRow newRow1 = tEmp.NewRow();
             dataGrid.SetDataBinding(ResultData, "Employee");
        }

        private void ReceiveError() {
             int nLeng = 0;
             object[] oEData = new object[ErrFLD_NUM];

             for(int i = 0; i < ErrFLD_NUM; i++) {
                 if(fdl.FBGET(nRcvBuf, fdl.FBGET_FLDKEY(ErrFKey[i]), 
                              out oEData[i], ref nLeng) < 0 )   
                     break;
             }
             ErrorProcess(oEData[2].ToString());
                 return;
        }
        protected override void Dispose( bool disposing ) {
             if(disposing) {
                if(components != null) {
                   components.Dispose();
                }
        }
        base.Dispose( disposing );
}

#region Windows Form Designer generated code
/*디자인 설계*/
        …
#endregion

static void Main() {
       Application.Run(new EmployeeMgr());
}

private void EmployeeMgr_Load(object sender, System.EventArgs e) {
        const string EnvFile = "D:\\tmax.env";

        if(atmi.TMAXREADENV(EnvFile,"TMAX") == -1 )
                ErrorProcess("환경파일 읽기 실패. -"+ EnvFile);
}

private void BtnExit_Click(object sender, System.EventArgs e) {
         Dispose();
}

private void BtnIns_Click(object sender, System.EventArgs e) {
         int nLeng = 0;

         if(CheckInputData( InputCheck.All ) == false) {
                 ErrorProcess("입력하지 않은 빈 칸이 있습니다.\n 
                               입사일은 8자리(YYYYMMDD)로 입력해 주세요.");
                 return;
          }

          bConnect = TmaxConnect();
          if(bConnect == false )        
                  ErrorProcess("서버에 연결할 수 없습니다.");
                        
          if(bTxRun == false) {
                  if(tx.TX_BEGIN() < 0) {
                         ErrorProcess( "TX_BEGIN Error");
                         return;
                   }
                   bTxRun = true;
          }

          if(nSndBuf != 0 )    
                   atmi.TPFREE(ref nSndBuf);
          if((nSndBuf = fdl.FBALLOC( 5, 1000) ) == 0 )
                   ErrorProcess("FBALLOC - Memory 할당 에러");
          if(nRcvBuf != 0 )               
                   atmi.TPFREE(ref nRcvBuf);
          if((nRcvBuf = fdl.FBALLOC( 5, 1000) ) == 0 )
                   ErrorProcess("FBALLOC - Memory 할당 에러");
          if(PutMyData( InputCheck.All ) == false) {
                   ErrorProcess("Field Key 삽입 도중 에러가 발생했습니다.");
                   return;
           }
           if(atmi.TPCALL( "FDLINSERT", nSndBuf, 0, ref nRcvBuf, 
              ref nLeng, atmi.TPNOFLAGS) < 0 ) {
                   ReceiveError();
                   return;
           }
           SuccessProcess( EditEmpNo.Text +"번이 삽입 되었습니다.");
}

private void BtnSel_Click(object sender, System.EventArgs e) {
         int nLeng = 0;
         object[] oData = new object[FIELD_NUM];
                
         if(CheckInputData( InputCheck.Pri_Key ) == false ) {
                  ErrorProcess("사원번호를 입력해주세요.");
                  return;
         }

         bConnect = TmaxConnect();
         if(bConnect == false )
                  ErrorProcess("서버에 연결할 수 없습니다.");
                        
         if(bTxRun == false ) {
                  if(tx.TX_BEGIN() < 0 ) {
                        ErrorProcess( "TX_BEGIN Error");
                        return;
                  }
                  bTxRun = true;
         }

         if(nSndBuf != 0 )                
                  atmi.TPFREE(ref nSndBuf);
         if((nSndBuf = fdl.FBALLOC( 5, 1000) ) == 0 )
                  ErrorProcess("FBALLOC - Memory 할당 에러");
         if(nRcvBuf != 0)               
                  atmi.TPFREE(ref nRcvBuf);
         if((nRcvBuf = fdl.FBALLOC( 5, 1000)) == 0)
                  ErrorProcess("FBALLOC - Memory 할당 에러");
         if ( PutMyData( InputCheck.Pri_Key ) == false) {
                  ErrorProcess("Field Key 삽입 도중 에러가 발생했습니다.");
                  return;
         }

         if(atmi.TPCALL("FDLSELECT", nSndBuf, 0, ref nRcvBuf, 
                         ref nLeng, atmi.TPNOFLAGS) < 0 ) {
                  ReceiveError();
                  return;
         }

         int OccrN =  fdl.FBKEYOCCUR(nRcvBuf,fdl.FBGET_FLDKEY(EmpFKey[0]));
         for(int j=0; j< OccrNum; j++) {
                  DataRow OutputRow = tEmp.NewRow();
                  for(int i=0; i< FIELD_NUM; i++) {
                            if(fdl.FBGET_TU( nRcvBuf, fdl.FBGET_FLDKEY(EmpFKey[i]), 
                               j, out oData[i], ref nLeng) < 0 )    
                                       break;
                  OutputRow[ColumnName[i]] = oData[i];
                  }
                  tEmp.Rows.Add(OutputRow);
         }
                                        
         SuccessProcess("조회 내용입니다.");
         dataGrid.Visible = true;
         BtnBack.Visible = true;
         dataGrid.RowHeadersVisible = false;
}

private void BtnUpt_Click(object sender, System.EventArgs e) {                        
         int nLeng=0;
         if(CheckInputData( InputCheck.Pri_Key ) == false ) {
                   ErrorProcess("입력하지 않은 빈 칸이 있습니다.\n 
                                 입사일은 8자리(YYYYMMDD)로 입력해 주세요.");
                   return;
         }

         bConnect = TmaxConnect();
         if(bConnect == false )
                   ErrorProcess("서버에 연결할 수 없습니다.");
                        
         if( bTxRun == false ) {
                   if(tx.TX_BEGIN() < 0 ) {
                             ErrorProcess( "TX_BEGIN Error");
                             return;
                   }
                   bTxRun = true;
         }

         if(nSndBuf != 0 )         
                   atmi.TPFREE(ref nSndBuf);
         if((nSndBuf = fdl.FBALLOC( 5, 1000) ) == 0 )
                   ErrorProcess("FBALLOC - Memory 할당 에러");
         if(nRcvBuf != 0 )         
                   atmi.TPFREE(ref nRcvBuf);
         if((nRcvBuf = fdl.FBALLOC( 5, 1000) ) == 0 )
                   ErrorProcess("FBALLOC - Memory 할당 에러");
                        
         if(PutMyData( InputCheck.All ) == false) {
                   ErrorProcess("Field Key 삽입 도중 에러가 발생했습니다.");
                   return;
         }

         if(atmi.TPCALL("FDLUPDATE", nSndBuf, 0, ref nRcvBuf, 
                        ref nLeng, atmi.TPNOFLAGS) < 0 ) {
                   ReceiveError();
                   return;
         }
                        
         SuccessProcess(EditEmpNo.Text + "번이 수정 되었습니다.");
                        ClearWindow();
}

private void BtnDel_Click(object sender, System.EventArgs e) {
         int nLeng = 0;

         if( CheckInputData( InputCheck.Pri_Key ) == false ) {
                    ErrorProcess("사원번호를 입력해주세요.");
                    return;
         }

         bConnect = TmaxConnect();
         if( bConnect == false )
                    ErrorProcess("서버에 연결할 수 없습니다.");
                        
         if( bTxRun == false ) {
                    if( tx.TX_BEGIN() < 0 ) {
                             ErrorProcess( "TX_BEGIN Error");
                             return;
                    }
                    bTxRun = true;
         }
         if( nSndBuf != 0 )         
                    atmi.TPFREE(ref nSndBuf);
         if( ( nSndBuf = fdl.FBALLOC( 5, 1000) ) == 0 )
                    ErrorProcess("FBALLOC - Memory 할당 에러");
         if( nRcvBuf != 0 )         
                    atmi.TPFREE(ref nRcvBuf);
         if( ( nRcvBuf = fdl.FBALLOC( 5, 1000) ) == 0 )
                    ErrorProcess("FBALLOC - Memory 할당 에러");
                        
         if( PutMyData( InputCheck.Pri_Key ) == false ) {
                    ErrorProcess("Field Key 삽입 도중 에러가 발생했습니다.");
                    return;
         }

         if(atmi.TPCALL("FDLDELETE", nSndBuf, 0, ref nRcvBuf, 
            ref nLeng, atmi.TPNOFLAGS) < 0) {
                    ReceiveError();
                    return;
         }
                        
         SuccessProcess( EditEmpNo.Text + "번이 삭제되었습니다.");
         ClearWindow();
                
}

private void BtnBack_Click(object sender, System.EventArgs e) {
         dataGrid.Visible = false;
         BtnBack.Visible = false;
         ClearWindow();
}
}

다음은 UTF8로 설정한 유니코드 지원 사원관리 프로그램 디자인 소스의 예제이다.

일반적으로 Atmi.SetServerEncoding(Encoding.UTF8)만 설정해도 무방하지만, 클라이언트 측에서도 별도의 인코딩 방식을 사용 중이라면 SetClientEncoding()을 통해 설정한다. 또한, 64비트 지원을 위해 nSndBuf, nRcvBuf가 int에서 IntPtr로 변경되었다.

<Form1.cs>

using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Data;

using AtmiNS;
using WinApiNS;
using FdlNS;
using TxNS;
using TmaxApiNS;

namespace _4GL_Sample
{
public class EmployeeMgr : System.Windows.Forms.Form
{
        Atmi.SetServerEncoding(Encoding.UTF8);
        private Atmi atmi = new Atmi();
        private WinApi winApi = new WinApi();
        private Fdl fdl = new Fdl();
        private Tx tx = new Tx();
        private TmaxApi tmaxApi = new TmaxApi();

        bool bTxRun, bConnect;
        IntPtr nSndBuf, nRcvBuf;
        public string[] EmpFKey =
        {"EMPNO","ENAME","JOB","MGR","DATE","SAL","COMM","DEPTNO"};
        public string[] ErrFKey = {"E_TYPE","E_CODE","E_MSG","E_TMP"};
        public string[] ColumnName =
        {"사원번호","이름","직책","담당임원","입사일","봉급","COMM","부서명"};

        public const int FIELD_NUM = 8;
        public const int ErrFLD_NUM = 4;
        public enum InputCheck {  Pri_Key, All };

        private System.Windows.Forms.Label label1;
        private System.Windows.Forms.Label label2;
        private System.Windows.Forms.Label label3;
        private System.Windows.Forms.Label label4;
        private System.Windows.Forms.Label label5;
        private System.Windows.Forms.Label label6;
        private System.Windows.Forms.Label label7;
        private System.Windows.Forms.Label label8;
        private System.Windows.Forms.GroupBox groupBox1;
        private System.Windows.Forms.TextBox EditEmpNo;
        private System.Windows.Forms.TextBox EditName;
        private System.Windows.Forms.TextBox EditJob;
        private System.Windows.Forms.TextBox EditMgr;
        private System.Windows.Forms.TextBox EditDate;
        private System.Windows.Forms.TextBox EditSal;
        private System.Windows.Forms.TextBox EditComm;
        private System.Windows.Forms.TextBox EditDept;
        private System.Windows.Forms.Label LabelErr;
        private System.Windows.Forms.Button BtnSel;
        private System.Windows.Forms.Button BtnUpt;
        private System.Windows.Forms.Button BtnDel;
        private System.Windows.Forms.Button BtnIns;
        private System.Windows.Forms.Button BtnExit;
        private System.Windows.Forms.Button BtnBack;
        private System.Windows.Forms.DataGrid dataGrid;

        private DataSet ResultData;
        private DataTable tEmp;
        private DataColumn cEmpNo;
        private DataColumn cEName;
        private DataColumn cJob;
        private DataColumn cMgr;
        private DataColumn cDate;
        private DataColumn cSal;
        private DataColumn cComm;
        private DataColumn cDept;
        private System.ComponentModel.Container components = null;                

        public EmployeeMgr()
        {
            bTxRun = false;
            bConnect =  false;
            nSndBuf = IntPtr.Zero;
            nRcvBuf = IntPtr.Zero;
            InitializeComponent();
            MakeDataSet();
        }        

        private void ErrorProcess(string ErrMsg)
        {
            LabelErr.Text = ErrMsg;
            if( bTxRun == true )
            {
                 tx.TX_ROLLBACK();
                 bTxRun = false;
            }
            if( nRcvBuf != IntPtr.Zero ) 
                atmi.TPFREE(ref nRcvBuf);
            if( nSndBuf != IntPtr.Zero )                
                atmi.TPFREE(ref nSndBuf);
                    
                    if( bConnect == true )
                    {
                        atmi.TPEND();
                        bConnect = false;
                    }
        }

        private void SuccessProcess(string SucMsg)
        {
             LabelErr.Text = SucMsg;
                    if( bTxRun == true ) {
                        tx.TX_COMMIT();
                        bTxRun = false;
                     }
                     if( nRcvBuf != IntPtr.Zero )                 
                         atmi.TPFREE(ref nRcvBuf);
                     if( nSndBuf != IntPtr.Zero )          
                         atmi.TPFREE(ref nSndBuf);
                     if( bConnect == true ) {
                         atmi.TPEND();
                         bConnect = false;
                     }
        }

        private void ClearWindow() {
             EditEmpNo.Clear();
             EditName.Clear();
             EditJob.Clear();
             EditMgr.Clear();
             EditDate.Clear();
             EditSal.Clear();
             EditComm.Clear();
             EditDept.Clear();
             tEmp.Clear();
        }

        private bool TmaxConnect(){
             tpstart_t  tpinfo = new tpstart_t();
             tpinfo.cltname = "star";
             tpinfo.usrname = "star";
             tpinfo.usrpwd  = "star";
             tpinfo.dompwd  = "star";
             tpinfo.flags   =  atmi.TPUNSOL_IGN;
                        
             nRcvBuf = atmi.TPALLOC("TPSTART", null, 0);
             if( nRcvBuf == IntPtr.Zero)        
                 return false;
                        
             atmi.FilltpstartBuf(ref nRcvBuf, tpinfo);
                        
             if( atmi.TPSTART(nRcvBuf) == -1 )        
                 return false;

             return true;
       }
       private bool CheckInputData(InputCheck Level) {
             if(EditEmpNo.Text.Length == 0)           
                return false;
             if(Level == InputCheck.Pri_Key )        
                return true;
             else {
                if(EditName.Text.Length == 0)  
                   return false;
                if(EditJob.Text.Length == 0)  
                   return false;
                if(EditMgr.Text.Length == 0)  
                   return false;
                if(EditDate.Text.Length != 8)  
                   return false;
                if(EditSal.Text.Length == 0)  
                   return false;
                if(EditComm.Text.Length == 0)  
                   return false;
                if(EditDept.Text.Length == 0)  
                   return false;
                return true;
              }
        }

        private bool PutMyData(InputCheck Level) {
             int nData;
             float fData;

             if(EditEmpNo.Text.Length != 0) {
                   nData = int.Parse(EditEmpNo.Text);
                   if( fdl.FBPUT(nSndBuf,fdl.FBGET_FLDKEY(EmpFKey[0]),
                                   ref nData,0)< 0)
                        return false;
              }
             if(Level == InputCheck.Pri_Key )     
                   return true;

             if(EditName.Text.Length != 0)
                   if(fdl.FBPUT(nSndBuf, fdl.FBGET_FLDKEY(EmpFKey[1]), 
                                  EditName.Text, EditName.Text.Length) < 0)
                      return false;
             if(EditJob.Text.Length != 0)
                   if(fdl.FBPUT( nSndBuf, fdl.FBGET_FLDKEY(EmpFKey[2]), 
                                      EditJob.Text, EditJob.Text.Length) < 0 )        
                      return false;
             if(EditMgr.Text.Length != 0) {
                    nData = int.Parse(EditMgr.Text);
                    if(fdl.FBPUT(nSndBuf,fdl.FBGET_FLDKEY(EmpFKey[3]),
                                 ref nData,0)< 0 )
                       return false;
             }
             if(EditDate.Text.Length == 8)
                    if(fdl.FBPUT( nSndBuf, fdl.FBGET_FLDKEY(EmpFKey[4]), 
                       EditDate.Text, EditDate.Text.Length) < 0 )        
                          return false;
             if(EditSal.Text.Length != 0){
                    fData = float.Parse(EditSal.Text);
                    if(fdl.FBPUT(nSndBuf,fdl.FBGET_FLDKEY(EmpFKey[5]),
                                  ref fData,0)< 0 )
                       return false;
             }
             if(EditComm.Text.Length != 0) {
                    fData = float.Parse(EditComm.Text);
                    if(fdl.FBPUT(nSndBuf,fdl.FBGET_FLDKEY(EmpFKey[6]),
                                 ref fData,0)< 0 )
                       return false;
             }
             if(EditDept.Text.Length != 0) {
                    nData = int.Parse(EditDept.Text);
                    if(fdl.FBPUT(nSndBuf,fdl.FBGET_FLDKEY(EmpFKey[7]),
                                 ref nData,0)< 0)
                       return false;
             }
             return true;
        }

        private void MakeDataSet() {
             ResultData = new DataSet("dataGrid");

             tEmp = new DataTable("Employee");
             cEmpNo = new DataColumn(ColumnName[0],typeof(int));;
             cEName = new DataColumn(ColumnName[1],typeof(string));
             cJob = new DataColumn(ColumnName[2],typeof(string));
             cMgr = new DataColumn(ColumnName[3],typeof(int));
             cDate = new DataColumn(ColumnName[4],typeof(string));
             cSal = new DataColumn(ColumnName[5],typeof(float));
             cComm = new DataColumn(ColumnName[6],typeof(float));
             cDept = new DataColumn(ColumnName[7],typeof(int));

             tEmp.Columns.Add(cEmpNo);
             tEmp.Columns.Add(cEName);
             tEmp.Columns.Add(cJob);
             tEmp.Columns.Add(cMgr);
             tEmp.Columns.Add(cDate);
             tEmp.Columns.Add(cSal);
             tEmp.Columns.Add(cComm);
             tEmp.Columns.Add(cDept);

             ResultData.Tables.Add(tEmp);

             //DataRow newRow1 = tEmp.NewRow();
             dataGrid.SetDataBinding(ResultData, "Employee");
        }

        private void ReceiveError() {
             int nLeng = 0;
             object[] oEData = new object[ErrFLD_NUM];

             for(int i = 0; i < ErrFLD_NUM; i++) {
                 if(fdl.FBGET(nRcvBuf, fdl.FBGET_FLDKEY(ErrFKey[i]), 
                              out oEData[i], ref nLeng) < 0 )   
                     break;
             }
             ErrorProcess(oEData[2].ToString());
                 return;
        }
        protected override void Dispose( bool disposing ) {
             if(disposing) {
                if(components != null) {
                   components.Dispose();
                }
        }
        base.Dispose( disposing );
}

#region Windows Form Designer generated code
/*디자인 설계*/
        …
#endregion

static void Main() {
       Application.Run(new EmployeeMgr());
}

private void EmployeeMgr_Load(object sender, System.EventArgs e) {
        const string EnvFile = "D:\\tmax.env";

        if(atmi.TMAXREADENV(EnvFile,"TMAX") == -1 )
                ErrorProcess("환경 파일 읽기 실패. -"+ EnvFile);
}

private void BtnExit_Click(object sender, System.EventArgs e) {
         Dispose();
}

private void BtnIns_Click(object sender, System.EventArgs e) {
         int nLeng = 0;

         if(CheckInputData( InputCheck.All ) == false) {
                 ErrorProcess("입력하지 않은 빈 칸이 있습니다.\n 
                               입사일은 8자리(YYYYMMDD)로 입력해 주세요.");
                 return;
          }

          bConnect = TmaxConnect();
          if(bConnect == false )        
                  ErrorProcess("서버에 연결할 수 없습니다.");
                        
          if(bTxRun == false) {
                  if(tx.TX_BEGIN() < 0) {
                         ErrorProcess( "TX_BEGIN Error");
                         return;
                   }
                   bTxRun = true;
          }

          if(nSndBuf != IntPtr.Zero )    
                   atmi.TPFREE(ref nSndBuf);
          if((nSndBuf = fdl.FBALLOC( 5, 1000) ) == IntPtr.Zero )
                   ErrorProcess("FBALLOC - Memory 할당 에러");
          if(nRcvBuf != IntPtr.Zero )               
                   atmi.TPFREE(ref nRcvBuf);
          if((nRcvBuf = fdl.FBALLOC( 5, 1000) ) == IntPtr.Zero )
                   ErrorProcess("FBALLOC - Memory 할당 에러");
          if(PutMyData( InputCheck.All ) == false) {
                   ErrorProcess("Field Key 삽입 도중 에러가 발생했습니다.");
                   return;
           }
           if(atmi.TPCALL( "FDLINSERT", nSndBuf, 0, ref nRcvBuf, 
              ref nLeng, atmi.TPNOFLAGS) < 0 ) {
                   ReceiveError();
                   return;
           }
           SuccessProcess( EditEmpNo.Text +"번이 삽입 되었습니다.");
}

private void BtnSel_Click(object sender, System.EventArgs e) {
         int nLeng = 0;
         object[] oData = new object[FIELD_NUM];
                
         if(CheckInputData( InputCheck.Pri_Key ) == false ) {
                  ErrorProcess("사원번호를 입력해주세요.");
                  return;
         }

         bConnect = TmaxConnect();
         if(bConnect == false )
                  ErrorProcess("서버에 연결할 수 없습니다.");
                        
         if(bTxRun == false ) {
                  if(tx.TX_BEGIN() < 0 ) {
                        ErrorProcess( "TX_BEGIN Error");
                        return;
                  }
                  bTxRun = true;
         }

         if(nSndBuf != IntPtr.Zero )                
                  atmi.TPFREE(ref nSndBuf);
         if((nSndBuf = fdl.FBALLOC( 5, 1000) ) == IntPtr.Zero )
                  ErrorProcess("FBALLOC - Memory 할당 에러");
         if(nRcvBuf != IntPtr.Zero)               
                  atmi.TPFREE(ref nRcvBuf);
         if((nRcvBuf = fdl.FBALLOC( 5, 1000)) == IntPtr.Zero)
                  ErrorProcess("FBALLOC - Memory 할당 에러");
         if ( PutMyData( InputCheck.Pri_Key ) == false) {
                  ErrorProcess("Field Key 삽입 도중 에러가 발생했습니다.");
                  return;
         }

         if(atmi.TPCALL("FDLSELECT", nSndBuf, 0, ref nRcvBuf, 
                         ref nLeng, atmi.TPNOFLAGS) < 0 ) {
                  ReceiveError();
                  return;
         }

         int OccrN =  fdl.FBKEYOCCUR(nRcvBuf,fdl.FBGET_FLDKEY(EmpFKey[0]));
         for(int j=0; j< OccrNum; j++) {
                  DataRow OutputRow = tEmp.NewRow();
                  for(int i=0; i< FIELD_NUM; i++) {
                            if(fdl.FBGET_TU( nRcvBuf, fdl.FBGET_FLDKEY(EmpFKey[i]), 
                               j, out oData[i], ref nLeng) < 0 )    
                                       break;
                  OutputRow[ColumnName[i]] = oData[i];
                  }
                  tEmp.Rows.Add(OutputRow);
         }
                                        
         SuccessProcess("조회 내용입니다.");
         dataGrid.Visible = true;
         BtnBack.Visible = true;
         dataGrid.RowHeadersVisible = false;
}

private void BtnUpt_Click(object sender, System.EventArgs e) {                        
         int nLeng=0;
         if(CheckInputData( InputCheck.Pri_Key ) == false ) {
                   ErrorProcess("입력하지 않은 빈 칸이 있습니다.\n 
                                 입사일은 8자리(YYYYMMDD)로 입력해 주세요.");
                   return;
         }

         bConnect = TmaxConnect();
         if(bConnect == false )
                   ErrorProcess("서버에 연결할 수 없습니다.");
                        
         if( bTxRun == false ) {
                   if(tx.TX_BEGIN() < 0 ) {
                             ErrorProcess( "TX_BEGIN Error");
                             return;
                   }
                   bTxRun = true;
         }

         if(nSndBuf != IntPtr.Zero )         
                   atmi.TPFREE(ref nSndBuf);
         if((nSndBuf = fdl.FBALLOC( 5, 1000) ) == IntPtr.Zero )
                   ErrorProcess("FBALLOC - Memory 할당 에러");
         if(nRcvBuf != IntPtr.Zero )         
                   atmi.TPFREE(ref nRcvBuf);
         if((nRcvBuf = fdl.FBALLOC( 5, 1000) ) == IntPtr.Zero )
                   ErrorProcess("FBALLOC - Memory 할당 에러");
                        
         if(PutMyData( InputCheck.All ) == false) {
                   ErrorProcess("Field Key 삽입 도중 에러가 발생했습니다.");
                   return;
         }

         if(atmi.TPCALL("FDLUPDATE", nSndBuf, 0, ref nRcvBuf, 
                        ref nLeng, atmi.TPNOFLAGS) < 0 ) {
                   ReceiveError();
                   return;
         }
                        
         SuccessProcess(EditEmpNo.Text + "번이 수정 되었습니다.");
                        ClearWindow();
}

private void BtnDel_Click(object sender, System.EventArgs e) {
         int nLeng = 0;

         if( CheckInputData( InputCheck.Pri_Key ) == false ) {
                    ErrorProcess("사원번호를 입력해주세요.");
                    return;
         }

         bConnect = TmaxConnect();
         if( bConnect == false )
                    ErrorProcess("서버에 연결할 수 없습니다.");
                        
         if( bTxRun == false ) {
                    if( tx.TX_BEGIN() < 0 ) {
                             ErrorProcess( "TX_BEGIN Error");
                             return;
                    }
                    bTxRun = true;
         }
         if( nSndBuf != IntPtr.Zero )         
                    atmi.TPFREE(ref nSndBuf);
         if( ( nSndBuf = fdl.FBALLOC( 5, 1000) ) == IntPtr.Zero )
                    ErrorProcess("FBALLOC - Memory 할당 에러");
         if( nRcvBuf != IntPtr.Zero )         
                    atmi.TPFREE(ref nRcvBuf);
         if( ( nRcvBuf = fdl.FBALLOC( 5, 1000) ) == IntPtr.Zero )
                    ErrorProcess("FBALLOC - Memory 할당 에러");
                        
         if( PutMyData( InputCheck.Pri_Key ) == false ) {
                    ErrorProcess("Field Key 삽입 도중 에러가 발생했습니다.");
                    return;
         }

         if(atmi.TPCALL("FDLDELETE", nSndBuf, 0, ref nRcvBuf, 
            ref nLeng, atmi.TPNOFLAGS) < 0) {
                    ReceiveError();
                    return;
         }
                        
         SuccessProcess( EditEmpNo.Text + "번이 삭제되었습니다.");
         ClearWindow();
                
}

private void BtnBack_Click(object sender, System.EventArgs e) {
         dataGrid.Visible = false;
         BtnBack.Visible = false;
         ClearWindow();
}
}

6.2.5. 서버 프로그램

서비스 프로그램

다음은 서비스 프로그램의 예제이다.

<emp_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);
}

Makefile

다음은 <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