안녕하세요? 맨날맑음 입니다.

네이트을 보면 X(종료) 버튼을 누를시 프로그램이 꺼지지 않고, 트레이로 가게 됩니다.
이번에 알아볼 내용은 .net Windows Forms 프로젝트에서 이런 기능을 구현 하는 것입니다.

우선 준비물이 필요합니다. 트레이에 나타나게 할 아이콘 파일(.ico)입니다.
아이콘 파일은 웹상에서 마음에 드는것을 다운로드 받아도 되고, 포토샵이나 기타 프로그램으로
그림파일을 변환시켜 사용하셔도 됩니다. 물론 솔루션 탐색기 -> 해당 프로젝트(우클릭) -> 추가 -> 새항목 -> 아이콘파일을 선택하셔서 직접 그려서 사용하셔도 됩니다. 각자 취향에 맞는 아이콘을 준비해 주면 됩니다.

우선 Windows Forms 프로젝트를 생성합니다.

도구상자에서 NotifyIcon을 선택하여 Form에 추가 합니다. 추가 후 소성창의 Icon 속성에 준비해준 Ico 파일을 연결해 줍니다. 여기까지만 하고 실행시켜도 트레이에 우리 프로그램의 아이콘이 보이는 걸 볼 수 있습니다.

트레이에서 마우스 오른쪽 버튼을 눌렀을 경우 나올 메뉴를 위해 ContextMenuStrip도 추가해 줍니다.
ContextMenuStrip의 메뉴를 추가 합니다. 간단히 '종료' 메뉴(Name : ExitToolStripMenuItem)만 넣었습니다. 다시 notifyIcon1의 속성창으로 가서 ContextMenuStrip의 값을 contextMenuStrip1로 지정 합니다.
여기까지 하셨으면 프로그램 실행 시 트레이에 아이콘이 나타나게 되고, 오른쪽 버튼으로 아이콘을 클릭하면
종료 메뉴가 나타나게 됩니다.

이제 코드창(cs)으로 가서 몇가지 이벤트핸들러만 구현해 주면 트레이에 아이콘이 나타나는 프로그램을 만들수 있습니다.
using System;
using System.Windows.Forms;
namespace TraySample
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
            this.FormClosing += Form1_FormClosing;
            this.notifyIcon1.DoubleClick += notifyIcon1_DoubleClick;
            this.ExitToolStripMenuItem.Click += ExitToolStripMenuItem_Click;
        }
        // 트레이의 종료 메뉴를 눌렀을때
        void ExitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            //트레이아이콘 없앰
            notifyIcon1.Visible = false;
            //프로세스 종료
            Application.Exit();
        }
        //트레이 아이콘을 더블클릭 했을시 호출
        void notifyIcon1_DoubleClick(object sender, EventArgs e)
        {
            this.Visible = true; // 폼의 표시
            if (this.WindowState == FormWindowState.Minimized)
                this.WindowState = FormWindowState.Normal; // 최소화를 멈춘다 
            this.Activate(); // 폼을 활성화 시킨다
        }
        //폼이 종료 되려 할때 호출
        void Form1_FormClosing(object sender, FormClosingEventArgs e)
        {
            e.Cancel = true; // 종료 이벤트를 취소 시킨다
            this.Visible = false; // 폼을 표시하지 않는다;
        }
    }
}
주석으로 충분한 설명이 있지만 간단히 소스를 설명하면, 우선 타이틀바의 X 단추를 눌렀을때 프로그램이 종료 되는것을 막기위해 FormClosing 이벤트 핸들러에서 폼이 종료 이벤트를 취소 시켰습니다.
또한 트레이 아이콘을 더블클릭하면 폼이 다시 나와야함으로 notifyIcon1_DoubleClick 이벤트 핸들러에서 는 폼을 다시 표시하고, 활성화 시켰으며, 메뉴에서 종료를 선택했을때는 프로그램을 종료하게 하였습니다.

이렇게 간단하게 네이트온처럼 트레이에 표시되는 응용프로그램을 만들 수 있습니다.
Posted by 맨날맑음
,
안녕하세요? 맨날맑음 입니다.
아르바이트 삼아 하고있는 프로그램 제작하다가 타이틀바의 높이를 알아 올 일이 생겼습니다.
C#에서는 제공하는 클래스가 없는것 같고, API 함수를 쓰면 쉽게 해결 할 수 있습니다.
int WINAPI GetSystemMetrics(int nIndex );
사실 GetSystemMetrics 함수는 매개변수에 따라 여러가지 기능을 하는데요. 주로 시스템의 구성이나 설정을
얻어올 때 사용됩니다. 시스템 영역의 정보를 얻어오려면 이 함수의 기능을 찾아보면 거의 있습니다.
그중에서 이번엔 타이틀바의 높이를 알아오는 방법을 알아보겠습니다. 타이틀바의 높이는
SM_CYCAPTION
플래그를 매개변수로 넣어주어 리턴되는 값으로 얻어 올 수 있습니다.

