메뉴 바로가기 검색 및 카테고리 바로가기 본문 바로가기

한빛출판네트워크

한빛랩스 - 지식에 가능성을 머지하다 / 강의 콘텐츠 무료로 수강하시고 피드백을 남겨주세요. ▶︎

IT/모바일

C# 쓰레드 이야기: 7. C#으로 만드는 WinTop

한빛미디어

|

2001-12-11

|

by HANBIT

27,016

지금까지 쓰레드를 이용하는 방법에 대해서 알아보았고, 예외 처리에 대해서 짤막하게 알아보았다. 이번에는 이러한 지식들을 정리하고 한데 모아보는 의미에서 프로세스 모니터를 만들어 볼 것이다. NT에서의 작업 관리자나 Linux에서의 top과 같은 유틸리티를 사용하여 시스템에서 실행중인 프로세스의 상태를 일목요연하게 알아볼 수 있었다. 그러나 프로세스 밑에서 실행중인 쓰레드의 정보를 얻는 것은 작업 관리자로는 어렵다. ^^; WinTop을 작성하는 방법은 메모장과 같은 텍스트 에디터를 사용하는 방법과 VS.NET과 같은 개발환경을 사용할 수 있다. 어느쪽을 사용해도 되며, 여기서는 메모장만 이용한다. 여기서 작성할 프로그램의 실행 화면은 다음과 같다.

그리고 각각의 프로세스를 클릭했을 때 쓰레드에 대한 자세한 정보를 볼 수 있으며 결과는 다음과 같다.

이 화면은 MS 워드의 프로세스에 대한 정보를 선택한 것이다. 먼저 첫번째 화면부터 작성해보도록 하자.
이름: WinTop.cs

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

namespace WinTop
{
  public class MyForm : System.Windows.Forms.Form
  {
    public static void Main()
    {
      Application.Run(new MyForm());
    }
각각의 필요한 클래스들을 선언한다. 윈도우 폼을 사용하기 위해 System.Windows.Forms 네임 스페이스를 사용하며, 폼에 각각의 컨트롤을 그리고 위치시키기 위해 System.Drawing 네임 스페이스를 사용한다. Timer와 컨트롤의 자원 해제 등을 위해서 System.ComponentModel 네임 스페이스를 사용한다. 마지막으로 프로세스와 쓰레드 컬렉션을 다루기 위해 System.Collections 네임 스페이스를 사용하고, 시스템의 프로세스와 쓰레드 정보를 얻어내기 위해 System.Diagnostics 네임 스페이스를 사용한다.
    #region Control Declaration
    private System.Windows.Forms.ListView lstProcess;
  
    private System.Windows.Forms.Label lblTotal;
    private System.Windows.Forms.Label lblProcessTotal;
    private System.Windows.Forms.Label lblThreadTotal;

    private System.Windows.Forms.ColumnHeader colHeader1;
    private System.Windows.Forms.ColumnHeader colHeader2;
    private System.Windows.Forms.ColumnHeader colHeader3;
    private System.Windows.Forms.ColumnHeader colHeader4;
    private System.Windows.Forms.ColumnHeader colHeader5;
    private System.Windows.Forms.ColumnHeader colHeader6;

    private System.Windows.Forms.CheckBox chkRefresh;
    private System.ComponentModel.IContainer components;
    private System.Windows.Forms.Panel panTotal;

