안녕하세요. 어제 System 명령 사용에 대한 질문을 했는데 
제가 사용했던 소스를 다시 올리겠습니다. 
현재 제가 개발한 프로그램이 20대 정도에서 운용하고 있는데, 
평균 일주일에 2, 3번 정도 프로그램이 다운되네요. 
장비 사양 및, 장비 부하등은 똑 같다고 보시면 됩니다. 
일단 제가 개발한 환경은 
- Windows 2000 Pro 
- Delphi 5 
그리고 프로그램 주요 특징 으로는 
- 120 개의 스래드가 사용이 됨 
- 스래드 멤버 변수도 있지만 전역 변수에 참조 되는 일도 있음. 
- 전역 변수는 각각의 스래드가 사용될 수 있도록 120개의 개체가 저장된 TStringList 로 구성됨. 
//----------------------------------------------------------------------------------------------------------------------// 
// 첫번째의 경우 
//----------------------------------------------------------------------------------------------------------------------// 
* Doctor Watson Log 
스레드 Id 0x708의 상태 덤프 
eax=20162112 ebx=16262190 ecx=00000035 edx=00000008 esi=00000020 edi=16262190 
eip=00401fdb esp=0642e97c ebp=005bd488 iopl=0         nv up ei pl nz na po nc 
cs=001b  ss=0023  ds=0023  es=0023  fs=003b  gs=0000             efl=00010206 
함수: <nosymbols> 
        00401fbb c1fa02           sar     edx,0x2 
        00401fbe a190d45b00       mov     eax,[005bd490]         ds:005bd490=00136dc0 
        00401fc3 8b4490f4         mov     eax,[eax+edx*4+0xf4]   ds:0086d5db=???????? 
        00401fc7 85c0             test    eax,eax 
        00401fc9 7510             jnz     0040c0db 
        00401fcb a190d45b00       mov     eax,[005bd490]         ds:005bd490=00136dc0 
        00401fd0 895c90f4         mov     [eax+edx*4+0xf4],ebx   ds:0086d5db=???????? 
        00401fd4 895b04           mov     [ebx+0x4],ebx          ds:16acf762=???????? 
        00401fd7 891b             mov     [ebx],ebx              ds:16262190=7257202e 
        00401fd9 eb3a             jmp     0040ab15 
오류 -> 00401fdb 8b10             mov     edx,[eax]              ds:20162112=???????? 
        00401fdd 894304           mov     [ebx+0x4],eax          ds:16acf762=???????? 
        00401fe0 8913             mov     [ebx],edx              ds:16262190=7257202e 
        00401fe2 8918             mov     [eax],ebx              ds:20162112=???????? 
        00401fe4 895a04           mov     [edx+0x4],ebx          ds:0086d5da=???????? 
        00401fe7 eb2c             jmp     0040a115 
        00401fe9 81fe003c0000     cmp     esi,0x3c00 
        00401fef 7c0d             jl      0040aafe 
        00401ff1 8bd6             mov     edx,esi 
        00401ff3 8bc7             mov     eax,edi 
        00401ff5 e802ffffff       call    00401efc 
        00401ffa 84c0             test    al,al 
*----> 스택 역 추적 <----* 
FramePtr ReturnAd Param#1  Param#2  Param#3  Param#4  Function Name 
005BD488 00000000 00136DC0 00137FB4 00138164 00000000 !<nosymbols> 
//----------------------------------------------------------------------------------------------------------------------// 
// 소스 및 소스 설명 
//----------------------------------------------------------------------------------------------------------------------// 
* 120개의 스래드가 운용중에 있습니다. 여기서 trdChannel 은 스래드입니다. 
* 각각의 스래드에서 사용하는 변수는 채널 변수와 전역 변수로 구성되여 있습니다. 
* 채널 변수는 
* 전역 변수는 
* 전역 변수 선언은 gPortInfoList : TStringList 로 처리 했습니다. 
* 전역 변수 초기 데이터 입력은 프로그램 시작시 다음과 같이 입력을 합니다. 
  recPortInfo : TPortInfo; 
   
  recPortInfo := TPortInfo.Create; 
   
  recPortInfo.....  <- 데이터 초기값 입력 
  recPortInfo.....  <- 데이터 초기값 입력 
  recPortInfo.....  <- 데이터 초기값 입력 
   
  gPortInfoList.AddObject(Trim(recPortInfo.PortExt), recPortInfo); 
   