저는 C#에서 사용하여야 했기 때문에 윈폼 프로젝트로 테스트해 보았습니다.
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern int GetSystemMetrics(int nIndex);
public Form1()
{
     InitializeComponent();
     const int SM_CYCAPTION = 4;
     int height = GetSystemMetrics(SM_CYCAPTION);            
     this.Text += height.ToString();
}
너무 간단하지만, C#에서 함수 사용을 위해 DLL 선언을 해주었고, CM_CYCAPTION = 4 를
매개변수로 하여 해당 함수를 호출하여 높이를 얻어왔습니다.


실행화면
윈도우 설정에서 타이틀바의 높이를 변경해 보았는데요. 잘 얻어오는 것을 알 수 있습니다.


Posted by 맨날맑음
,
프로젝트 도중 키 이벤트를 강제로 발생시켜야 하는 상황이 생겼는데요;;
SendMessage로 WM_KEYDOWN을 보내놓고,  키가 입력되지 않는다고 화내고 있었습니다''
keybd_event라는 키보드 메세지를 생성해주는 훌륭한 Win32 API함수가 있더군요

keybd_event 함수의 원형은 다음과 같습니다.(USER32.DLL)
VOID keybd_event(      
    BYTE bVk,              // 가상 키코드
    BYTE bScan,          // 하드웨어 스캔 코드
    DWORD dwFlags,     // 동작 지정 Flag
    PTR dwExtraInfo     // 추가 정보
);
bVK
          가상키코드를 지정하는데 1~125 범위의 값이어야 합니다.
bScan
         
사용하지 않는 인자 입니다.

dwFlags
          함수의 동작을 지정하는데 0을 넣을 경우 KeyDown이고 0x0002를 넣을 경우 KeyUP 입니다.

dwExtraInfo
          키스트로크 관련 32비트의 추가 정보를 지정 한다고 합니다.

C#에서 사용시!
[DllImport("user32.dll)]
public static extern void Keybd_event(byte vk, byte scan, int flags, ref int extrainfo);


※ ex)
const byte AltKey = 18;
const int KEYUP = 0x0002;   
int
Info=0;
keybd_event(AltKey, 0, 0, ref Info);   // ALT key 다운
keybd_event(AltKey, 0, KEYUP, ref Info);  // ALT key 업

Posted by 맨날맑음
,


어제 프로젝트 관련된 코딩을 하다가 난수를 써야 할 일이 있어서
C#의 Random Class에 관해 알아 보았다!

사실 Random Class의 사용방법은 그리 복잡하지도 어렵지도 않지만;
내가 만들고 싶은 난수가 적절히 만들어 지지 않아 몇시간을 테스트한김에
이와 관련된 포스팅을 하게 되었다;

예전 C언어나 C++의 rand() 함수를 사용할때는

int random = rand()%50;
위의 코드를 수행하게 되면 random 변수에는 에는 0부터 49까지 난수가 들어가게 된다;
rand()에sms seed 값이 있어 저렇게만 수행할 경우
만약 랜덤한 수가 1 3 6 2 6 순으로 나왔다면; 프로그램을 껏다 켜도 똑같은 패턴으로 나오게 된다;
그래서 아래의 항목을 추가하게 되면 그것을 막을 수 있었다;

srand(time(NULL)); // 시간에 따른 난수 발생, 없을경우 실행때마다 같은 난수가 발생한다.


이제 C# 의 Random Class를 보자;

사용법 정말 간단하다;

Random r  = new Random();

생성자는 오버로딩 되어있어 생성시 시드값을 정할수 있고! 정하지 않을수 있다;

정할경우 위에 말한대로 계속 똑같은 패턴의 난수가 발생하며 디폴트일 경우에는 시스템의 시간을 이용하여 
시드값을 정하기 때문에 매번 다른 패턴의 난수
가 발생한다;

제공하는 매소드를 알아보자;

r.Next() : 범위가 없는 랜덤한 값을 리턴한다
r.Next(int maxValue) : 난수의 범위의 상한을 지정
r.Next(int minValue, int maxValue) : 난수의 범위의 하한과 상한을 지정!


여기서 중요한것은 난수의 하한은 난수에 포함 되지만 상한은 포함 되지 않는다는것이다;
즉 1부터 10의 값을 난수로 얻고 싶을때 Next(1,10); 을 호출하면 10은 죽어도 안나온다는 말인다;;
10을 난수로 얻고 싶다면 Next(1,11)을 호출 해야한다;

그외에
r.NextBytes()
r.NextDouble() 
메소드가 있는데 int값만 필요해서 사용하지는 않았지만 메소드 명으로 쉽게 짐작 할수 있다;

이제 본론으로 들어가서 이렇게 간단한 Random Class의 사용방법을  포스팅을 하게 된 이유는
내가 발생하고 싶은 난수가 특정시간에 여러개의 객체를 생성할때 각각의 객체마다 다른 
난수를 가지고 있게 하고 싶었지만 Seed값을 기본적으로 시간을 사용하기 때문에 
할수가 없어서이다;

다음의 예제를 보면;

class Program
{
        static void Main(string[] args)
        {
            for (int i = 0; i < 10; i++)
            {
                Item item = new Item();
            }
        }
}

class Item
{
        int num1;
        int num2;
        public Item()
        {
            Random r = new Random();
            num1 = r.Next(1, 100);
            num2 = r.Next(100, 200);
            Console.WriteLine("{0} , {1}", num1, num2);
        }
}



매우 간단한 구조이다 Main()에서는 Item 객체를 10개 만들고 
Item 객체의 생성자에서는 난수를 2개 (각각 1~99, 100~199 범위의 값) 발생시켜
출력한다;

어떤 결과가 나올까? 10개의 객체는 각각 다른 난수를 가지고 있을까?

-- 실행 결과 --

결과처럼 '아니오' 이다; 

이와 같은 발생의 이유는 기본적으로 난수발생의 시드가 시간이기 떄문이다;
위에 예제처럼 빠르게 10개의 객체를 생성할 경우 시드가 같아져 같은 결과가 나오는것이다;

어떻게 해결할까?

밑의 두가지 예제의 차이를 알게되면 쉽게 해결 가능하다;
예제1)
Random
r = new Random();
for (int i = 0; i < 10; i++)
{
     Console.WriteLine("{0}", r.Next());               
}