    private Timer timer;
    public Process[] procs;
    public int m_processCount;    // 총 프로세스 수
    public int m_threadCount;     // 총 쓰레드 수
    #endregion
폼에서 사용할 각각의 컨트롤을 private으로 선언하고, 몇 가지를 전역변수로 사용하기 위해 public으로 선언한다. 각각의 선언은 다음과 같이 전체 이름을 사용했다.
private System.Windows.Forms.ListView lstProcess;
간단히 하려면 다음과 같이 짧게 사용할 수 있다.
private ListView lstProcess;
여기서 주목할 부분은 IContainer 인터페이스를 사용한 components이다. IContainer 인터페이스는 컨트롤에 대한 컨테이너 역할을 한다. ISite, IComponent 인터페이스의 구현이 IContainer에 포함되어 있다. #region과 #endregion이라는 지시자를 사용하여 VS.NET에서 코드를 숨기도록 할 수도 있다. 다음은 MyFrom 클래스에 대한 생성자를 정의하는 부분이다. 여기서는 앞에서 선언한 각 윈도우 폼 컨트롤들의 인스턴스를 생성하고, 적절한 초기화를 한다.
    #region form constructor
    public MyForm()
    {

      // ComponentModel 지원
      this.components = new System.ComponentModel.Container();

      //timer 초기화
      this.timer = new System.Windows.Forms.Timer(this.components);
      timer.Enabled = true;
      timer.Interval = 1000;
      timer.Tick += new System.EventHandler(this.timer_Tick);
components 컨테이너에 timer를 담아두며, 타이머를 설정한다. 타이머 이벤트가 발생하는 간격은 1000ms(1초)로 설정하며, 이벤트 핸들러를 선언하여 이벤트가 발생할 때 실행할 메소드를 위임한다.
      // form Init.
      this.Text = "WinTop";

      // lstProcess ListView Init.
      this.lstProcess = new System.Windows.Forms.ListView();
      lstProcess.Name = "lstProcess";
      lstProcess.Size = new System.Drawing.Size(550, 500);
      lstProcess.HeaderStyle = ColumnHeaderStyle.Clickable;
      lstProcess.View = View.Details;
      lstProcess.FullRowSelect = true;
      lstProcess.Sorting = SortOrder.Ascending;
다음은 폼의 제목을 WinTop으로 설정하며, 프로세스 목록을 보여줄 ListView 클래스를 적절하게 선언하는 부분이다. ListView는 탐색기에서 각각의 항목을 보여주는 데 사용하는 클래스이다. 주의할 것은 우리가 원하는 모습의 ListView를 사용하려면 View를 View.Details로 선언해야 한다. FullRowSelect는 개별적으로 열을 선택하지 않고 전체 열을 하나로 하여 선택하도록 하는 부분이다. 다음은 프로세스의 이름을 오름차순으로 정렬하기 위해 Sorting을 사용하였다.
      // lstProcess ColumnHeader Init.
      this.colHeader1 = new System.Windows.Forms.ColumnHeader();
      this.colHeader2 = new System.Windows.Forms.ColumnHeader();
      this.colHeader3 = new System.Windows.Forms.ColumnHeader();
      this.colHeader4 = new System.Windows.Forms.ColumnHeader();
      this.colHeader5 = new System.Windows.Forms.ColumnHeader();
      this.colHeader6 = new System.Windows.Forms.ColumnHeader();

      colHeader1.Text = "Process";
      colHeader1.Width = 70;
      colHeader1.TextAlign = HorizontalAlignment.Left;

      colHeader2.Text = "Process ID";
      colHeader2.Width = 70;
      colHeader2.TextAlign = HorizontalAlignment.Left;
    
      colHeader3.Text = "Priority";
      colHeader3.Width = 70;
      colHeader3.TextAlign = HorizontalAlignment.Right;

      colHeader4.Text = "Physical Mem(KB)";
      colHeader4.Width = 120;
      colHeader4.TextAlign = HorizontalAlignment.Right;

      colHeader5.Text = "Virtual Mem(KB)";
      colHeader5.Width = 120;
      colHeader5.TextAlign = HorizontalAlignment.Right;

      colHeader6.Text = "Start Time";
      colHeader6.Width = 150;
      colHeader6.TextAlign = HorizontalAlignment.Left;

      lstProcess.Columns.AddRange( new System.Windows.Forms.ColumnHeader[] {
                                                                             colHeader1, 
                                                                             colHeader2,
                                                                             colHeader3,
                                                                             colHeader4,
                                                                             colHeader5,
                                                                             colHeader6
                                                                           });

      lstProcess.Click += new System.EventHandler(lstProcess_OnClick);
이 부분은 ListView에 추가할 각각의 컬럼을 추가하는 부분이다. 하나의 컬럼은 ColumnHeader 타입으로 정의해야 한다. 마지막에는 ListView에서 프로세스를 클릭하면 해당 프로세스에 대한 쓰레드 정보를 보여주기 위해 이벤트를 할당한 부분이다.
      this.lblTotal = new System.Windows.Forms.Label();
      lblTotal.Text = "Total";
      lblTotal.Size = new System.Drawing.Size(35, 10);
      lblTotal.Location = new System.Drawing.Point(557, 5);

      this.lblThreadTotal = new System.Windows.Forms.Label();
      lblThreadTotal.Text = @"Thread: ";
      lblThreadTotal.Size = new System.Drawing.Size(100, 10);
      lblThreadTotal.Location = new System.Drawing.Point(5, 20);
    
      this.lblProcessTotal = new System.Windows.Forms.Label();
      lblProcessTotal.Text = @"Process: ";
      lblProcessTotal.Size = new System.Drawing.Size(100, 10);
      lblProcessTotal.Location = new System.Drawing.Point(5, 40);
각각의 Label을 설정하고 위치와 크기를 정하는 부분이다. 그 외 Text속성을 정의할 때 @를 사용하였다.
  lblProcessTotal.Text = @"Process: ";
@는 형식문자 등을 해석하지 않고 문자 그대로 출력하는 것을 뜻한다. ":"과 같은 특수문자는 실제로 출력되지 않지만, @을 사용하여 출력하도록 하였다. 물론 "₩:"와 같이 사용할 수도 있다.
  lblPath.Text = "C:₩₩WinNT₩₩System32";
위와 같이 "₩"를 쓰기 위해 "₩₩"과 같이 복잡하게 쓰는 대신에 @를 사용하여 다음과 같이 간단하게 쓸 수 있다.
  lblPath.Text = @"C:₩WinNT₩System32";
다음은 Panel을 만들고, Panel에 앞에 만든 Label을 추가한 부분이다. 보면 무엇을 하는지 다들 잘 알 수 있을 것이다.
      // Panel Init.
      this.panTotal = new System.Windows.Forms.Panel();
      panTotal.Size = new System.Drawing.Size(150, 100);
      panTotal.Location = new System.Drawing.Point(555, 5);
      panTotal.BorderStyle = System.Windows.Forms.BorderStyle.Fixed3D;

      panTotal.Controls.AddRange(new System.Windows.Forms.Control[] {
                                                                      lblProcessTotal,
                                                                      lblThreadTotal,
                                                                    });    
    
다음은 체크박스를 만드는 부분이다. 체크박스는 화면에 표시되는 쓰레드 정보를 갱신하기 위해 사용한다.
      // CheckBox Init.
      this.chkRefresh = new System.Windows.Forms.CheckBox();
      chkRefresh.Text = "Refresh";
      chkRefresh.Checked = false;
      chkRefresh.Size = new System.Drawing.Size(80, 20);
      chkRefresh.Location = new System.Drawing.Point(555, 480);
다음은 Controls.AddRange를 사용하여 지금까지 작성한 컨트롤을 폼에 추가한다.
      // add controls to form.
      this.Controls.AddRange(new System.Windows.Forms.Control[] {
                                                                  lstProcess, 
                                                                  lblTotal,
                                                                  panTotal,
                                                                  chkRefresh
                                                                });

      this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
      this.ClientSize = new System.Drawing.Size(720, 500);
      this.Load += new System.EventHandler(this.MyForm_Load);

    } // end of MyForm()
    #endregion
위 코드의 마지막에는 폼에 Load 이벤트를 추가하고, 이벤트에 사용할 함수를 위임했다. 다음은 ListView를 초기화하고, 앞에서 정의한 ColumnHeader를 추가한다.
private void InitializelstProcess()
    {
      // Add ListView Column Headers

      lstProcess.Clear();
      lstProcess.Columns.AddRange( new System.Windows.Forms.ColumnHeader[] {
                                                                             colHeader1, 
                                                                             colHeader2,
                                                                             colHeader3,
                                                                             colHeader4,
                                                                             colHeader5,
                                                                             colHeader6
                                                                           });
    }
다음은 체크 박스 chkRefresh가 선택되었는지를 확인하여 선택되어있으면 프로세스 목록을 갱신하도 록 하는 부분이다. 이 부분은 timer를 이용한다.
    private void timer_Tick(object sender, System.EventArgs e)
    {
      if ( chkRefresh.Checked )
      {
        RefreshList();
      }
    }