* TPortInfo 구조는 다음과 같은 타입으로 구성되여 있습니다. 
     PortNo              : SmallInt 
     PortExt                : ShortString 
     PortAgentID        : ShortString 
     PortPos                : ShortString 
     PortACD                : ShortString 
     PortDB                : ShortString 
     StartFile                 : ShortString 
     EndFile             : ShortString 
     PortCount        : Integer 
     StartSno                : Integer 
     EndSno              : Integer 
     ScenarioPath         : ShortString 
     ScenarioDesc        : ShortString 
     PortMode            : String 
     TruncInfo                : String 
     AniInfo                : String 
     AniInfoDefault        : String 
     DnisData                : String 
     R2Tone                : String 
     DnisDataDefault        : String 
     ExtData                : String 
     ExtDataDefault        : String 
     CallData                : String 
     CallDataDefault         : String; 
     AcdStatus        : Integer; 
     AcdStatusDefault        : Integer; 
     AcdAgent                : Integer; 
     AcdAgentDefault        : Integer; 
     HeldCount        : Integer; 
     HeldTime                : Integer; 
     HeldServer        : Integer; 
     TransferFlag         : Integer; 
     SVFlag                 : Integer; 
     strSVReturnCode        : String 
     strSVScore         : String; 
     Call_ID01                : String   
     Call_ID02                : String 
     Call_ID03         : string; 
     ExtraData         : string; 
     ExtraODNData         : string; 
     PartyDeleted        : String 
     PartyChanged         : String; 
     ServiceStart        : String   
     ServiceStop         : String; 
     PortLock            : Boolean; 
     PortState           : Byte;   
     iSleepCnt                 : Integer; 
     bCTIDisconnect         : Boolean; 
     PortActionFlg           : Boolean; 
     PortApplyCheck          : Boolean; 
     PortMentApplyCheck : Boolean; 
     PortMentActionFlg  : Boolean; 
     PortStopCheck           : Integer; 
     bMohHangup         : Boolean; 
     vMohThread         : TMOHThread; 
     PortApplyCompleteFlag : Boolean; 
     CTIDisconnectEvent : Integer; 
     iCSNo                : Integer 
     iCSEvent                 : Integer; 
     strCSFile                : String 
     strCSHour         : String; 
     CallStartTime         : String; 
     strPlayFile         : String; 
     strDigitCount         : String; 
     strTimeout         : String; 
     strBaseScore         : String; 
     strThisDN                : String 
     strOtherDN        : String 
     strANI                 : String; 
     bEventPartyDeleted : Boolean; 
     bMOHThreadDelay         : Boolean; 
   
* 이 부분에서 처리할때 InsertFree 에러가 나오는 것 같습니다. 
// 스래드가 시작할 때 맨처음에 수행되는 모듈입니다. 
procedure trdChannel.Service_Start(bFlag : Boolean); 
var curDate: TDateTime; 
    strLog, strDesc, strResult : String; 
    iRc : Integer; 
begin 
    curDate := Now; 
     
    EnterCriticalSection(gPortCritical); 
        with TPortInfo(gPortInfoList.Objects[t_Index]) do 
        begin 
          Inc(PortCount); 
          ServiceStart := FormatDateTime('dd hh:nn:ss', curDate); 
          ServiceStop  := ''; 
           
          // CallTrace 시간 형식 변경. 
          CallStartTime := FormatDateTime('yyyymmddhhnnss',curDate); 
          // Channel Status display 
          Set_StateCount(t_Index, PortState, STATUS_ANSWER); 
          PortState := STATUS_ANSWER; 
        end; 
    LeaveCriticalSection(gPortCritical); 
     
end; 
// 화면상에 정보를 보여주기 위하여 Count 정보를 증가 및 변동하는 모듈입니다. 
procedure Set_StateCount(iIndex, iOldState, iNewState : Integer); 
var vInx : Integer; 
    curTime : String; 
begin 
    EnterCriticalSection(gGlobalCountCritical); 
    TPortInfo(gPortInfoList.Objects[iIndex]).PortState := iNewState; 
    Dec(gStatusCount[iOldState]); 
    Inc(gStatusCount[iNewState]); 
    case iNewState of 
         STATUS_ANSWER : begin 
                             // Peak Time Count 
                             curTime := FormatDateTime('hhnn', Now); 
                             // 시간대별 Call Count 누적 
                             vInx := StrToInt(FormatDateTime('hh', Now)); 
                             
                             Inc(gTimeCount[vInx]); 
                             
                             // Acd Group별 Call Count 누적 
                             vInx := gAcdGroup.IndexOf(TPortInfo(gPortInfoList.Objects[iIndex]).PortACD); 
                             if vInx > -1 then 
                                 Inc(TAcdGroupClass(gAcdGroup.Objects[vInx]).CallCount); 
                         end; 
    end; 
    LeaveCriticalSection(gGlobalCountCritical); 
end; 
//----------------------------------------------------------------------------------------------------------------------// 
// 두번째의 경우 
//----------------------------------------------------------------------------------------------------------------------// 
* 두번째의 경우는 닥터왓슨 로그는 나오지 않았지만 InsertError에 대하여 Try Except 가 발생한 부분입니다. 
  아래의 로직을 수행하면서 E.Message 내용이 나왔습니다. 해당 Address가 InsertFree 입니다. 
  (Access violation at address 00401FDB in module 'MT.exe'. Read of address 20162112) 
  그래서 Try Except 안에 있는 부분에서 잘못된 것이라고 생각합니다. 
