블로그 이미지
Every unexpected event is a path to learning for you. blueasa

카테고리

분류 전체보기 (2797)
Unity3D (853)
Programming (479)
Python (8)
TinyXML (5)
STL (13)
D3D (3)
MFC (1)
C/C++ (54)
C++/CLI (45)
C# (251)
WinForm (6)
WPF (5)
Math (10)
A.I. (1)
Win32API (11)
Algorithm (3)
Design Pattern (7)
UML (1)
MaxScript (1)
FMOD (4)
FX Studio (1)
Lua (2)
Terrain (1)
Shader (3)
boost (2)
Xml (2)
JSON (4)
Etc (11)
Monad (1)
Html5 (4)
Qt (1)
Houdini (0)
Regex (14)
Server (33)
Unreal (4)
Gamebryo (56)
Tip & Tech (185)
협업 (61)
3DS Max (3)
Game (12)
Utility (68)
Etc (98)
Link (32)
Portfolio (19)
Subject (90)
iOS,OSX (55)
Android (14)
Linux (5)
잉여 프로젝트 (2)
게임이야기 (3)
Memories (20)
Interest (38)
Thinking (38)
한글 (30)
PaperCraft (5)
Animation (408)
Wallpaper (2)
재테크 (18)
Exercise (3)
나만의 맛집 (3)
냥이 (10)
육아 (16)
Total
Today
Yesterday

C# 싱글톤

Programming/C# / 2010. 7. 2. 16:55

이 전략에서는 클래스의 임의 구성원을 처음으로 참조할 때에 인스턴스가 만들어집니다. 공용 언어 런타임이 변수 초기화를 담당합니다. 파생을 차단하기 위해 클래스가 sealed로 표시되며 이로 인해 인스턴스가 추가될 수 있습니다. 클래스를 sealed로 표시할 때의 장단점에 대한 설명은 [Sells03]을 참조하십시오. 또한 변수가 readonly로 표시됩니다. 이 표시는 정적 초기화 (여기서 표시된 것처럼) 중이나 클래스 생성자에서만 변수를 할당할 수 있음을 의미합니다. 여기서는 정적 초기화가 나타나 있습니다.

이 구현은 공용 언어 런타임을 사용하여 변수를 초기화한다는 점을 제외하면 앞의 예제와 유사합니다. 이 구현에서는 싱글톤(Singleton) 패턴이 해결하려고 하는 두 가지 문제 즉, 글로벌 액세스와 인스턴스화 제어에 대해 다룹니다. public static 속성은 인스턴스에 글로벌 액세스 포인트를 제공합니다. 또한 생성자가 private로 선언되기 때문에 클래스 외부에서는 싱글톤(Singleton) 클래스를 인스턴스화할 수 없습니다. 따라서 변수가 의미하는 것은 시스템에 존재할 수 있는 인스턴스만을 말합니다.

싱글톤(Singleton) 인스턴스는 private로 선언된 정적 구성원 변수에 의해 참조되므로 Instance 속성 호출이 클래스를 처음으로 참조한 후에야 비로소 인스턴스화가 수행됩니다. 따라서 이 솔루션은 싱글톤(Singleton)의 Design Patterns 양식에서처럼 지연 인스턴스화 속성 양식을 구현합니다.

이 방식의 유일한 단점은 인스턴스화 구성에 대한 제어가 약간 저하된다는 점입니다. Design Patterns 양식에서는 인스턴스화 이전에 비기본 생성자를 사용하거나 다른 작업을 수행할 수 있었습니다. 이 솔루션에서는 .NET Framework가 초기화를 수행하므로 이 옵션이 필요 없습니다. 대부분의 경우는 .NET에서 싱글톤(Singleton)을 구현하기 위해 정적 초기화가 더 선호됩니다.

멀티 스레드 싱글톤(Singleton)

대부분의 상황에는 정적 초기화가 적합합니다. 응용 프로그램이 인스턴스화를 연기해야 하고, 인스턴스화 이전에 비기본 생성자를 사용하거나 다른 작업을 수행해야 하고, 멀티 스레드 환경에서 작업해야 하는 경우는 다른 솔루션이 필요합니다. 하지만 정적 초기화 예제에서처럼 스레드 안정성을 보장하기 위해 공용 언어 런타임을 사용할 수 없는 경우가 존재합니다. 이러한 경우는 여러 개의 스레드에 대해 개체 인스턴스가 하나만 만들어지도록 특정한 언어 성능을 사용해야 합니다. 보다 더 일반적인 솔루션은 각각의 스레드가 싱글톤(Singleton) 인스턴스를 동시에 만들지 못하도록 막기 위해 이중 확인 잠금(Double-Check Locking)[Lea99] 관용구를 사용하는 것입니다.

참고:공용 언어 런타임은 다른 환경에서 흔히 나타나는 이중 확인 잠금(Double-Check Locking) 사용에 관련된 문제를 해결합니다. 이 문제에 대한 자세한 내용은 메릴랜드 주립대, 컴퓨터 사이언스 학과 웹 사이트(http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html)에서 "The 'Double-Checked Locking Is Broken' Declaration" 을 참조하십시오.

다음 구현에서는 싱글톤(Singleton)의 인스턴스가 아직 만들어지지 않았을 때 한 개의 스레드만이 임계 영역(lock 블록에 의해 식별)에 들어갈 수 있습니다.

 

using System;

public sealed class Singleton
{
   private static volatile Singleton instance;
   private static object syncRoot = new Object();

   private Singleton() {}

   public static Singleton Instance
   {
      get 
      {
         if (instance == null) 
         {
            lock (syncRoot) 
            {
               if (instance == null) 
                  instance = new Singleton();
            }
         }

         return instance;
      }
   }
}
 

이 방식에서는 인스턴스가 필요할 경우에만 인스턴스가 단 한 개만 만들어지도록 합니다. 또한 인스턴스 변수에 액세스할 수 있기 전에 인스턴스 변수 할당을 완료하도록 변수를 volatile로 선언합니다. 마지막으로 이 방식에서는 교착 상태를 방지하기 위해 유형 자체를 잠그지 않고 syncRoot 인스턴스를 사용하여 잠급니다.

이 이중 확인 잠금(double-check locking) 방식은 Instance 속성 메서드의 모든 호출에 대한 독점적인 잠금을 막으면서도 스레드 동시 발생의 문제를 해결합니다. 이 방식에서는 또한 개체가 처음으로 액세스될 때까지는 인스턴스화를 연기할 수 있습니다. 실제로 응용 프로그램에는 이러한 유형의 구현이 거의 필요 없습니다. 대부분의 경우는 정적 초기화 방식만으로 충분합니다.