    private void RefreshList()
    {
      InitializelstProcess();
      FillProcessView();
    }
RefreshList()에서는 단순히 ListView를 초기화하고 프로세스를 채우는 FillProcessView() 함수를 호출한다.
    private void FillProcessView()
    {
      try
      {
        string[] strThread = new String[6];

        // Process와 Thread Count는 항상 초기화.
        m_threadCount = 0;
        procs = Process.GetProcesses();
        m_processCount = procs.Length;

        lstProcess.BeginUpdate();

        foreach(Process aProc in procs)
        {
          // Physical/Virtual Mem in KB
          long physicalMem = aProc.WorkingSet/1024;
          long virtualMem = aProc.VirtualMemorySize/1024;

          strThread[0] = aProc.ProcessName.ToString();
          strThread[1] = aProc.Id.ToString();
          strThread[2] = aProc.BasePriority.ToString();
          strThread[3] = physicalMem.ToString() + " KB";
          strThread[4] = virtualMem.ToString() + " KB";
          strThread[5] = aProc.StartTime.ToString();

          // Add Item to the list view
          ListViewItem item = new ListViewItem(strThread, 0);
          lstProcess.Items.Add(item);

          // Sum Threads Count from each Process
          m_threadCount = m_threadCount + aProc.Threads.Count;
        }

        lstProcess.EndUpdate();

        lblProcessTotal.Text = @"Processes: " + m_processCount;
        lblThreadTotal.Text = @"Threads : " + m_threadCount;
      }

      catch(Exception e)
      {
        System.Diagnostics.EventLog evlog = new System.Diagnostics.EventLog();
        evlog.Log = "Application";
        evlog.Source = e.Source;
        evlog.WriteEntry(e.Message);
      }
    }
먼저 Process.GetProcesses()를 이용해서 시스템에 있는 모든 프로세스를 가져온다(이것을 이용해서 콘솔에 프로세스를 출력하는 예제는 지난 시간에 쓴 글을 참고하기 바란다). lstProcess의 BeginUpdate()와 EndUpdate()는 ListView를 갱신하는 동안 화면에 처리를 반영하지 않는다. 만약 프로세스에 대한 정보를 얻는데 실패하면 NT의 이벤트 로그에 로그를 기록하도록 하였다.
    private void lstProcess_OnClick(object sender, System.EventArgs e)
    {
      ListViewItem li = lstProcess.SelectedItems[0];
      string strProcID = lstProcess.SelectedItems[0].SubItems[1].Text;
      string strProcName = lstProcess.SelectedItems[0].SubItems[0].Text;

      int currProcID = Convert.ToInt32(strProcID);
      string currProcName = Convert.ToString(strProcName);

      WinTopThreadInfo aFrm = new WinTopThreadInfo();
      aFrm.DisplayForm(currProcID, currProcName);
    }
ListView에서 해당 프로세스를 클릭하면 프로세스의 쓰레드에 대한 정보를 새로운 창에 띄우는 부분이다. 코드를 보면 무슨 내용인지 알 수 있을 것이다. 쓰레드 정보를 보여주는 부분은 WinTopThreadInfo 클래스로 작성할 것이고, DisplayForm 메소드를 갖는다.
    protected override void Dispose(bool disposing)
    {
      if(components != null)
      {
        components.Dispose();
      }
      base.Dispose(disposing);
    }

  }
}
마지막은 자원을 정리하는 부분이다. 여기서는 굳이 정의하지 않아도 되지만 예의상(?) 사용하였다. Dispose는 WinTopThreadInfo 클래스에서도 사용하며, 새로운 창을 생성하는데 사용한 자원을 반환하기 위해 사용한다. 코드를 작성했으면 다음은 쓰레드 정보를 보여주는 WinTopThreadInfo 클래스를 작성하도록 하자.
이름 : WinTopThreadInfo.cs

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