// 사용자 정의 변수의 값을 지정한 값으로 초기화 한다. 
//    Data1 : 사용자 정의 변수 
//    Data2 : 초기값 
function FCN_Initialize(iIndex, iPort, iSnoNo, iSnoType: Integer; sSnoPath, sSnoFile: String): Integer; 
var 
    recFuncTable: TFuncTableRecord; 
    iPos1 : integer; 
    strLog, strResult, sResult, strOne1, strData1 : string; 
    lOk : Boolean; 
begin 
    // FuncTable Table에서 값을 가져온다. 
    if not Read_FuncTable(iIndex, iPort, iSnoNo, sSnoFile, recFuncTable) then 
    begin 
        Result := EVT_SQL_ERROR; 
        WriteTraceLog(ALL_LOG, iPort, iSnoNo, iSnoType, Result, 'FCN_Initialize: EVT_SQL_ERROR'); 
        exit; 
    end; 
    strLog := ''; 
    lOk := True; 
     
     
    try 
        strData1 := recFuncTable.strData1; 
        if (not IsNull(strData1)) then 
            if (strData1[Length(strData1)] <> UDV_END_DELI_COMMA) then strData1 := strData1 + UDV_END_DELI_COMMA; 
        while strData1 <> '' do 
        begin 
            iPos1 := Pos(UDV_END_DELI_COMMA, strData1); 
            if iPos1 < 1 then Break; 
            strOne1 := Copy(strData1, 1, iPos1-1); //UDV Value 
            if strOne1 = '' then Break; 
            // 전역 변수 저장함. gPortInfoList 의 iIndex에 해당하는 개체에 데이터 저장 
            Result := Set_UdvValue(iIndex, Trim(strOne1), '', 
                        'FCN_Initialize', EVT_NEXT_SCENARIO, EVT_REALCT_HANGUP); 
            if Result <> EVT_NEXT_SCENARIO then 
            begin 
                lOk := False; 
                break; 
            end; 
            Delete(strData1, 1, iPos1); 
            Sleep(1); 
        end; 
    except 
        on E: Exception do 
        begin 
            strResult := E.Message; 
            Result := EVT_REALCT_HANGUP; 
            strLog := 'FCN_Initialize: Except Error *##*-' + strResult; 
            WriteTraceLog(ALL_LOG, iPort, 0, 0, 0, strLog); 
            exit; 
        end; 
    end; 
end; 
// gPortList 는 시스템 시작시 각각의 스래드 ID 값을 저장시킵니다. 
   vPort := trdChannel.Create(); 
   gPortList.AddObject(FormatFloat('0000', iPortIndex{n-1}), vPort); 
// gUserVarList 는 시스템 시작시 각각의 스래드가 가지고 있어야할 기본 정보를 저장하는 리스트입니다. 
   recUdvClass := TUDVRecord.Create; 
   with recUdvClass do 
   begin 
       // 자신의 채널에서 쓸 UDV를 저장한다. 
       Name        := FieldByName('Name').AsString; 
       DataType := FieldByName('DataType').AsString; 
       InitData    := FieldByName('InitData').AsString; 
       Des           := FieldByName('Des').AsString; 
       gUserVarList.AddObject(UpperCase(Name), recUdvClass); 
   end; 
       
// m_UdvList 는 각각의 스래드가 가지고 있는 멤버 변수입니다. 이 값은 gUserVarList 값을 처음에 초기화 합니다. 
// 그래서 스래드가 처음에 시작할 때 초기 값을 gUserVarList 을 이용하면서 로직을 수행하다가 
// 다기 처음에 수행할 때 값을 다시 초기화 합니다. 
// 스래드가 수행중에 스래드가 가지고 있는 멤버 변수 값을 Write 하는 모듈입니다. 
function Set_UDVValue(const iIndex : Integer; const vUdv : String; const vData : String; 
                      const sFuncName : String; const iSuccess : Integer; const iFail : Integer) : Integer; 
var vInx : Integer; 
    recUdv : TUDVRecord; 
begin 
    Result := iSuccess; 
    try 
        vInx := Get_UDVIndex(iIndex, Trim(vUDV)); 
        if vInx > -1 then 
        begin 
            recUdv := TUdvRecord(trdChannel(gPortList.Objects[iIndex]).m_UdvList.Objects[vInx]) 
            recUdv.InitData := vData; 
        end else 
        begin 
          Result := iFail; 
        end; 
    except 
        Result := iFail; 
    end; 
end; 
function Get_UDVIndex(const iIndex : Integer; const vUDV : String) : Integer; 
begin 
    Result := -1; 
    if( vUDV <> '') then 
    begin 
        if vUDV[1] = UDV_START_DELI_AT then 
            Result := gUserVarList.IndexOf(Copy(vUDV, 2, Length(vUDV))); 
    end; 
end;