예제2)
for (int i = 0; i < 10; i++)
{
     Random r = new Random();
     Console.WriteLine("{0}", r.Next());
}

--결과 화면--


화면에서 보다싶이 Random 객체를 빠르게 10번 생성한 예제 2에서는  같은 번호가 나왔고
객체를 재사용한 예제1에서는 다른 결과가 나왔다;


이 차이를 이용해 위에서 실패한 Item class에 랜덤한값을 배치 할수 있는것이다;

Random 객체를 static으로 두고 하나만 계속 사용한다면 위의 예제도 우리가 원하는 난수를 갖게 될것이다;

Posted by 맨날맑음
,

NET Framework 3.0에서 LINQ로 인하여 너무나도 많은 것이 변화되어 버렸기에 짚고 넘어가야만 한다.

 LINQ의 아키텍쳐

조금 작긴하지만 LINQ로 할 수 있는 것은 object, Dataset, SQL, Entities, XML 이렇게 5가지이며 Object와 RDB, XML 3가지에 대해 컨트롤 할 수 있게 되었다. 다시말해 3tire 시스템에서 맨 끝에 존재하는 현존하는 데이터 영역 모두를 LINQ를 통해 단일화된 방법으로 사용할 수 있는 것이다. (생각해보자 그전에 Data Object, DB, XML 3가지의 사용법이 모두 달라 얼마나 힘들었는지?) 또한 LINQ를 활용하여 delegate 대리자를 선언하지 않아도 되며 OR Mapping 툴 또는 Framework를 별도로 설치하지 않아도 된다.

NET Framework 3.0 의 변화 부분


1. "var" (Local Variable Type Inference) 변수 지원
var는 정말 불확실성때문에 꺼려지는 문법중하나이다. 그러나 MS가 누구인가? VB부터 줄곧 따라다니던 VAR를 포기하나 했더니 결국 집어 넣고 말았다.
using System;
class Test {
  public static void Main() {
    var a = "12345";
    Console.WriteLine (a);
    var b = 123456;
    Console.WriteLine (b.ToString());
  }
}
컴파일 부터 실행까지 아무런 문제 없이 실행된다. -_- 단, var를 실행되는 런타임이 결정하는 것이 아니라 컴파일하는 컴파일러가 결정한다. 따라서 null을 넣게 되면? "test.cs(4,9): error CS0815: 암시적으로 형식화된 지역변수에는 을 할당 할 수 없습니다."라는 메시지를 보게될 것이다. 이부분이 가장 중요한 부분이다. 왜냐면 다른 특징들은 모두 var를 지원함으로써 얻어지는 것이기 때문이다.
 