namespace WinTop
{

public class WinTopThreadInfo : System.Windows.Forms.Form
{
    private System.Windows.Forms.Label lblTotalThread;
    private System.Windows.Forms.ListView lstThread;

    private long procID;
    private ProcessThreadCollection m_Threads;

    public int m_threadCount;
    
    private System.ComponentModel.Container components = null;
여기서 주의할 것은 namespace WinTop과 같이 같은 네임스페이스안에 클래스를 선언한다는 점이다. 또한 프로세스에 속한 쓰레드에 대한 정보를 담기 위해 ProcessThreadCollection을 사용했다. 또한 System.ComponentModel.Container components와 같이 사용하여, 폼에서 사용하는 컴포넌트에 대한 컨테이너로 사용한다.
      public WinTopThreadInfo()
      {

      // Label Init.
      this.lblTotalThread = new Label();
      lblTotalThread.Size = new System.Drawing.Size(70, 12);
      lblTotalThread.Location = new System.Drawing.Point(3, 5);
      lblTotalThread.Text = "Total Threads : ";

      // ListView Init.
      this.lstThread = new ListView();
      lstThread.Name = "lstThread";
      lstThread.Size = new System.Drawing.Size(500, 500);
      lstThread.FullRowSelect = false;
      lstThread.View = View.Details;
      lstThread.Sorting = SortOrder.Ascending;
      lstThread.Location = new System.Drawing.Point(3, 20);

      InitializeThreadList();

      this.Controls.AddRange(new System.Windows.Forms.Control[] {
                                                                  lblTotalThread,
                                                                  lstThread
                                                                });
      this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
      this.ClientSize = new System.Drawing.Size(510, 530);
    } // end of WinTopThreadInfo
이 부분은 앞에서 설명한 WinTop.cs와 같으므로 코드를 보면 무엇을 하는지 알 수 있을 것이다.
    private void InitializeThreadList()
    {
      lstThread.Columns.Add("ID", 50, HorizontalAlignment.Left);
      lstThread.Columns.Add("Priority", 70, HorizontalAlignment.Left);
      lstThread.Columns.Add("Base Priority", 70, HorizontalAlignment.Left);
      lstThread.Columns.Add("Total Process Time", 100, HorizontalAlignment.Left);
      lstThread.Columns.Add("State", 70, HorizontalAlignment.Left);
      lstThread.Columns.Add("Start Time", 150, HorizontalAlignment.Left);
      lstThread.Columns.Add("User Time", 120, HorizontalAlignment.Left);
    } // end of InitializeThreadList
WinTop.cs에서는 ColumnHeader를 직접 생성하여 ListView에 추가했지만 여기서는 ListView.Columns.Add를 사용하여 간편하게 컬럼을 설정하도록 하였다.
    private void FillThreadList(int procID)
    {
      string[] strThreads = new String[7];
      m_Threads = GetThreads(procID);
      ProcessThreadCollection threads;
      Process proc;

      proc = Process.GetProcessById(procID);
      threads = proc.Threads;

      m_threadCount = threads.Count;
      lblTotalThread.Text = @"Total threads: " + m_threadCount;

      for(int Indx = 0; Indx < threads.Count; Indx++)
      {
        strThreads[0] = threads[Indx].Id.ToString();
        strThreads[1] = threads[Indx].CurrentPriority.ToString();
        strThreads[2] = threads[Indx].BasePriority.ToString();
        strThreads[3] = threads[Indx].TotalProcessorTime.ToString();
        strThreads[4] = threads[Indx].ThreadState.ToString();
        strThreads[5] = threads[Indx].StartTime.ToString();
        strThreads[6] = threads[Indx].UserProcessorTime.ToString();
        
        ListViewItem li = new ListViewItem(strThreads, 0);
        lstThread.Items.Add(li);
      }
    } // end of FillThreadList
FillThreadList()는 ListView에 쓰레드의 정보를 채운다.
public static ProcessThreadCollection GetThreads(int procID)
    {
      try
      {
        Process proc=Process.GetProcessById(procID);
        ProcessThreadCollection threads = proc.Threads;
        return threads;
      }
      catch ( Exception )
      {
        return null;
      }
    } // end of GetThreads
프로세스 ID를 이용하여 프로세스에 속한 쓰레드를 가져온다. 가져오는 데 실패하면 null을 반환하도록 한다.
    protected override void Dispose(bool disposing)
    {
      if(components != null)
      {
        components.Dispose();
      }
      base.Dispose(disposing);
    } // end of Dispose
  }
}
폼이 없어질 때 자원을 정리하기 위해서 Dispose()를 오버라이드하였다. 사용자 정의 컨트롤이나 인터페이스에 관심 있는 분들은 MSDN.NET에서 ISite, IComponent, DesignTime등을 주제로 하여 찾아보기 바란다. 컴파일은 다음과 같이 한다.
Csc /t:winexe /out:WinTop.exe WinTop.cs WinTopThreadInfo.cs
컴파일이 끝나면 윈도우용 WinTop이 간단하나마 만들어졌을 것이다. 코드를 한 줄 한 줄 살펴보면서 프로세스와 쓰레드에 대한 정보를 어떻게 가져오는지 살펴보기 바란다. System.Diagnostics에는 프로그램을 추적하고 분석할 수 있는 기능도 있지만, Process, ProcessModule, ProcessThread 클래스를 사용하여 프로세스를 제어할 수 있는 기능도 있다. System.Diagnostics.Process.Kill()과 같은 인스턴스 메소드를 사용하여 원하는 프로세스를 종료할 수도 있다. 시스템의 페이지된 메모리 크기와 페이지 되지 않은 메모리 크기를 알고 싶다면 System.Diagnostics.Process.NonpagedSystemMemorySize와 .PagedMemorySize를 이용하면 된다. 보다 더 많은 것들에 관심 있는 분들은 MSDN을 찾아볼 것을 권한다. 끝으로 VS.NET beta2와 VS.NET RC에 들어있는 MSDN은 닷넷을 위하여 나온 것으로 MSDN Subscription으로 제공되는 MSDN과 MS의 MSDN 온라인에는 없는 많은 유용한 정보를 갖고 있다(특히 VS.NET RC에는 많은 자료가 들어있다). 때문에 여기서 적절한 관련 자료에 대한 URL을 제공하지 못하는 것을 안타깝게 생각하지만 MSDN.NET에서 충분한 자료를 찾아볼 수 있을 것이다 생각한다. VS.NET beta 2에서는 ListView를 추가하고 컴파일하면 폼에서 생성하는 코드에 오류가 있어서 실행되지 않을 것이다. (VS.NET RC에서는 잘 동작한다.) 따라서 윈도우 폼에 대해서 잘 모른다면 메모장으로 직접 코드를 입력하면서 구성이나 개념을 아는 것이 더 좋다고 생각한다. 마지막으로 쓰레드와는 무관하지만 시스템의 정보를 알아내려면 다음과 같은 코드를 컴파일하여 실행해보기 바란다.
이름 : info.cs

