Q&A

  • 소스 좀 봐주세요.. TCP 서버인데 너무 잘 죽어서
내용은 이렇습니다. TCP 서버인데 클라이언트로부터 LogIn 데이터가 오면 ListView에 어디서 온 것인지 기록합니다. 그 후 작업에 필요한 데이터가 오면 그 데이터를 메모에 복사합니다. 그럼 타이머가 항상 돌고 있다가 메모가 공백이 아니면 mysql에서 데이터를 추출하여 오라클에 Insert or Update or Delete 작업을 합니다. 타이머의 Interval은 250 이고 작업을 하기전에 타이머를 Enabeld := False를 해 두고 작업을 끝나면 다시 Enabled := True를 해 둡니다.

그런데 문제는 비교적 이 서버가 잘 죽는다는데 있습니다. 계속 몇일을 살아 있어야 하는데, 흑흑...
제 소스에 문제가 많은 건가요? 고수님들의 조언을 구합니다.

<!--CodeS-->

vunit pMain;

interface

uses
  Windows....;

type
  TCommBlock = packed record
                 Command   : String[10];
                 UserID    : String[20];
                 ComName   : String[20];
                 SafemanID : String[12];
                 IDNo      : String[13];
               end;
              
  TfrmMain = class(TForm)
    suiForm: TsuiForm;
    TrayIcon1: TTrayIcon;
    PopupMenu1: TPopupMenu;
    N1: TMenuItem;
    N2: TMenuItem;
    Timer_ConnectCheck: TTimer;
    IdTCPServer: TIdTCPServer;
    TimerAdd: TTimer;
    Panel2: TPanel;
    MemoAdd: TsuiMemo;
    MemoDel: TsuiMemo;
    ListBox: TsuiListBox;
    Label2: TLabel;
    Panel1: TPanel;
    ListView: TsuiListView;
    Label3: TLabel;
    Panel3: TPanel;
    Label4: TLabel;
    Label5: TLabel;
    eIP: TsuiEdit;
    ePort: TsuiEdit;
    Panel4: TPanel;
    btnConfig: TsuiButton;
    btnStartServer: TsuiButton;
    btnStopServer: TsuiButton;
    TimerStart: TTimer;
    TimerDel: TTimer;
    procedure TrayIcon1DblClick(Sender: TObject);
    procedure N1Click(Sender: TObject);
    procedure N2Click(Sender: TObject);
    procedure Timer_ConnectCheckTimer(Sender: TObject);
    procedure IdTCPServerExecute(AThread: TIdPeerThread);
    procedure btnStopServerClick(Sender: TObject);
    procedure btnStartServerClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
    procedure IdTCPServerConnect(AThread: TIdPeerThread);
    procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
    procedure IdTCPServerDisconnect(AThread: TIdPeerThread);
    procedure IdTCPServerException(AThread: TIdPeerThread;
      AException: Exception);
    procedure IdTCPServerListenException(AThread: TIdListenerThread;
      AException: Exception);
    procedure btnConfigClick(Sender: TObject);
    procedure TimerAddTimer(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure TimerStartTimer(Sender: TObject);
    procedure TimerDelTimer(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
    procedure ActiveChk;
    function CurrConnect: Integer;
    function ConnectChk: Boolean;
    function Oracle_Start: Boolean;
    function Oracle_Stop: Boolean;
    procedure MsgAdd(S: String);
    procedure ReadIni;
    procedure FailWrite(S: String);
  end;
var
  frmMain: TfrmMain;
  pvIniFile: TiniFile;
  pvSList: TStringList;
  pvType: Integer;
  pvOraName, pvOraPass, pvOraTNS: String;
  pvFormCloseFlag, pvAddFlag, pvDelFlag,
  pvNoAdd, pvNoEdit, pvNoDelete, pvAutoStart, pvConnect: Boolean;

implementation
uses pDM, pConfig;
{$R *.DFM}
function StrCut(S: String; Count: Integer): String;
begin
  if ByteType(S, Count) = mbLeadByte Then
    Result := Copy(S, 1, Count-1)
  else
    Result := Copy(S, 1, Count);
end;
procedure TfrmMain.FailWrite(S: String);
var
  fp1: TextFile;
  f_e: String;
begin
  if not DirectoryExists(ExtractFilePath(ParamStr(0)) + 'Log') then ForceDirectories(ExtractFilePath(ParamStr(0)) + 'Log');
  f_e := ExtractFilePath(ParamStr(0)) + 'Log\' + FormatDateTime('YYYYMMDD',Now) + '.txt'; '
  AssignFile(fp1,f_e);
  if FileExists(f_e) then Append(fp1) else ReWrite(fp1);
  Writeln(fp1,S);
  CloseFile(fp1);
end;
procedure TfrmMain.MsgAdd(S: String);
var
  vMsg: String;
begin
  vMsg := '[ '+FormatDateTime('YYYY-MM-DD HH:NN:SS',Now)+' ] ' + S;
  ListBox.Items.Insert(0, vMsg);
  FailWrite(vMsg);
  if ListBox.Items.Count > 1000 then ListBox.Items.Clear;
end;
function TfrmMain.CurrConnect: Integer;
var
  List : TList;
begin
  List := IdTCPServer.Threads.LockList;
  try
    Result := List.Count;
  finally
    IdTCPServer.Threads.UnlockList;
  end;
end;
procedure TfrmMain.TrayIcon1DblClick(Sender: TObject);
begin
  Show;
end;
procedure TfrmMain.N1Click(Sender: TObject);
begin
  Show;
end;
procedure TfrmMain.N2Click(Sender: TObject);
begin
  pvFormCloseFlag := False;
  Close;
end;
procedure TfrmMain.Timer_ConnectCheckTimer(Sender: TObject);
var
  Loop, aLoop: Integer;
  IP, Handle: String;
  List : TList;
  Find: Boolean;
begin
  if ListView.Items.Count = 0 then Exit;
  if not IdTCPServer.Active then Exit;
  List := IdTCPServer.Threads.LockList;
  try
    for Loop := ListView.Items.Count - 1 downto 0 do
    begin
      Find   := False;
      IP     := ListView.Items.Item[Loop].SubItems[1];
      Handle := ListView.Items.Item[Loop].SubItems[3];
      for aLoop := 0 to List.Count -1 do
      begin
        if (IP = TIdPeerThread(List.Items[aLoop]).Connection.Socket.Binding.PeerIP) and
           (Handle = IntToStr(TIdPeerThread(List.Items[aLoop]).Connection.Socket.Binding.Handle)) then
        begin
          Find := True;
          Break;
        end;
      end;
      if not Find then ListView.Items.Item[Loop].Delete;
    end;
  finally
    IdTCPServer.Threads.UnlockList;
  end;
end;
procedure TfrmMain.IdTCPServerExecute(AThread: TIdPeerThread);
var
  // Command : String;
  FindItem, AddItem: TListItem;
var
  CommBlock: TCommBlock;
  RetrunStr: String;
begin
  if not AThread.Terminated and AThread.Connection.Connected then
  begin
    AThread.Connection.ReadBuffer(CommBlock, SizeOf (CommBlock));
    if (CommBlock.Command = 'Add') then
    begin
      MemoAdd.Lines.Add(CommBlock.SafemanID+'|'+CommBlock.IDNo);
    end else
    if (CommBlock.Command = 'Edit') then
    begin
      MemoAdd.Lines.Add(CommBlock.SafemanID+'|'+CommBlock.IDNo);
    end else
    if (CommBlock.Command = 'Del') then
    begin
      MemoDel.Lines.Add(CommBlock.SafemanID+'|'+CommBlock.IDNo);
    end else
    if (CommBlock.Command = 'LogOut') then
    begin
      FindItem := ListView.FindCaption(-1, CommBlock.UserID, False, False, False);
      if (FindItem <> nil) and (FindItem.SubItems[0] = CommBlock.ComName) then
      begin
        MsgAdd('LogOut: '+ CommBlock.UserID +'['+AThread.Connection.Socket.Binding.PeerIP+']');
        FindItem.Delete;
      end;
    end else
    if (CommBlock.Command = 'LogIn') then
    begin
      FindItem := ListView.FindCaption(-1, CommBlock.UserID, False, False, False);
      if FindItem = nil then
      begin
        MsgAdd('LogIn: '+ CommBlock.UserID +'['+AThread.Connection.Socket.Binding.PeerIP+']');
        RetrunStr := 'LogIn';
        ListView.Items.BeginUpdate;
        AddItem := ListView.Items.Add;
        AddItem.Caption := CommBlock.UserID;    // ID
        AddITem.SubItems.Add(CommBlock.ComName);       // Name
        AddITem.SubItems.Add(AThread.Connection.Socket.Binding.PeerIP); // IP
        AddITem.SubItems.Add(FormatDateTime('yyyy-mm-dd hh:nn:ss',Now)); // Time
        AddITem.SubItems.Add(IntToStr(AThread.Connection.Socket.Binding.Handle));
        ListView.Items.EndUpdate;
      end else
      begin
        if (FindItem.Caption     = CommBlock.UserID) and
           (FindItem.SubItems[0] = CommBlock.ComName) and
           (FindItem.SubItems[1] = AThread.Connection.Socket.Binding.PeerIP) then RetrunStr := 'LogIn' else
        begin
          MsgAdd('Using: '+ CommBlock.UserID +'['+AThread.Connection.Socket.Binding.PeerIP+']');
          RetrunStr := 'Using';
        end;
      end;
      CommBlock.Command   := RetrunStr;
      CommBlock.UserID    := '';
      CommBlock.SafemanID := '';
      CommBlock.IDNo      := '';
      AThread.Connection.WriteBuffer(CommBlock, SizeOf(CommBlock));
    end;
  end;
end;
procedure TfrmMain.ActiveChk;
begin
  if IdTCPServer.Active then
  begin
    suiForm.Caption := 'ResNet119 - Server [서버 시작]';
    btnStartServer.Enabled := False;
    btnStopServer.Enabled := True;
    MsgAdd('서버가 시작되었습니다.');
  end else
  begin
    suiForm.Caption := 'ResNet119 - Server [서버 중지]';
    btnStartServer.Enabled := True;
    btnStopServer.Enabled := False;
    MsgAdd('서버가 중지되었습니다.');
  end;
end;
procedure TfrmMain.btnStopServerClick(Sender: TObject);
begin
  if CurrConnect > 0 then
  begin
    Application.MessageBox(PChar('접속중인 사용자가 있어 종료할 수 없습니다.'),'알림',MB_OK or MB_ICONINFORMATION);
    Exit;
  end;
  try
    IdTCPServer.Active := False;
    // IdTCPServer.Bindings.Clear;
    ActiveChk;
  except
    on E : Exception do Application.MessageBox(PChar('서버 중지를 실패하였습니다.'+#13#10+#13#10+'오류 내용: '+E.Message),'알림',MB_OK or MB_ICONINFORMATION);
  end;
end;
procedure TfrmMain.btnStartServerClick(Sender: TObject);
{var
  Binding : TIdSocketHandle;}
begin
  try
    if IdTCPServer.Active then Exit;
    IdTCPServer.DefaultPort := StrToInt(ePort.Text);
    IdTCPServer.Active := True;
    ActiveChk;
  except
    on E : Exception do Application.MessageBox(PChar('서버 시작을 실패하였습니다.'+#13#10+#13#10+'오류 내용: '+E.Message),'알림',MB_OK or MB_ICONINFORMATION);
  end;
end;
procedure TfrmMain.ReadIni;
var
  IniFile : TIniFile;
begin
  IniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'SrvConfig.ini');
  try
    pvOraName   := frmDM.DecryptData( IniFile.ReadString('Oracle', 'UserName',  '') );
    pvOraPass   := frmDM.DecryptData( IniFile.ReadString('Oracle', 'Passwd',    '') );
    pvOraTNS    := frmDM.DecryptData( IniFile.ReadString('Oracle', 'TNS',       '') );
    pvAutoStart := IniFile.ReadBool  ('Etc',    'AutoStart', False);
    pvType      := IniFile.ReadInteger('Save', 'Type', -1);
    pvNoAdd     := IniFile.ReadBool  ('Data',    'NoAdd',    False);
    pvNoEdit    := IniFile.ReadBool  ('Data',    'NoEdit',   False);
    pvNoDelete  := IniFile.ReadBool  ('Data',    'NoDelete', False);
  finally
    IniFile.Free;
  end;
end;
function DoInstanceExist(WndTitle, NicName: string): Boolean;
var
   hSem: THandle;
   hWndMe: HWnd;
   semNm, wTtl: array[0..256] of Char;
begin
   Result := False;
   // Pascal strings 을 Char형 배열로 복사;
   StrPCopy(semNm, NicName);
   StrPCopy(wTtl, WndTitle);
   // 메모리에 Semaphore 생성
   hSem := CreateSemaphore(nil, 0, 1, semNm);
   // semaphore가 이미 존재하는지 검사
   if ((hSem <> 0) and (GetLastError() = ERROR_ALREADY_EXISTS)) then
   begin
      CloseHandle(hSem);
      // 이전 instance 검색
      hWndMe := FindWindow(nil, wTtl);
      if (hWndMe <> 0) then
      begin
         if IsIconic(hWndMe) then
            ShowWindow(hWndMe, SW_SHOWNORMAL)
         else
            SetForegroundWindow(hWndMe);
      end;
      Result := True;
   end;
end;
procedure TfrmMain.FormCreate(Sender: TObject);
begin
  if DoInstanceExist(Self.Caption, 'ResNet119_Server.exe') then
  begin
    MessageDlg(#13#10 + 'ResNet119 - Server 프로그램이 이미 실행중입니다.', mtWarning, [mbOk], 0);
    Halt(0); // 중복실행 방지를 위해...
  end;
  pvAddFlag       := False;
  pvDelFlag       := False;
  pvFormCloseFlag := False;
end;
procedure TfrmMain.IdTCPServerConnect(AThread: TIdPeerThread);
begin
  MsgAdd('Connect: '+AThread.Connection.Socket.Binding.PeerIP);
end;
procedure TfrmMain.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
  if pvFormCloseFlag then
  begin
    CanClose := False;
    Hide;
  end else
  begin
    if CurrConnect > 0 then
    begin
      CanClose := False; pvFormCloseFlag := True;
      Application.MessageBox(PChar('접속중인 사용자가 있어 종료할 수 없습니다.'),'알림',MB_OK or MB_ICONINFORMATION);
    end else
    begin
      if Application.MessageBox(PChar('프로그램을 종료하시겠습니까?'), PChar('알림'), MB_YESNO + MB_ICONQUESTION) <> IDYes then
      begin
        pvFormCloseFlag := True;
        CanClose := False
      end else
      begin
        pvSList.Free;
        btnStopServerClick(Sender);
      end;
    end;
  end;
end;
procedure TfrmMain.IdTCPServerDisconnect(AThread: TIdPeerThread);
begin
  MsgAdd('Disconnect: '+AThread.Connection.Socket.Binding.PeerIP);
end;
procedure TfrmMain.btnConfigClick(Sender: TObject);
begin
  frmConfig := TfrmConfig.Create(Self);
  try
    frmConfig.ShowModal;
  finally
    frmConfig.Free;
  end;
  ReadIni;
end;
function TfrmMain.ConnectChk: Boolean;
begin
  if frmDM.Ora_Database.Connected then
  begin
    Result := True;
    MsgAdd('오라클에 접속되었습니다.');
  end else
  begin
    Result := False;
    MsgAdd('오라클 접속이 해제되었습니다.');
  end;
end;
function TfrmMain.Oracle_Start: Boolean;
begin
  Result := False;
  if frmDM.Ora_Database.Connected then
  begin
    Result := True;
    Exit;
  end;
  if (Trim(pvOraName) <> '') and (Trim(pvOraPass) <> '') and (Trim(pvOraTNS) <> '') then
  begin
    frmDM.Ora_Database.Login    := pvOraName;
    frmDM.Ora_Database.Password := pvOraPass;
    frmDM.Ora_Database.Database := pvOraTNS;
    try
      frmDM.Ora_Database.Connect;
      Result := ConnectChk;
    except
      on E: Exception do MsgAdd('오라클 접속 시도중에 오류가 발생하였습니다. [오류 내용: '+E.Message+']');
    end;
  end else MsgAdd('오라클 접속 정보가 없이 접속을 취소하였습니다.');
end;
function TfrmMain.Oracle_Stop: Boolean;
begin
  Result := True;
  if pvAddFlag or pvDelFlag then
  begin
    MsgAdd('작업중이므로 오라클 접속을 해제할 수 없습니다.');
    Exit;
  end;
  try
    frmDM.Ora_Database.Disconnect;
    Result := ConnectChk;
  except
    on E : Exception do
    begin
      Result := True;
      MsgAdd('오라클 접속 해제를 실패하였습니다. [Err Msg: '+E.Message+']');
    end;
  end;
end;
procedure TfrmMain.TimerAddTimer(Sender: TObject);
var
  vChk: Boolean;
  vFindKey: String;
  function UpdateJob4: Boolean;
  begin
    // mysql에서 찾은 데이터를 오라클로 Update 한다.
  end;
  function InsertJob4: Boolean;
  begin
    // mysql에서 찾은 데이터를 오라클로 Insert 한다.
  end;
begin
  TimerAdd.Enabled := False;
  if Trim(MemoAdd.Text) <> '' then
  begin
    MemoAdd.Clear;
    if pvType = 4 then
    begin
      if FileAge(ExtractFilePath(Application.ExeName) + 'EtcCode5.ini') > 0 then
      begin
        frmDM.My_AddFindQuery4.Close;
        frmDM.My_AddFindQuery4.Open;
        MsgAdd('작업할 데이터 수: '+IntToStr(frmDM.My_AddFindQuery4.RecordCount));
        if frmDM.My_AddFindQuery4.RecordCount > 0 then
        begin
          if Oracle_Start then
          begin
            pvAddFlag := True;
            while not frmDM.My_AddFindQuery4.Eof do
            begin
              vChk := False;
              vFindKey := FastReplace(frmDM.My_AddFindQuery4.FieldByName('ssn').AsString,'-','');
              MsgAdd('검색 키워드: '+vFindKey);
              frmDM.Ora_AddFindQuery4.Close;
              frmDM.Ora_AddFindQuery4.ParamByName('id_no').AsString := vFindKey;
              frmDM.Ora_AddFindQuery4.Open;
              if frmDM.Ora_AddFindQuery4.RecordCount > 0 then
              begin
                frmDM.Ora_AddFindQuery4.Close;
                if not pvNoEdit then vChk := UpdateJob4 else MsgAdd('갱신 금지가 설정되어 있습니다.');
              end else
              begin
                frmDM.Ora_AddFindQuery4.Close;
                if not pvNoAdd then vChk := InsertJob4 else MsgAdd('추가 금지가 설정되어 있습니다.');
              end;
              if vChk then
              begin
                frmDM.My_ClearQuery4.Close;
                frmDM.My_ClearQuery4.ParamByName('ssn').AsString := frmDM.My_AddFindQuery4.FieldByName('ssn').AsString;
                frmDM.My_ClearQuery4.ExecSql;
                frmDM.My_ClearQuery4.Close;
              end;
              frmDM.My_AddFindQuery4.Next;
            end;
            pvAddFlag := False;
            Oracle_Stop;
          end else MsgAdd('오라클 연결에 실패하여 작업을 취소합니다.');
        end;
        frmDM.My_AddFindQuery4.Close;
      end else MsgAdd('운영에 필요한 파일(EtcCode5.ini)이 없어 작업을 취소합니다.');
    end;
  end;
  TimerAdd.Enabled := True;
end;
procedure TfrmMain.FormShow(Sender: TObject);
begin
  if not pvFormCloseFlag then
  begin
    Width := 566;
    pvFormCloseFlag := True;
    eIP.Text := GStack.LocalAddress;
    ListView.DoubleBuffered := True;
    ListBox.DoubleBuffered := True;
    pvSList := TStringList.Create;
    ReadIni;
    Case pvType of
      1: pvIniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'EtcCode2.ini');
      2: pvIniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'EtcCode3.ini');
      3: pvIniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'EtcCode4.ini');
      4: pvIniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'EtcCode5.ini');
      5: pvIniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'EtcCode6.ini');
      6: pvIniFile := TIniFile.Create(ExtractFilePath(Application.ExeName) + 'EtcCode7.ini');
    end;
    MsgAdd('운영타입: '+IntToStr(pvType));
    TimerStart.Enabled := True;
  end;
end;
procedure TfrmMain.TimerStartTimer(Sender: TObject);
begin
  TimerStart.Enabled := False;
  if pvAutoStart then
  begin
    btnStartServerClick(Sender);
    Hide;
  end;
end;
procedure TfrmMain.TimerDelTimer(Sender: TObject);
var
  vFindKey: String;
begin
  TimerDel.Enabled := False;
  if Trim(MemoDel.Text) <> '' then
  begin
    MemoDel.Clear;
    if pvType = 4 then
    begin
      if pvNoAdd then MsgAdd('삭제 금지가 설정되어 있습니다.') else
      begin
        frmDM.My_DelFindQuery4.Close;
        frmDM.My_DelFindQuery4.Open;
        MsgAdd('작업할 데이터 수: '+IntToStr(frmDM.My_DelFindQuery4.RecordCount));
        if frmDM.My_DelFindQuery4.RecordCount > 0 then
        begin
          if Oracle_Start then
          begin
            pvDelFlag := True;
            while not frmDM.My_DelFindQuery4.Eof do
            begin
              vFindKey := FastReplace(frmDM.My_AddFindQuery4.FieldByName('ssn').AsString,'-','');
              try
                frmDM.Ora_DelQuery4.Close;
                frmDM.Ora_DelQuery4.ParamByName('id_no').AsString := vFindKey;
                frmDM.Ora_DelQuery4.ExecSql;
                frmDM.Ora_DelQuery4.Close;
                frmDM.My_DelQuery4.Close;
                frmDM.My_DelQuery4.ParamByName('ssn').AsString := frmDM.My_AddFindQuery4.FieldByName('ssn').AsString;
                frmDM.My_DelQuery4.ExecSql;
                frmDM.My_DelQuery4.Close;
                MsgAdd('데이터를 삭제하였습니다. - ['+frmDM.My_DelFindQuery4.FieldByName('safeman_id').AsString+'] '+frmDM.My_DelFindQuery4.FieldByName('name').AsString);
              except
                on E: Exception do MsgAdd('데이터 삭제를 실패하였습니다. - ['+frmDM.My_DelFindQuery4.FieldByName('safeman_id').AsString+'] '+frmDM.My_DelFindQuery4.FieldByName('name').AsString+' - [오류 내용: '+E.Message+']');
              end;
              frmDM.My_DelFindQuery4.Next;
            end;
            pvDelFlag := False;
            Oracle_Stop;
          end else MsgAdd('오라클 연결에 실패하여 작업을 취소합니다.');
        end;
        frmDM.My_DelFindQuery4.Close;
      end;
    end;
  end;
  TimerDel.Enabled := True;
end;
end.
<!--CodeE-->
2  COMMENTS
  • Profile
    이정욱 2007.12.11 10:17
    아래의 소스를 보니 TCPExecute에서 리스트를 처리 하셨네요.
    이 이벤트는 쓰레드 안에 있기 때문에, 쓰레드 세이프 한 리스트를 사용하셔야 합니다.
    아니면, 커맨드 형식을 저장해 놓으시고 메세지로 보내서 다른 곳에서 그 메세지를 받아서 처리하게 해주셔야 합니다.
    동시에 리스트 한개에 Add나 Delete를 하려고 한다면 문제가 생길 수 있죠.
    즉, 쓰레드 안에서 목록을 다루시면 안됩니다..



  • Profile
    주정자 2007.12.12 10:45
    이정욱님의 답변 감사합니다.  그러나, 제가 실력이 허접인지라 혹 가능하시면 간단한 예제를 들어 주실 수 있는지???

    • 유경민
      2008.09.27 09:24
      저랑 같은 사황인듯 ㅠ.ㅠ 저도 지금 죽을맛이에요 아무것도 모른상태에서 개발할려니 ㅋㅋ
    • 한상진
    • 2007.12.12 19:28
    • 3 COMMENTS
    • /
    • 0 LIKES
    • 최용일
      2007.12.12 22:33
      안녕하세요. 윈도우에서 관련 API를 제공합니다. WNet으로 시작되는 API들인데요... WNetAddConne...
    • nilriri™
      2007.12.12 21:09
    • 한상진
      2007.12.13 01:09
      답변 올려주셔서 감사합니다... ㅎㅎ WNetAddConnection2를 써서 해결이 될듯해서 하고 있는데요;; WNetA...
    • ET
    • 2007.12.12 01:39
    • 1 COMMENTS
    • /
    • 0 LIKES
    • 깨구락지
      2007.12.12 06:56
      ASCII 코드를 참고하십시오. http://www.delphi.co.kr/ascii.html
    • 용이...
    • 2007.12.11 20:25
    • 1 COMMENTS
    • /
    • 0 LIKES
    • 구창민
      2007.12.11 20:31
      안녕하세요.. 3초짜리 타이머를 하나 만드시고 clientsocke.Active := True; Timer.Enabled := T...
    • 김승재
    • 2007.12.11 03:22
    • 1 COMMENTS
    • /
    • 0 LIKES
    • 이정욱
      2007.12.11 10:24
      인디 9 버전도 멀티 CPU에서 문제 없이 동작 합니다... 아무래도 다른 부분에서 놓치신것 같습니다.. 혹...
    • 주정자
    • 2007.12.11 01:37
    • 2 COMMENTS
    • /
    • 0 LIKES
    • 이정욱
      2007.12.11 10:17
      아래의 소스를 보니 TCPExecute에서 리스트를 처리 하셨네요. 이 이벤트는 쓰레드 안에 있기 때문에, 쓰레...
    • 주정자
      2007.12.12 10:45
      이정욱님의 답변 감사합니다.  그러나, 제가 실력이 허접인지라 혹 가능하시면 간단한 예제를 들...
    • 혀노
    • 2007.12.10 22:46
    • 1 COMMENTS
    • /
    • 0 LIKES
    • Crazy™
      2007.12.11 00:04
      group band를 한번 사용해보세요,. 음 그리고 애초에 QRCompositeReport 용도는 다중 레포트가 있는경우 ...
    • 주정자
    • 2007.12.10 06:36
    • 2 COMMENTS
    • /
    • 0 LIKES
    • 이정욱
      2007.12.11 10:21
      그냥 Active 만 True로 써주셔도 됩니다. 바인딩에서 뜻하는것은 해당 머쉰의 아이피가 여러개일때 그중...
    • 주정자
      2007.12.12 10:40
      11
    • 정해철
      2007.12.08 01:13
      http://www.delphi.co.kr/zboard/view.php?id=qanda&page=1&sn1=&divpage=13&sn=off&ss=on&sc=on&select_ar...
    • 정경철
      2007.12.08 05:25
      혹시 이것을 말하는지 모르겠습니다. 델파이에 ChartPreview(AForm, AChart) 가 있습니다. 도움말을 참고...
    • 박종철
      2007.12.10 18:12
      먼저 관심갖고 답변해 주셔서 감사합니다. 혹시나 저같은 고민을 하신 분들이 계실까봐 부족하지만 저...
    • 박종삼
    • 2007.12.07 23:46
    • 2 COMMENTS
    • /
    • 0 LIKES
    • 최용일
      2007.12.08 00:07
      PSendBuff가 아마도 레코드형일것 같구요... 그중 sdBuff가 PChar나 Pointer형일것 같네요... 위 사항이...
    • 박종삼
      2007.12.08 23:50
    • 박종철
    • 2007.12.06 23:45
    • 4 COMMENTS
    • /
    • 0 LIKES
    • Guest
      2007.12.07 01:08
      Chart1.Width := FormTest.Width / 2; <-- 이부분입니다. Chart1.Width 는 정수형인데 FormTest.Wi...
    • 박종철
      2007.12.07 01:32
      감사합니다. 알려주신 방법대로 div로 연산을 하니 일단 컴파일 할 때에는 오류메세지 없이 잘 넘어갑...
    • Crazy™
      2007.12.07 02:05
      div / 2 대신 trunc(FormTest.Width / 2) 로 해보시고 그래도 에러나시면 FormCreate대신 FormActive 이...
    • 박종철
      2007.12.07 17:59
      답변 주신 분들께 감사드립니다. FormActivate, Create, Resize 이벤트 어디에서도 계속 에러가 나서 ...
    • 정경철
    • 2007.12.06 19:42
    • 4 COMMENTS
    • /
    • 0 LIKES
    • 구창민
      2007.12.06 22:51
      안녕하세요.. http://www.delphi.co.kr/zboard/view.php?id=qanda&page=1&sn1=&divpage=13&sn=off&ss=on...
    • 정경철
      2007.12.07 08:38
      위 링크 되어 있는 소스를 테스트 해 보면 모니터2에 표시됩니다. 버튼 같은것으로 폼을 열어 을때는정상...
    • 구창민
      2007.12.07 20:26
      음.. 테스트 해볼 방법이 없어 답변드리기가 곤란한데, 아래 코드에서 모니터2에 보여질 특정폼만 Left ...
    • 정경철
      2007.12.08 05:17
      위 소스대로 했습니다. 그런데 버튼으로 했을때에는 잘 됩니다. 그런데 메인폼이 열리때 하면 메인폼과 ...
    • 모영철
      2008.01.31 01:25
      Font의 캐릭터셋을 ShiftJIS 해주시면 됩니다.
    • Guest
      2007.12.06 02:30
      Group slices 를 사용해보세요. Style : Below % Value : 5 Label : 기타 이렇게 설정하면 전체에...
    • 박종철
      2007.12.06 17:32
      감사합니다~ 델파이 TChart에서는 어쩔수 없는 부분인가 봅니다. ^^;; 알려주신 방법으로 진행하려구요...
    • 코앤
    • 2007.12.05 19:48
    • 0 COMMENTS
    • /
    • 0 LIKES
    • 정경철
    • 2007.12.05 17:22
    • 2 COMMENTS
    • /
    • 0 LIKES
    • 구창민
      2007.12.05 19:23
      안녕하세요.. Panel을  런타임에 옮기시려면 아래 코드를 Panel 의 마우스다운 이벤트에 기술...
    • 정경철
      2007.12.06 20:00
      감사 합니다..