2. 배열, XML, DB 등 데이터를 가져오는 것이 하나의 방법으로 통한다.
바로 이부분이 LINQ의 능력이다. 배열이나 XML, DB등 데이터를 가져와서 핸들링 할때 데이터를 가져오는 부분이 모두 달랐다. XML을 파싱해서 각각의 변수에 넣었다가 핸들링하고 다시 집어 넣고 노가다 였는데 모든 것을 Linq 한방에 핸들링 할 수 있다는 것이다.
using System;
using System.Linq;

class Test {
  public static void Main() {
      int[] scores = new int[] { 97, 92, 81, 60 };



      var scoreQuery =  from score in scores 
                               where score > 80 
                               select score;



      foreach (int i in scoreQuery)
      {
          Console.Write(i + " ");
      }            
  }
}
배열인 scores에 대해 80점 이상인 놈만 가져오자. 이런거 할려면 if문 넣어서 뺑뺑이 돌리던 기억이 엇그제 같은데 LINQ로 한방에 끝나버린다.

3. Anonymous Type을 지원한다.
다음의 황당한 코드 함 보자.
using System;
class Test {
  public static void Main() {
      var user = new { Name = "vicviper", Age = 35 };
      Console.WriteLine (user.Name);
  }
}
무엇이 황당한가? 분명 2.0대에서는 user라는 클래스를 만들고 Name과 Age를 인터페이스 선언하여야 정상 작동하던 코드였다. 그러나, 우리의 C# 3.0 컴파일러는 이렇게 단순한 놈은 자동으로 생성해버린다. 따라서, 컴파일러 오류도 없고 정상작동되는 것이다. 심지어 이런것도 가능하다.
using System;class Test {
  public static void Main() {
      var family1 = new { Name = "papa", Age = 35 };
      var family2 = new { Name = "mama", Age = 35 };
      Console.WriteLine (family1.Age + family2.Age);
  }
}

XML 파싱하기

 (파일명 : test.xml)


   111.111.111.111
   vic
   password

Linq이전에는 이녀석을 비교하려고 XML Document 열어서 열심히 While문으로 Read했었다. Linq로 이놈을 한번 분석해보면 다음과 같이 매우 짧게 진행할 수 있다.
using System.Linq;
using System.Xml;
using System.Xml.Linq;


class Test {
  static void Main(){ 
   XDocument xe = XDocument.Load(System.Windows.Forms.Application.StartupPath  + "\\test.xml");
    var query = from c in xe.Descendants("config") 
                    select c;

   foreach(var q in query)
   {
      Console.WriteLine(q);
   }
   Console.ReadLine();
  }  
}
실행시켜보면 위의 XML전체가 출력되는 것을 볼수 있을 것이다. 이제 이녀석을 좀더 다듬어 보자. "select c;"라고 된 부분을 "select c.Element("id");"로 바꿔보자.
var query = from c in xe.Descendants("config") 
                    select c.Element("id");
실행을 하면 XML의 ID부분만 출력이 될 것이다.
111.111.111.111
어쩐지 제대로 찾아 들어오는 느낌. 이제 아까 바꾼 "select c.Element("id");"를 "select c.Element("id").Value;"로 바꿔보자.
    var query = from c in xe.Descendants("config") 
                    select c.Element("id").Value;
실행하면 결과가 아주 이쁘게 값만 떨어진다.
111.111.111.111
그런데 여기서 만족할 수 없다. 여러개의 값을 가려오려면 어떻게 해야 할 까? 다음과 같이 함 바꿔보자.
var query = from c in xe.Descendants("config") 
                    select new 
                    {
                        id = c.Element("id").Value,
                       user = c.Element("user").Value
                     }
   //이번에는 foreach문도 바꿔주자
   foreach(var q in query)
   {
      Console.WriteLine(q.id + q.user);
   }   
결과는 직접 함 해보도록하고 이제 다음단계로 SQL문처럼 한번 써보자. 다음과 같이 바꿔보고 실행 함 해보자.
   var query = from c in xe.Descendants("config") 
                where c.Element("user").Value == "vic"
                select c;
어떤게 나오는가? where문이 진짜 먹으니 뿌듯한 느낌!


XML생성


이제 한걸음 더 나가서 XML을 생성함 해보자. 사용은 무지 쉽다.
using System;
using System.Linq;
using System.Xml;
using System.Xml.Linq;

class Test {
  static void Main(){ 
  XDocument xdoc =
      new XDocument(
      new XDeclaration("1.0", "UTF-8", "yes"),
      new XProcessingInstruction("TEST", "TEST Data"),
      new XComment("테스트 XML"),
      new XElement("config",
          new XElement("id", "1234"),
          new XElement("user", "abc")
      )         
   );
   xdoc.Save(System.Windows.Forms.Application.StartupPath  + "\\test.xml");
  }  
}
test,xml을 열어 보면




  1234
  abc

이렇게 정확하게 들어와 있는 것이 보일것이다.
Posted by 맨날맑음
,