namespace csharp
{
  using System;
  using System.Collections;

  public class SystemInfo
  {
    public static void Main()
    {
      SystemInfo si = new SystemInfo();
      si.Display();
    }

    private void Display()
    {
      Console.WriteLine("Starting Tick Count : {0}", Environment.TickCount);
      Console.WriteLine("Current OS : {0}", Environment.OSVersion);
      Console.WriteLine(".NET Framework Version : {0}", Environment.Version);
      
      Console.WriteLine("Command Line : {0}", Environment.CommandLine);
      Console.WriteLine("Machine Name(NetBIOS) : {0}", Environment.MachineName);
      
      Console.WriteLine("Current Directory : {0}", Environment.CurrentDirectory);
      Console.WriteLine("System Directory : {0}", Environment.SystemDirectory);

      Console.WriteLine("User Name : {0}", Environment.UserName);
      Console.WriteLine("User Domain Name : {0}", Environment.UserDomainName);

      Console.WriteLine("Physical Memory of process : {0}", Environment.WorkingSet);

      string [] drives = Environment.GetLogicalDrives();
      int Indx = 0;

      foreach( string drive in drives)
      {
        Console.WriteLine("Drive {0} - {1}", Indx.ToString(), drive.ToString());
        Indx++;
      }
      Console.WriteLine("Ending Tick Count : {0}", Environment.TickCount);
    }
  }
}
이상으로 시스템에 있는 프로세스와 쓰레드 정보를 알아내고, 제어할 수 있는 방법에 대해서 알아보았다. 보다 자세한 부분에 관심이 있는 분들은 MSDN 등의 온라인 문서를 참고하기 바란다. 마지막으로 어찌보면 참으로 쓸데없는 내용을 적어놓은 건지도 모르지만, 과연 이러한 것들을 어디에 써먹을 것인가를 한 번은 나름대로 생각해 보는 것도 좋을 것 같다. 닷넷 프레임워크에서 제공하는 이러한 클래스를 이용해서 현재 실행중인 프로그램의 윈도우 타이틀을 가져오는 것은 어떨까. 이 타이틀을 이용해서 응용 프로그램을 제어하는 것도 가능할 것이다. (관심이 있다면 System.Runtime.InteropServices와 DllImport에 대해서 찾아보기 바란다.) 예제를 위해서 비교적 간단하게 작성했기 때문에 다소 객체 지향과는 동떨어진 구성을 했지만, 전체적인 틀을 새롭게 작성해서 리모팅이나 웹 서비스를 이용해서 원격으로 시스템의 프로세스 정보를 얻을 수도 있을 것이고, 특성 프로세스가 실행중인지 등을 알아낼 수도 있을 것이다. 또한 원격으로 특정 프로세스를 종료할 수도 있을 것이다. 추가적으로 NetWkstaGetInfo()와 같은 Win32 API를 이용한다면 원격으로 특성 서버의 종류(PDC, BDC, SQL Server, Novell Network Server, NT Member, Windows 9x)를 알아내는 것도 가능할 것이다. 기존에 작성한 클래스를 웹 서비스로 제공하는 wsdl.exe은 /username, /password, /domain과 같은 옵션을 이용하여 관리자 권한으로 접근하도록 제한된 영역에 접근할 수 있는 프록시 클래스를 생성할 수 있으며, 관리자 권한으로 할 수 있는 작업을 할 수도 있을 것이다. 무엇을 할 것인가는 여러분에게 달려있다. 다음 단계 다음에는 드디어 기다리던 멀티 쓰레드에서의 동기화에 대해서 다룰 것이다. 늘 하는 얘기지만 필자는 전문가도 아니며 모르기 때문에 질문이나 문의사항을 언제나 환영한다. 또한 For Guru(http://forguru.home.dhs.org)에서 많은 논쟁을 벌이는 것을 좋아한다. 필자가 틀렸다고 뒤에서 험담하기 보다는 For Guru나 email을 통해서 필자에게 틀린 점을 알려주기 바란다. (아까운 정력, 생산적인 곳에 쓰도록 하자) 벌써 12월이고, 한 달 후면 컴퓨터를 처음 접한지 16년째가 되고, 프로그래머로 일한지 6년이 되어 간다니 새삼스럽다. C#이라는 유행과 같은 언어로 쓰레드에 쓰고 있지만, 그 보다는 그 기본이 되는 바탕을 설명하려고 하는데 그러지 못해 아쉬운 점이 많다. 유행과 새로운 기술을 따라가는 것도 중요하지만, 그 바탕이 되는 기술을 아는 것이 더 중요하다는 것을 시간이 갈수록 느끼고 있다. 끝으로 필자의 부족한 실력이나마 WinTop을 작성할 수 있도록 도와준 프로그래머들에게 감사드린다. 전체 뼈대를 제공한 Melanie, ListView와 관련하여 조언을 아끼지 않았으며 지금까지의 C# 쓰레드 글들에 대해서 끊임없는 피드백을 해주신 4baf, Linux에서의 쓰레드로 필자를 괴롭히는 dicajohn, Perl에서의 쓰레드 프로그래밍에 대한 조언과 도움을 준 nuthack, 원고를 쓸 때마다 MSN으로 괴롭히는 운령, 모르는 부분에 대해 많은 참고가 되는 도서를 제공해준 한빛미디어, 그 외 많은 분들에게도 감사를 드린다. 필자의 부족한 실력으로 이런 글을 쓸 수 있을리없으며 많은 분들이 도와주신 덕분이다. [☞ 소스 다운 로드] 참고도서
  • 닷넷 프레임워크 에센스』(쑤안 타이, 호앙 램 저, 한빛미디어 2001) - 프레임워크 전반에 관해 꼭 필요한 부분만 있기 때문에 전체적인 코딩 틀이 생각나지 않을 때마다 참고하고 있다.
  • C# 프로그래밍』(제시 리버티 저, 한빛미디어, 2001) - C#에 대한 훌륭한 책이며, 쓰레드에 대해서도 장을 할애하고 있다. 처음 C# 쓰레드에 쓰기 전에 이 책을 보면서 쓰레드에 대해 쓸 것을 생각하였다. ADO.NET과 DataGrid에 대한 부분이 다른 책들보다 자세하게 되어 있으므로 데이터베이스 관련 프로그래밍을 하는 분들에게 도움이 될 것이다. (이 책은 훌륭한 입문서다. 쓰레드 관련서는 아니다. -_-)
  • VB.NET In a Nutshell』(Steven Roman, Ron Petrusha, Paul Lomax 저, 오라일리, 2001) - 쓰레드 예외처리에 대한 부분과 전체 닷넷 프레임워크의 충실한 분류와 개요 설명은 C#에서도 충분히 많은 도움이 되고 있다.
  • Subclassing & Hooking with Visual Basic』(Stephen Teilhet 저, 오라일리, 2001) - 비록 비주얼 베이직에서의 서브클래싱과 후킹에 대한 글이지만, 후반에 다루어져 있는 VB.NET에서의 서브 클래싱과 후킹부분과 상호 운영성 부분에서 많은 참고가 되었던 책이며, Win32 Msg를 닷넷 프로그램과 주고 받는 방법에 대해서 많은 참고가 되었다.
  • 『Inside C#』 - C# 언어보다는 그 설계의 이면에 대해 많은 이야기를 제공해주고 있으며, 가비지 컬렉터(Garbage Collector)와 쓰레드 부분에 있어서 많은 참고가 되었다.
  • 『C# & .NET Platform』- C#과 닷넷 플랫폼에 대한 자세한 해설이 들어있지만 솔직한 느낌은 두께에 비해 C#도, 닷넷 플랫폼에도 비중을 제대로 두지 못한 것 같아 아쉬움이 남는다. 쓰레드에 대해서 다루고 있는 부분은 없지만 응용 프로그램 제작시 많이 쓰게 되는 인터페이스의 사용에 있어서 많은 참고가 되었다.
TAG :
댓글 입력
자료실

최근 본 상품0