결과

C#에서 싱글톤(Singleton)을 구현할 경우 다음과 같은 이점과 단점이 있습니다.

이점

  • .NET Framework에서는 정적 변수 초기화가 언제 어떻게 일어나는지를 명시적으로 정의하므로 정적 초기화 방식이 가능합니다.

  • 위의 "멀티 스레드 싱글톤(Singleton)"에서 이미 설명한 이중 확인 잠금(Double-Check Locking) 관용구가 공용 언어 런타임에서 올바로 구현됩니다.

    단점

    멀티 스레드 응용 프로그램에 명시적 초기화가 필요한 경우는 스레딩 문제를 예방하기 위해 미리 조치를 취해야 합니다.

    참고 자료

    [Gamma95] Gamma, Helm, Johnson 및 Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995.

    [Lea99] Lea, Doug. Concurrent Programming in Java, Second Edition. Addison-Wesley, 1999.

    [Sells03] Sells, Chris. "Sealed Sucks." sellsbrothers.com News. 다음 웹 사이트에서 구할 수 있습니다: http://www.sellsbrothers.com/news/showTopic.aspx?ixTopic=411

    참고: 제목과는 무관하게 "Sealed Sucks" 기사는 클래스를 sealed로 표시할 때의 장단점에 대해 실제로 설명합니다

    출처 : Tong - nicekiller77님의 C#통

  • 반응형
    Posted by blueasa
    , |

    Singleton

    Programming/C# / 2010. 7. 2. 16:53

    Definition

    Ensure a class has only one instance and provide a global point of access to it. 

    클래스는 오직 하나의 인스턴스만을 가질 수 있으며, 인스턴스에 접근할 수 있는 하나의 글로벌 포인터를 제공한다.

     

     UML Class Diagram

     

    Participants

    The classes and/or objects participating in this pattern are: Singleton   (LoadBalancer)

    • defines an Instance operation that lets clients access its unique instance. Instance is a class operation.
    • responsible for creating and maintaining its own unique instance.

    이러한 패턴으로 클래스들과 객체들이 참여하는것을 싱글톤(Singleton)이라 한다.

    • 클라이언트들이 유일한 인스턴에 접근하는 하나의 인스턴스 연산을 정의한다. 인스턴스는 클래스 연산(Operation)이다.
    • 자신의 유일한 인스턴스를 생성하고 유지하는 것에 대해 신뢰할 수 있다.

     

    < 글/ 이미지지/ C# 소스 출처 : http://www.dofactory.com/ >

     

    Example

    Singleton 패턴은 객체의 인스턴스가 유일한 즉, 단 하나의 인스턴스만을 생성한다. 그리고 전연 변수와 같이 어디서든지 유일한 인스턴스에 접근하여 사용하는 패턴이다. 단 하나의 유일한 인스턴스만을 가지고 있어 중요한 자원을 관리하는 객체의 경우 편리하다.

    사실 그냥 전역 변수를 사용해서 관리하는 것도 좋지 않나 라고 생각도 했지만, 전역 변수를 사용하는 목적이 어디에서든 접근 가능하다는 목적도 있는 반면에 중요한 자원을 관리함에있어 인스턴스를 이용하여 자원을 관리를 한다. 그래서 그 인스턴스가 유일하다는 것을 보장해 줄 때 사용할 수 있다. 다른 예로, 실생활에서는 대통령은 하나이며 그 유일한 대통령에 의해 임무가 수행된다. 하지만, 대통령 즉, 인스턴스가 여러군대 생성된다면 이것은 혼란을 야기시킬것이다. 이러한 문제를 미리 방지 하고자 Singleton을 사용한다.

     

    Singleton은 기본적으로 다음과 같은 구조로 되어있다.

    1. public class Singleton
      {
           private static Singleton singleton;
           private Singleton()
           {         
           }
    2.  
    3.      public static Singleton getInstance()
           {
    4.  if(Singleton == null)
    5. singleton = new Singleton();
    6.           return singleton;
           }
      }

     

    위 코드에서 생성자를 보자. public이 아닌 private 로 선언되었다.  보통 Singleton ex = new Singleton() 으로 인스턴스로 생성하지만, 이렇게 여러 곳에서 인스턴스가 생성되는 것을 막기 위해 private 로 선언되었다.

    그래서 다음과 같이 이 인스턴스를 사용할 수 있다.

    1. Singleton ex = Singleton.GetInstance();

     

    Sample Code In C#, C++Singleton.zip <- Click Here


    출처 : http://blog.naver.com/fish19?Redirect=Log&logNo=120051709260

    반응형

    'Programming > C#' 카테고리의 다른 글

    Windows Forms FAQ - Windows Forms  (0) 2010.07.02
    C# 싱글톤  (0) 2010.07.02
    창크기 조절 막기  (0) 2010.07.02
    파일 드래그&드롭  (1) 2010.06.30
    Form-Form 데이터 전달  (0) 2010.06.29
    Posted by blueasa
    , |



    폼-속성-FormBorderSyle에서 FixedSingle이나 FixedDialog를 선택하면 됩니다.

    추가로 MaximizeBox/MinimizeBox도 꺼주시면 좋겠죠.

    창크기 조절 안되게 막으려는 용도니..

    - 설정하면 만들어지는 소스 -
     this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog;
    반응형

    'Programming > C#' 카테고리의 다른 글

    C# 싱글톤  (0) 2010.07.02
    Singleton  (0) 2010.07.02
    파일 드래그&드롭  (1) 2010.06.30
    Form-Form 데이터 전달  (0) 2010.06.29
    .NET CF에서 WndProc 사용법  (0) 2010.06.29
    Posted by blueasa
    , |

    먼저 데이터를 받을 폼이나 콘트롤의 AllowDrop 프로퍼티를 True 로 합니다.

    그런 다음 DragEnter Event에서 데이터를 확인하시고

    DragDrop event에서 실제 처리를 하시면 됩니다.

     

    이 예제는 다른 프로그램에서 파일을 드래그해서 리스트에 떨구면 파일을 추가하는 예제입니다.

     

            private void listBox1_DragEnter(object sender, DragEventArgs e)
            {
                    if (e.Data.GetDataPresent(DataFormats.FileDrop))
                    {
                        e.Effect = DragDropEffects.Copy;
                    }
                    else
                    {
                        e.Effect = DragDropEffects.None;
                    }
            }

            private void listBox1_DragDrop(object sender, DragEventArgs e)
            {
                try
                {
                    if (e.Data.GetDataPresent(DataFormats.FileDrop, false))
                    {
                        string[] fileNames = (string[])e.Data.GetData(DataFormats.FileDrop);
                        foreach (string fileName in fileNames)
                        {

                            // 여기서 기타 파일에 대한 처리를 해주시면 됩니다.
                            listBox1.Items.Add(fileName);
                        }
                    }
                }
                catch (System.Exception ex)
                {
                    //예외
                }
            }

    출처 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=17&MAEULNo=8&no=95686&ref=95686

    반응형

    'Programming > C#' 카테고리의 다른 글

    Singleton  (0) 2010.07.02
    창크기 조절 막기  (0) 2010.07.02
    Form-Form 데이터 전달  (0) 2010.06.29
    .NET CF에서 WndProc 사용법  (0) 2010.06.29
    C# 주의해야 할 문법(dispose,using,close)  (2) 2010.06.28
    Posted by blueasa
    , |

    데브피아 윤성님의 팁입니다.

    저는 보통 폼끼리 참조하여 사용해왔는데.. 이쪽이 훨씬 마음에 드네요.. :)

     

     

    ------------------- 이하 원문

     

    이글을 적게되는 이유의 하나는 지속되는 질답란의 질문들이며

    또 하나의 이유는 아래의 허접한 하나의 생성에 대해서 감사 댓글이 달렸기 때문이다.

     

    사실상 이걸 적어볼까 하고 생각한지가 일년은 훌쩍 넘은듯 싶다.

    귀찮기도 하고 별게 아닌거라서 뭐하자는 이야기인지 허접스럽기도 하고 말이다.

     

    사실상 아래의 "하나의 생성"인지.. 글에 대략 이건 옳지 못합니다. 클래스 생성이 낭비됩니다등의 댓글이 달릴것이라 예측했다.

    또는 openform 비교가 아닌 indexer적인 접근으로 null 비교를 하지 않는가 하는 의문이 달리지 않을까 했는데.. 적어놓은 시간이 짧아서인지 그런글은 없는듯싶다.

     

    -------------------------------

     

    Form collection 존재한다는 사실을 알고 있는이는 알고 있는데도 불구하고

    사람들은 이것을 이용하지 않는듯 싶다.

    (?)인지는 익숙하지 않아서 그런것이라.

    MS에서는 FromCollection ControlCollection Indexer까지 구현해서 만들어 두었음에도

    대부분의 경우 무식하게 Indexer 사용하지 않고 Loop 돌리거나 해서 찾는경우가 많다.

     

    아래의 ( 하나생성) 또한 그런 오류를 범한것중 하나이다.

    (과연 그럴까?.... 나름 이유가 있을까? 이유는 게을러서? 이지 않을까 싶다만.. 죽어도 잘난척이란..)

     

    Form Collection 있다는 사실 또는 이것을 이용할줄 모르는 사태로 인해서 가끔식은 FrameWork이라고 만든것들은 임의로 자신의 FormCollection 구현하고 이로인해서 오히려 쉬운길을 두고 어렵게 가기도 한다.

     

    기초적인것이면서 한줄짜리? 지식이라

    사실 이렇게 글을 적는게 오히려 민망하기도 하고 황망스럽기도 하나..

     

    몰라서 저지르는 실수들이 너무 많아서 이렇게 펜을 들어본다( 그러는 나는 알고 있을까나?)

     

    일단.. 기초과정을.. 밟아 보도록 하자..

     

    1. Indexer이용하기.

    이렇게.. 폼을 두개를 만들자.

     

    이런식으로.. 두개의 폼을.. 만들도록 하자.

    Form2 닫게 하려면 어떻게 하는가?

     

    Form2 닫지를 못하는 사람? 자진납세하자.(댓글함 적어다오 얼마나 많은지 파악이 안된다.)

    문제의 시작은 이것이다. Form2 생성했는데 어떻게 하질 못하겠다는것이다.

    Form2 생성은 이렇게 했다.

    결국 문제를 해결하기 위해서.. 어떤이는?...

     

    Form1 클래스의 변수로.. Xx 선언한다. 일종의 전역변수같은거 말이다.

    다른경우는 프로젝트의 자체 컬랙션에서 처리한다.

     

    두가지 나쁜 방법은 아니다.

    단지.. 내가 의문을 지니는것은 그럴까? 하는것뿐이다.

     

    Form2 닫는건 이렇게 적으면 된다.

     

    그렇다. 내가 그랬지… 한줄짜리 지식이라고..

    한줄 적어주고 어지간히 아는척이 많다.

     

    .. 이제는 FW 경우를 살펴보자.(FW FrameWork 준말로 사용한다. )

    솔류션을 이렇게 만들고 FW에서 기준폼으로 MyForm 만들고

    Form3 Myform 상속받았다고 치자.

     

    요렇게 상속받음..

     

    Myform 필요도 없지만.. 행여나 보여주자면..

    이런식이라고 보자.

     

    이런식이 될때 FormCollection.. 과연 Form3 가질까 하는것이다.

    대답은.. 모든 Form개체 상속받은것들은.. 무조건 Show할때.. FormCollection 들어간다.

    그럼 반대로 Hide하면 어떻게 될까? 사라질까?

    대답이 뭐일것 같은가? 귀찮아서 그냥 알려준다만.. 사라지기 전까지는 들어가 있다.

    hide 여전히 존재한다. Close 호출되어야 사라진다.

    다시 강조해서 말하자면.. Show할때 들어가고..(그냥 생성한다고 들어가진 않는다.)

    (여유되시면 reflection으로 show 까보면 좋겠다만.. 귀차니즘의 끝은 어디에 있는지 누가 해다오)

     

    하여간.. 위의 예제에서 말하고자 하는 이야기는

    FW 만들더라도 굳이 컬랙션을 만들지 마라.. 피곤하다.

    (말은 이렇게 하지만 대부분의 FW에서 필요하긴 하다. Form 경우 특정 영역성이 필요하게 되는데 일반적으로 기준 Form(위에서 말한 Myform) 에서 Show메세드의 오버로딩을 통해서 영역을 설정하곤 한다. / 또한 대부분의 경우 Dictionary같은 지네릭타입으로 해서 "as" 적지 않고 처리하길 원한다.)

     

    이제는 FW 사용하는 이들은 의문을 가질것이다. 그럼 Myform 상속받은것은 어떻게 구분하냐고?

    "is" 녀석은 그냥.. 있는게 아니다. 사용하면 될꺼 아닌가

    한줄이지만. 굳이 예제를 만들기도 귀찮은 관계로.. 패스다. 모른다고 누가 손든다면... 너무한거 아냐? 라고 말하고 싶다.

    안되면 확장메서드 하나 만들어 버리던지..

     

    . 이제 본론에 들어가야 하는데 폼에게 특정한 메시지를 전송한다는것 자체가 무의미한 수준까지 왔다.

    이미 눈치 코치 알아버렸듯이 할것이 없다.

     

    그래도 행여나 하는 맘으로 몇자 적어보자.

     

    나같은 경우는 Form에서 Form으로 특정메세지나 무시기를 전달하기 위해서 SendMessage같은 API 써본적은 없다. 물론 Domain 다른 Application이라면 어쩔수 없겠지만..

    그런경우만..WinProc override해서 처리하면 된다.

    이럴때도 광역인지 뭔가 방법이 있을법도 하다만.. 내가 모르는덕분에.. 이건 배워야 할듯 싶다.

     

    계속 주제를 어긋나는데 주제에 집중하자.

    하여간 나의 경우는 Interface 정의해서 사용한다.

     

    심풀하게 설명해서 이렇게 인터페이스를 만들어 보자.

     

     

    그럼.. 이걸 가지고.. Form2에게 이녀석을 구현하도록 해보자.

    이렇게 구현하고 나서..

     

    이지의 좌측 아래에 있는 버튼을 누르면.. 이렇게 실행이 된다.

    .. Form2 에게 궁극적으로 메시지를 보냈다.(물론 헛소리다.)

    .. From2 매서드를 단지 실행한것에 불과하다 args넣어서.

     

    소스야 앞서 이야기 했듯이 is 사용하면 된다.

     

    이렇게.. 물론 이것의 응용(?)으로 앞서 이야기 했듯이  Indexer 입력받아 바로.. Is 인터페이스 체크하고 실행하는 방법도 있겠다. (업무에 따라서..)

    응용은 알아서 하길 바란다. 계속 곁가지로 나가면.. 사실 적을게 너무 많다.

     

     

    위와 동일한 형태지만.. Myform 에서 가지고 하는걸 보자.

    (이건 필요 없을것 같다..... 그냥 대충 대충 진행하자.. 생략해도 좋다.)

    FW 경우에는 이렇게 해야될것이다.

    우리가 사용할 녀석에게 당연히 상속을 받아 구현하게 해야할것이다.

     

    여기서 주의깊게 봐야 하는것은 상속받고 구현하고.. 그리고 Virtual이라고 적어두는것이다.

    그래야… 이녀석을 다시 상속받는 녀석들에게.. Override 할수 있도록 하기 위함이다.

    아차차.. virtual앞에 public 붙쳐야되는데 얼른 적다보니 빠뜨렸다. 알아서 넣자.

     

    하여간..

     

    한줄짜리 기초가 이렇게 많은 글을 적어야 되는지는 .. 당연히 설명하는 능력의 부재이겠지만..

    이게 나름 적기 시작하면 무지 적을게 많기도 하다.( 뻥이다. 귀찮아서 적지 않을뿐이다.)

    여하튼 인터페이스에 정의하는것들로는  Clear같은거라든지.. 여타 몇몇개는 고정적으로 사용되는것들이 있는데 그런것들도 알아서 하도록 하자..

    반응형

    'Programming > C#' 카테고리의 다른 글

    창크기 조절 막기  (0) 2010.07.02
    파일 드래그&드롭  (1) 2010.06.30
    .NET CF에서 WndProc 사용법  (0) 2010.06.29
    C# 주의해야 할 문법(dispose,using,close)  (2) 2010.06.28
    Handling Application Events: On Idle  (0) 2010.06.28
    Posted by blueasa
    , |

    .NET 기반에서 C#으로 윈도우메세지를 핸들링하기 위해서는 WndProc를 오버라이드하여 사용하는 것으로 많이 알려져 있습니다. 하지만 .Net CF 에서는 Form클래스에 WndProc메소드가 기본적으로 존재하지 않습니다. 대신에 MessageWindow클래스를 이용하여 WndProc를 오버라이드하면 윈도우 메세지를 핸들링할 수 있도록 지원해줍니다.

     

    그럼, 간단하게 폼 위에서 마우스가 이동하면, 이때 마우스 좌표값을 윈도우 메세지 형태로 날려서

    이 값을 Form의 Text속성에 표시하는 예제를 함께 구현해 보도록 하지요.

     

    우선 MessageWindow 클래스 사용을 위해서는 다음과 같이 참조추가를 해주어야 합니다.

     

     

     

    사용하기 원하는 코드에서

     

    using Microsoft.WindowsCE.Forms;

     

    이제 MessageWindow를 사용할 수 있게 됩니다.

     

    아래는 기본적으로 프로젝트 생성시에 생성되는 폼(frmMain)에서

    MessageWindow를 상속받아 WndProc를 오버라이드한 클래스를 이용하는 코드입니다.

    굵은 부분을 참고하면 어떤식으로 윈도우메세지를 핸들링하는지 흐름을 파악할 수 있을 겁니다.

     

    frmMain 코드 - 프로젝트 생성시 생성되는 기본 폼

     

        public partial class frmMain : Form
        {
            MsgWindow MsgWin = null;

     

            public frmMain()
            {
                InitializeComponent();

                this.MsgWin = new MsgWindow(this);
            }

     

            //마우스 이동시에 메세지를 생성하여 날려주는 작업을 해주겠습니다.

            protected override void OnMouseMove(MouseEventArgs e)
            {
                Message msg = Message.Create(MsgWin.Hwnd, MsgWindow.WM_CUSTOMMSG,

                                                                (IntPtr)e.X, (IntPtr)e.Y);
                MessageWindow.SendMessage(ref msg);
                base.OnMouseMove(e);
            }

           

            //MsgWin을 통해 현재 폼에 접근할 수 있도록 메소드를 하나 정의합니다.

            public void RespondToMessage(int x, int y)
            {
                this.Text = "X = " + x.ToString() + ", Y= " + y.ToString();
            }
        }

     

     

     

     MesssageWindow를 상속받아 WndProc를 오버라이드

     

        public class MsgWindow : MessageWindow
        {
            public const int WM_CUSTOMMSG = 0x0400;

            private frmMain msgform;

     

            public MsgWindow(frmMain msgform)
            {
                this.msgform = msgform;
            }

     

            protected override void WndProc(ref Message msg)
            {
                switch (msg.Msg)
                {
                    case WM_CUSTOMMSG:
                        this.msgform.RespondToMessage((int)msg.WParam, (int)msg.LParam);
                        break;
                }
               
                base.WndProc(ref msg);
            }
        }

     

    반응형
    Posted by blueasa
    , |

    이번 내용은 C# 주의해서 보아야 할 것들에 대한 정리 두번째 강좌로서 OOP 관련한 것들을 순서없이 정리한 것입니다.

    C#과 OOP
    C# 클래스는 메소드, 속성외에 인덱서, 프로퍼티, 생성자, 연산자, 델리게이트, 이벤트 등을 포함한다.


    [static과 instance]
    Static 키워드와 함께 선언된 필드는 정적 필드 혹은 정적 데이터로 불려진다. 그리고 static 으로 선언되지 않은 필드들은 인스턴스 필드 혹은 인스턴스 데이터로 불려진다.
    Static 키워드는 액세스 한정과는 아무런 상관이 없다.(private static/public static 모두 가능)

    인스턴스 메소드도 정적 메소드와 같이 메모리에 한번만 저장되고 클래스와 전체적으로 연관되어 있다.

    정적으로 선언된 메소드에서는 인스턴스 필드에 액세스할 수 없다.

    참조는 메모리의 주소값을 나타내는 32비트의 숫자이므로 참조를 복사하거나 비교하는 것이 성능면에서 봤을 때 아주 우수하다. 그러므로 되도록이면 참조를 사용하는 것이 좋다.

    [상속]
    인터페이스 상속/ 구현상속
    기본클래스/수퍼클래스, 파생클래스/서브클래스
    상속의 원칙
    파생클래스에 필드, 메소드, 프로퍼티 등 어떠한 형식의 멤버들도 새롭게 추가할 수 있지만, 기본클래스에는 정의되지 않아야 그렇게 할 수 있다.
    기본클래스에 정의된 메소드나 프로퍼티의 구현을 교체할 수 있다.(프로퍼티는 구현을 가지지 않는다.) 메소드의 새로운 구현이 이전의 구현을 오버라이드(Override) 한다.
    기본클래스에 대한 메소드를 오버라이드할 때는 반드시 override 키워드 사용

    기본클래스의 메소드가 가상(virtual)으로 선언되지 않으면 그 메소드를 override 할 수 없도록 한다.
    각 파생클래스는 오직 하나의 기본 클래스로 부터만 상속 받을 수 있다.

    [Object 클래스]

    메소드 액세스한정 역할
    string ToString() public virtual 객체의 문자열 표현을 반환한다.
    int GetHashTable() public virtual 어떤 테이블에서 객체의 인스턴스를 효율적으로 찾아볼 수 있도록 하기 위해 디잔인된 객체의 해시 값을 반환한다.
    bool Equals(object obj) public virtual 객체의 두 인스턴스를 비교하여 같은 인스턴스인지 판단한다.
    bool Equals(object objA, objB) public static 객체의 두 인스턴스를 비교하여 같은 인스턴스인지 판단한다.
    bool ReferenceEquals(object objA, object objB) public static 두 참조가 같은 객체를 참조하고 있는지 비교한다.
    Type GetType() public 객체의 데이터 형식을 반환한다.
    object MemberwiseClone() protected 객체의 복사본을 만든다.
    void Finalize() protected virtual 리소스를 해지하기 위한 상황에서 사용한다.


    object라 함은 System.Object를 의미한다.
    object 키워드는 변수나 인수의 데이터 형식을 선언할 때와 같인 클래스가 무엇을 의미하는지 명시해 줄수 없을 때도 사용 가능하다.

    System.Object.GetType은 가상으로 정의되지 않았기 때문에 ToString()과는 달리 GetType()을 오버라이드 할 수 없다.

    [가상 메소드와 비가상 메소드]
    이 둘의 차이점은 메소드가 실행 시에 호출되는 방식이다.

    메소드가 가상이 아니라면, 컴파일러는 단순히 참조가 선언된 데이터 형식을 사용한다.

    만약 메소드가 가상이면 컴파일러는 참조가 실제로 무엇을 가리키고 있는지 실행 시에 점검하는 코드를 만들어 낸다. 그리고 이 인스턴스가 어떤 클래스가 인스턴스인지를 구분하여 적절한 메소드 오버라이드를 호출한다. 예를 들어 가상 메소드가 100번 반복되는 루프 안에서 호출된다면 이 프로그램은 참조 변수가 참조하고 있는 인스턴스 형식을 100번 검사해야 한다. 왜냐면 매번 반복 수행할 때마다 참조는 다른 인스턴스를 가리킬수 있기 때문이다.

    [메소드 숨기기]
    파생클래스에서 new 키워드를 사용하여 동일한 signature를 가진 기본클래스의 메소드를 숨길 수 있다.

    [추상 함수와 기본클래스]
    클래스가 추상으로(abstract)으로 선언되면 인스턴스화될 수 없다
    메소드를 추상으로 정의하면 이는 파생클래스에서 오버라이드 된다고 가정하므로 구현될 필요 없다.
    클래스내의 어떤 메소드가 추상이면 클래스 자신도 추상이어야 한다.
    추상클래스로부터 파생된 비추상 클래스는 반드시 추상 메소드를 오버라이드해야 한다.

    [sealed 클래스와 sealed 메소드]
    추상클래스나 추상메소드의 반대 개념
    상속되거나 오버라이드 될 수 없다.

    [기본 클래스의 메소드 호출하기]
    base 키워드 사용

    [상속]
    메소드 뿐만 아니라 구현된 다른 클래스 멤버도 오버라이드 할 수 있고 숨길수 있다. 즉 프로퍼티도 원한다면 가상으로 선언할 수 있고, 오버라이드할 수 있다.
    필드는 가상으로 선언되거나 오버라이드될 수 없다. 하지만 파생 클래스에 동일한 이름의 다른 필드를 선언함으로써 기본 클래스의 필드를 숨길 수는 있다.
    정적 메소드를 가상으로 선언할 수는 없지만 인스턴스 메소드처럼 숨길 수는 있다.
    T형식으로 선언된 참조 변수가 T로부터 파생된 클래스의 어떤 인스턴스를 참조할 수 있다. 그렇다고 해서 T내에 선언되지 않은 어떤 멤버를 그 변수를 통하여 참조할 수 있는 것은 아니다.

    [메소드 오버로딩(Overloading)]
    오버로딩과 오버라이드는 아무 상관 없다.
    메소드 오버로딩은 상속이나 가상 메소드와는 아무런 상관이 없다.
    매개변수를 명시적으로 형식 변환하여 오버로드가 가지는 매개변수의 데이터형식과 정확하게 일치시켜 주는 것이 좋다.
    오버로드의 signature가 달라야 한다. 단지 반환형식만이 다른 것이 아니라, 매개변수의 이름과 데이터 형식이 달라야 한다.
    두개의 메소드가 실제로 다른 일을 하는 우에는 오버로딩을 사용하면 안된다.
    기본매개변수 public doSomeThing(int x, int y=10)과 같은 문법을 사용할 수 있는 VB/VC++ 과는 달리 C#에서는 이것이 가능하지 않아 메소드 오버라이딩을 사용해야 한다.

    [생성(Construction)과 정리(Disposal)]

    생성자는 그것을 포함하고 있는 클래스아 같은 이름으로 선언되면 어떠한 반환 형식도 가질 수 없다.
    클래스 안에 어떠한 생성자도 정의하지 않았다면 컴파일러는 암시적으로 기본적인 초기화를 클래스 인스턴스 내의 멤버 변수에 적용시킨다.

    생성자의 다른 용도로서 클래스의 인스턴스가 얼마나 많이 생성되었는가를 세는 것이다.

    Public class Authenticator
    {
    private static unit nInstancesCreated=0;

    public Authenticator(string initialPassword)
    {
    ++nInstancesCreated;
    Password = initialPassword;
    }
    private string Password;
    private static uint minPasswordLength = 6;
    }


    정적생성자
    클래스에 어떤 매개변수도 받아들이지 않는 정적 생성자를 정의할 수 있다. 클래스의 객체가 생성되지 않고 단 한번만 수행된다. 필요한 이유는 정적 변수의 값을 초기화하기 위해서이다.



    class Authenticator
    {
    static Authenticator()
    {
    minPasswordLength=6;
    }
    public Authenticator()
    {
    Password = “lskdflsdf’;
    }
    public Authenticator(string initialPassword)
    {
    Password = initialPassword;
    }
    private string Password;
    private static uint minPasswordLength;
    }

    정적 생성자가 클래스의 인스턴스 멤버에 액세스할 수 없고 정적 멤버에만 액세스할 수 있다는 것은 매우 당연하다.
    두개 이상의 정적 생성자를 가진 클래스를 작성하는 경우에는 어떤 정적 생성자가 먼저 실행해야 하는지가 정의되지 않는다. 그러므로 이미 실행되었거나 앞으로 실행될 다른 정적 생성자에 의존하는 코드를 정적 생성자 안에 넣어서는 안된다.

    [상수(const) 필드와 읽기전용(readonly) 필드]
    둘 다 값이 바뀔 수 없는 상수로 간주된다.
    const
    상수는 정의될 때 값이 결정되어야 한다.
    Public const int MaxPasswordLength=20;
    Public const int MaxPasswordLength; //not permitted!!
    상수는 암시적으로 정적(static)이다. 명시적으로 static 선언은 허용안함
    readonly
    상수 보다 좀더 나은 유연성을 제공
    컴파일 시에는 값을 결정할 수 없고 실행 시에 계산되는 어떤 결과값을 초기값으로 결정해야 하는 경우 사용한다.
    클래스의 각 인스턴스 마다 다른 값을 가질 수 있으므로 정적이 아니고 정적으로 선언하고 싶으면 명시적으로 static 선언해야 함

    public class Authenticator
    {
    public readonly DateTime CreationDate;
    public static readonly uint MaxPasswordLength;
    static Authenticator()
    {
    MaxPasswordLength=20;
    }
    public Authenticator()
    {
    // 클래스의 인스턴스에 따라 달라질 수 있다.
    CreationDate = new DateTime(2001,1,1)
    }
    }


    다른 생성자로부터 생성자를 호출하기

    public Authenticator() : this(“lskdfdfd”)
    {
    } // 다른 생성자가 먼저 실행되어야 함을 명시한다.


    계층에 매개변수를 가지지 않는 생성자 추가하기
    아무 매개변수도 받아들이지 않는 생성자를 추가시켜서 기본 생성자를 교체

    public GenericCustomer()
    : base() //생략가능
    {
    name = “
    ”;
    }

    base 키워드와 this키워드는 다른 생성자를 호출하기 위한 유일한 키워드이다.

    [정리하기 : 소멸자(Destructor)]
    C#은 Dispose()나 Close(), Finalize() 메소드를 지원한다. 이 메소드들은 함께 작동하도록 디자인되었다.
    Finalize()
    리소스를 해지하는 데 사용되는 선택사항 중 하나인 Finalize()는 고전적인 소멸자와 가장 근접한 개념이다. Finalize() 라는 메소드를 클래스에 정의하면 이 메소드는 클래스 인스턴스가 소멸될 때 자동으로 호출된다.
    Finalize()는 결정적이지 않다. 즉 일반적으로 언제 인스턴스가 소멸될지 모르기 때문에 언제 Finalize()가 호출될지 예측할 수 없다.
    필요에 다라 System.GC.Collect()를 호출함으로써 어떤 시점에서 가비지 켈렉터를 강제로 작동시킬 수 있다.
    일반적으로 꼭 필요한 경우가 아니면 이 메소드를 구현하지 않을 것을 권한다.

    Dispose()와 Close()
    C#은 Dispose() 메소드에 관한 아주 많은 지원을 하고 있다.
    이것의 장점은 어떤 리소스가 더 이상 필요 없어진 즉시 해지된다는 것이지만 클라이언트 코드가 알아서 호출해 주어야 한다.

    Public void Dispose()
    {
    //리소스 정리
    GC.SuppressFinalize(this);
    }
    protected overrie void Finalize()
    {
    //리소스 정리
    base.Finalize();
    }

    클라이언트 코드가 Dispose()를 호출할 것을 기억하고 있다면 리소스는 제때에 해지된다.
    System.GC 클래스의 SuppressFinalize() 메소드는 매개변수로 전달된 객체가 더 이상 완료화될 필요가 없다는 것을 .NET 런타임에게 알리는 역할을 한다. 완료화(finalization)라는 것은 객체에 대한 가비지 컬렉션이 행해질 때 Finalize() 메서드는 호출되지 않고, 이것과 관련된 어떠한 성능 저하의 문제도 일어나지 않는다.

    Close()대 Dispose()
    Close()와 Dispose()의 차이점은 주로 규약상의 차이이다. Close()는 이 리소스가 나중에 다시 사용될 수 있음을 내포하고 있는 반면 Dispose()는 완료의 의미를 좀더 내포하고 있다.
    즉 Dispose()를 호출하면 클라이언트가 이 특정 객체에 대한 사용을 완전히 종료함을 의미한다.

    IDisposable
    기본적으로 C#은 객체의 참조가 범위 밖으로 나갔을 때 자동으로 그 객체의 Dispose()(Close()는 해당되지 않는다.)가 호출되도록 하기 위한 문법을 제공한다.


    Class ResourceGobbler : IDisposable
    {

    public void Dispose()
    {
    }
    }
    {
    ResourceGobbler TheInstance = new ResourceGobbler();
    // 처리
    TheInstance.Dispose();
    }
    =
    using (ResourceGobbler TheInstance = new ResourceGobbler())
    {
    //처리
    }

    using 구조는 사용하지 않는 것이 좋다.
    IDisposable로부터 클래스를 파생시키면 반드시 Dispose() 메소드를 구현해야 한다.

    Sample
    Class DataStoreConnection : IDisposable
    {
    private int DataStoreHandle = 0;
    private bool CanOpen = true;
    private readonly string name;

    public DataStoreConnection(string name)
    {
    this.name = name;
    }
    public void Open()
    {
    if (CanOpen == false)
    Console.WriteLine(name + “: Error: Attempt to Open after calling Dispose()”);
    If (DataStoreHandle ==0)
    {
    DataStoreHandle = 1;
    Console.WriteLine(name + “: Connected to DataStore”);
    }
    else
    Console.WriteLine(name + “: Error: Already connected to DataStore”);
    }
    public void Close()
    {
    DataStoreHandle = 0;
    }
    public void Dispose()
    {
    Console.WriteLine(“Disposing: “ + name );
    CanOpen = false;
    Close();
    GC.SuppressFinalize(this);
    }
    ~DataStoreConnection()
    {
    Close();
    }
    }

    Close()메소드가 한번 이상 호출되어도 아무런 오류가 발생하지 않는데 이런 방식으로 코딩하는 것이 바람직하다.
    Dispose()는 Close(0와 동일한 일을 하는데 Dispose()를 호출하는 것은 이 객체 사용의 완전한 긑을 맺었다는 것을 의미하므로 이것은 완료화를 수행하지 못하도록 SuppressFinalize()를 호출한다.(여기서 완료화란 Finalize() 메소드를 호출하는 것을 의미)
    Dispose()는 또한 CanOpen 필드를 false로 설정하여 이 객체가 데이터저장소로 연결할 수 없도록 명시한다.
    최종적으로 Finalize() 메소드(소멸자)는 연결을 닫는다.
    Finalize() 메소드에서 콘솔에 메시지를 출력할 수 없다. 가비지 컬렉터는 콘솔 윈도우에 액세스할 수 없기 때문에 우리가 close() 메소드에서 콘솔에 메시지를 출력하려고 하면 실행시 오류가 발생한다.
    [메모리]
    Stack에 저장된 데이터(값 형식)는 scope를 벗어나면 소멸되고 다시 그 자리에 새로운 변수를 위한 공간으로 사용되는 중첩이 성립된다.
    New 연산자에 의해 Heap에 저장된 데이터(참조 형식)는 수명이 가비지 컬렉터에 의해 소멸될때까지 유지된다.

    가비지 컬렉터에 의한 압축은 관리 힘이 기존의 비관리 힘에 비해 가지는 차이점이다 페이지 스와핑이 적게 일어나므로 굉장히 빠르다.

    만약 값 형식이 참조 형식의 일부분(예를 들어 배열의 element나 클래스의 멤버)으로 정의된다면 참조형식을 저장하고 있는 데이터 내부인 힙의 인라인으로 저장된다.

    [구조체(Struct)]

    구조체는 값 형식이다.
    구조체는 상속을 지원하지 않는다.
    구조체의 생성자는 동작하는 방법이 약간 다르다, 특히 컴파일러는 항상 매개변수를 받아들이지 않는 기본 생성자를 제공하는데, 개발자는 임의로 이것을 변경할 수 없다.

    구조체를 메소드의 매개변수로 전달하거나 어떤 구조체를 다른 구조체에 대입하는 경우에는 성능 면에서 좋지 않은 결과를 초래한다. Ref 매개변수로 전달하면 이러한 성능저하를 막을수 있다.
    구조체의 어떤 멤버를 가상으로 선언하는 것은 불가능
    구조체는 암시직으로 sealed로 간주되므로 그렇게 선언할 필요 없다.
    구조체에도 생성자를 선언할 수 있는데 아무런 매개변수도 받아들이지 않는 생성자는 선언할 수 없다.
    구조체도 Close(), Dispose() 메소드를 가질수 있다. 하니반 Finalize()는 지원되지 않는다.

    [인덱서]
    객체를 마치 배열처럼 다룰 수 있게 한다.
    인덱서는 프로퍼티와 매우 비슷한 방식으로 get함수와 set함수를 이용하여 정의된다. 인덱서의 이름은 this이다.

    Struct Vector
    {
    public double x,y,z;
    public double this[int i]
    {
    get
    {
    switch(i)
    {
    case 0:
    return x;
    case 1:
    return y;
    case 2:
    return z;
    default:
    throw new IndexOutOfRangeException(“Attempt to retrieve Vector element ” + i)
    }
    }
    set
    {
    switch(i)
    {
    case 0:
    x=value;
    break;
    case 1:
    y=value;
    break;
    case 2:
    z=value;
    break;
    default:
    throw new IndexOutOfRangeException(“Attempt to retrieve Vector element ” + i)
    }
    }
    }
    }

    foreach 루프에서는 인덱서를 사용할 수 없다. Foreach 명령문은 각 항목을 배열이 아닌 컬렉션을 간주하므로 다른 방식으로 작동한다.

    [인터페이스]
    인터페이스 멤버는 항상 public이고 가상이나 정적으로 선언될 수 없다.
    인터페이스는 일종의 계약역할을 한다.
    클래스가 컬렉션에 필요한 인터페이스를 구현한다고 선언을 해야(System.Collections.IEnmerable), 이것이 지정 컬렉션이라고 생각할 수 있는 것이다.
    인터페이스를 상속하는 클래스에서 인터페이스의 모든 메소드를 구현하지 않으면 컴파일 오류이다.
    반응형
    Posted by blueasa
    , |

    Handling Application Events: On Idle

    using System;
    using System.Threading;
    using System.Reflection;
    using System.Windows.Forms;
       

    public class HelloWorldForm : Form
    {
        public HelloWorldForm()
        {
            Text = "Hello, WindowsForms!";
        }
    }
       
    public class ApplicationEventHandlerClass
    {
        public void OnIdle(object sender, EventArgs e)
        {
            Console.WriteLine("The application is idle.");
        }
    }
       
    public class MainClass
    {
        public static void Main()
        {
            HelloWorldForm FormObject = new HelloWorldForm();
            ApplicationEventHandlerClass AppEvents = new ApplicationEventHandlerClass();
       
            Application.Idle += new EventHandler(AppEvents.OnIdle);
            Application.Run(FormObject);
        }
    }


    출처 : http://www.java2s.com/Code/CSharp/Development-Class/HandlingApplicationEventsOnIdle.htm           

           
    반응형
    Posted by blueasa
    , |

    안녕하세요 : )

    뭐라고 할 말이 없을만큼 오래간만입니다 ;;;

     

    이제 프로젝트도 거의 종료가 되어가고, 개인적으로 준비하던 일도 어찌어찌 끝이 보이기 시작하는데다가, '아 그럼 예전에 그만뒀던 연재를 시작해볼까!'라고 열의를 불태워볼까 했더니!

     

    ...6사단으로 소집명령이 떨어졌습니다 orz

     

    아직 시간은 좀 있습니다만, 뭔가 멍해져 버려서.. 짧은 아티클이라도 올리고 끌려가자는 마음에 끄적여 봅니다 ;

     

     

    그리고 그려 그리고픈

     

    어떤 언어를 배웠든 간에, 화면에 연속적으로 뭔가를 그려보려는 시도해 봤던 사람은 '어라 이거 왜 화면이 깜박거리냐..'라는 생각을 한 번쯤은 해 봤을 것이다. 이 아티클은 그런 현상을 해결하는 방법을 설명한다.

     

    .Net에서는 GDI+라는 인터페이스를 제공, 그래픽과 관련된 장치를 제어하도록 하고 있다. 보통 이와 관련된 클래스와 기타 객체들은 System.Drawing 네임스페이스를 통해서 제공된다.

     

    준비가 되었다면, 윈폼 기반의 프로젝트를 하나 생성하고 버튼 두 개를 추가한 뒤 다음과 같은 소스를 입력하여 보자 :

     

    private void button1_Click(object sender, System.EventArgs e)

    {

        Graphics graphic = this.CreateGraphics();

        SolidBrush brush = new SolidBrush(Color.DarkRed);

        for(int i=0;i<200;i++)

        {

            graphic.Clear(this.BackColor);

            graphic.FillEllipse(brush, i, 0, 200, 200);

        }

        brush.Dispose();

        graphic.Dispose();

    }

     

    private void button2_Click(object sender, System.EventArgs e)

    {

        Graphics graphic = this.CreateGraphics();

        SolidBrush brush = new SolidBrush(Color.DarkRed);

        Bitmap bitmap = new Bitmap(

            this.Width, this.Height

            );

        Graphics memGraphic = Graphics.FromImage(bitmap);

        for(int i=0;i<200;i++)

        {

            memGraphic.Clear(this.BackColor);

            memGraphic.FillEllipse(brush, i, 0, 200, 200);

            graphic.DrawImageUnscaled(bitmap,0,0);

        }

        brush.Dispose();

        memGraphic.Dispose();

        bitmap.Dispose();

        graphic.Dispose();

    }

     

    둘 다 적색의 원을 오른쪽으로 이동시키며 그리는 코드이지만 버튼 1은 더블 버퍼링을 사용하지 않은 것이고, 버튼 2는 더블 버퍼링을 사용한 코드이다. 코드를 보면 알겠지만, 비트맵으로부터 얻은 Graphics 객체에 그리기와 관련된 모든 작업을 한 후에 비트맵을 폼의 Graphics 객체에 그리는 부분이 추가되어 있다. 이 경우 실제 폼에 대한 그리기 작업은 한 번만 이루어지기 때문에 깜박임이 없어지게 된다.

     

    설명을 보기보다는 실행시켜보면 이 기법을 써야할 이유를 알게 될 것이다 : )

     

    p.s:Dispose() 메서드는 사실 안 써도 별 상관 없지만, GC가 리소스를 빨리 해제해도록 하는데 도움이 되므로 쓰는 습관을 들이는 게 좋다.

     

     

    정리

     

    간단하지만 남들이 잘 알려주지 않는 코드였습니다.

    그럼 : )


    출처 : http://www.gosu.net/GosuWeb/Article-detail.aspx?ArticleCode=955

    반응형
    Posted by blueasa
    , |

    지금 키보드입력이 삽입상태인지 수정상태인지, Caps Lock 켜져 있는지 꺼져있는지를

    GetKeyState API 이용 알아내는 방법입니다.

     

    MFC 그랬듯이 .NET Library Windows API 완전히 표현하고 있지는 않는  같습니다.
    그러나 C#에서 Unmanaged Code 사용할  있으니까 Windows API 직접 사용하면 되겠지요.    


     

    using System.Runtime.InteropServices;
    ...

    [DllImport("User32.dll")]
    public static extern int MessageBox(int h, string m, string c, int type);


    [DllImport("User32.dll")]
    public static extern short GetKeyState(int nVirtualKey);

    private void textBox1_TextChanged(object sender, System.EventArgs e)
    {
         if ((GetKeyState(0x15) & 0x01) == 0x01) MessageBox(0, "Hangul Key", "Sunken", 0);
         if ((GetKeyState(0x14) & 0x01) == 0x01) MessageBox(0, "CapsLock Key", "Sunken", 0);
         if ((GetKeyState(0x90) & 0x01) == 0x01) MessageBox(0, "NumLock Key", "Sunken", 0);

    }

     


     

    자료출처 : http://www.devpia.com/

    반응형
    Posted by blueasa
    , |