반응형
반응형

오늘 업무용메신저를 제작하다가.. 이모티콘처리하는 부분을 구현하였다.

이모티콘이 들어간 리치박스를 동적으로 보여주고.. 이모티콘을 선택하면 시스템 클립보드에 데이터를 배치하고.. 그걸 가져다가 사용하는 식으로 구현을 했는데..

 

이게 왠 일~? 이모티콘을 클릭하고.. 클립보드에 올리려는 순간..

ThreadStateException과 함께 아래 메시지가 등장하며 프로그램이 하늘나라로 떠나버리셨다.

 

 

OLE 호출을 수행하려면 현재 스레드를 STA(단일 스레드 아파트) 모드로 설정해야 합니다. 표시된 STAThreadAttribute가 Main함수에 있는지 확인하십시오.

 

 

나름 프로그램 시작하는 Main메소드에서 [STAThread]라는 문구를 보아왔기에..

동물적 감각으로(?) [STAThreadAttribute]로 고쳐주었다. 결과는 동일하게 프로그램이 죽었다.

검색해보니.. [STAThread]라고 쓰나.. [STAThreadAttribute]라고 쓰나.. 동일한 방식인 것;;

 

그리고는 몇 번의 삽질 끝에 알아낸 해결방법은 아래와 같다.

해당 메소드를 호출하는 쓰레드를 생성할 때..

쓰레드가 시작하기전 다음과 같은 코드를 넣어주면 끝난다.

 

Thread th = new Thread(ThreadProc);

th.SetApartmentState(ApartmentState.STA);

th.Start();

 

오늘도 이렇게 위기를 넘겼다..

반응형
반응형

웹브라우저 컨트롤 이벤트 중에 DocumentCompleted 라는게 있다.

웹문서가 다 읽혀졌는지를 확인해서 특정 처리를 할 수 있다.

근데 문제는 이 이벤트는 딱 한번만 발생하는게 아니라 웹문서내에 프레임이 들어가 있으면 프레임 갯수만큼 반복이 된다.

보기에는 한페이지 처럼 보이지만 실제 속을 들여다 보면 여러개의 프레임으로 구성되어 있다.

 

네이버 페이지만 해도 이 이벤트가 6~7번 발생한다.

그러다 보니 원하는 결과를 제대로 얻을 수가 없다.

 

웹페이지가 모두 로딩되었는지 체크하고 원하는 작업 실행하기

 

 

    private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {

            // 아래 부분은 한번만 발생
            if (e.Url.AbsoluteUri == webBrowser1.Url.AbsoluteUri)
            {
                GetSearchData(); // 원하는 작업 실행
            }
    }

 

 

 

 

아래는 다른 방법

 

 
    // 문서로드 완료 이벤트
    private void webBrowser1_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
            // 문서가 정상적으로 로드 완료 되었을 경우만 실행
            if (this.webBrowser1.ReadyState == WebBrowserReadyState.Complete)
            {
                // body 태그 이하 모든 요소들 추출
                foreach (HtmlElement elements in this.webBrowser1.Document.Body.Children)
                {
                    // 각 부모요소안의 모든 요소를 추출
                    foreach (HtmlElement element in elements.All)
                    {
                        // 특정 태그 목록 추출
                        switch (element.TagName)
                        {
                            case "IMG":
                                // 이미지 경로 출력
                                MessageBox.Show(element.GetAttribute("SRC"));
                                break;
                            case "A":
                                // 링크 경로 출력
                                MessageBox.Show(element.GetAttribute("HREF"));
                                break;
                        }
                    }
                }
            }
    }

반응형
반응형

//로그인에 필요한 데이터...  
 string postData = "a=124&b=344";   

 HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create("데이터를 전송할 URI");

 

  //인코딩은 서버에서 처리하고 있는 인코딩으로 지정해주세요.

  //대부분의 사이트는 Defalt 혹은 euc-kr로 하면 되는 것 같습니다.

  byte[] byteArray = Encoding.GetEncoding("ks_c_5601").GetBytes(postData);

 

  httpWebRequest.Method = "POST";
  httpWebRequest.ContentType = "application/x-www-form-urlencoded";
  httpWebRequest.ContentLength = byteArray.Length;

            httpWebRequest.CookieContainer = new CookieContainer();

            //저장된 쿠키콜렉션이 있을 경우에는 쿠키를 포함해서 전송한다.
            if (_CookieContainer.ContainsKey(txtID.Text))
                httpWebRequest.CookieContainer.Add(_CookieContainer[txtID.Text]);

            Stream stream = httpWebRequest.GetRequestStream();

            stream.Write(byteArray, 0, byteArray.Length);
            stream.Close();

            HttpWebResponse httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();

            //로그인 후 서버로부터 받은 쿠키콜렉션을 특정 자료구조에 저장해 놓았다가
            //로그인 후의 페이지를 요청하게 될 때 사용한다.
            if (!_CookieContainer.ContainsKey(txtID.Text))
                _CookieContainer.Add(txtID.Text, httpWebResponse.Cookies);
            else
            {
                //로그인 후에 다른 페이지를 접속했을 경우 
                //쿠키를 추가하게 되는 경우가 있으므로 
                //이럴 때는 기존에 갖고 있던 쿠키에 추가하고 
                //자료 구조에 저장해놓는다.
                CookieCollection coo = _CookieContainer[txtID.Text];
                coo.Add(httpWebResponse.Cookies);
                _CookieContainer[txtID.Text] = coo;
            }

            Stream responseStream = httpWebResponse.GetResponseStream();

            //받을 때도 마찬가지로 서버에서 전송해주는 인코딩 방식으로 읽어주세요

            StreamReader streamReader = new StreamReader(responseStream, Encoding.GetEncoding("ks_c_5601"));

            string resultString = streamReader.ReadToEnd();
            
            
            Console.WriteLine(resultString);

 

//전송 데이터, URI, 수신 데이터 등의 자료는 이더리얼이라는 프로그램을 사용하시면 편합니다.

[출처] [스크랩핑] 특정 사이트 로그인해서 문서 가져오기. |작성자 코달

반응형
반응형

뭐 간단한 -_- 거지만 몇번 포트인지 모르는 분들이 제법 계셔서 함 올려봅니다.

 

ITS 의 경우 13번 포트가 사용 됩니다.

 

표준 시간을 얻어서 적용해야되는 경우 아래와 같이 타임 서버에서 값을 얻어서 쓸 수 있습죠.

 

 

using System;

using System.Collections.Generic;

using System.Text;

using System.IO;

using System.Net.Sockets;

 

namespace NTP_CLIENT

{

    class Program

    {

        static void Main(string[] args)

        {

            TcpClient tsc = new TcpClient("time-a.nist.gov", 13);

 

            if (tsc.Connected)

            {

                    NetworkStream ns = tsc.GetStream();

                    StreamReader sr = new StreamReader(ns);

                    string sResult = sr.ReadToEnd().Trim();

 

                    Console.WriteLine(sResult); //서버에서 받은 결과

 

                    //공백으로 결과값을 나눠서 배열에 넣음.

                    string[] saResult = sResult.Split(' ');

 

                    foreach (string s in saResult)

                    {

                        Console.WriteLine(s);

                    }

            }

            else

            {

                Console.WriteLine("-_-; 연결 안됨");

            }

        }

    }

}

 

 

결과

 

 

일단 ITS 의 경우 시간/날짜는  tcp 13 번 포트를 이용해서 접속이 가능합니다.

 

NTP 기반일 경우 UDP 123 이나 텔넷 37 번이구요. 

 

위의 형태로 받은 결과물을 소스처럼 공백으로 잘라내면 원하는 항목을 얻을 수 있습니다.

 

변수에 저장해서 클라이언트 쪽 시간/날짜 값 변경에 사용하시면 되겠죠.

 

즐프하세요.

 

ITS 리스트 : http://tf.nist.gov/tf-cgi/servers.cgi

반응형
반응형
Delegate[대리자]란?
- 메서드의 대리자
- 메서드를 보다 효율적으로 사용하기 위하여 특정 메서드 자체를 캡슐화 할 수 있게 만들어주는 방법

using System;

namespace DelegateTest
{
delegate void pis1();
delegate void pis2(int x);
class Test1
{
public void x1()
{
Console.WriteLine("Test1");
}
public void x2(int y)
{
Console.WriteLine("Test2 " + y);
}
}

class Test2
{
[STAThread]
public static void Main(string[] args)
{
Test1 t1 = new Test1();
pis1 p1 = new pis1(t1.x1);
pis2 p2 = new pis2(t1.x2);
p1();
p2(100);
}
}
}

============출력결과================
Test1
Test2  100
====================================

이게 Delegate[대리자]이다. 솔직히 -.- 왜 쓰는지 모르겠다. 밑의 소스를 보면 알겠지만

using System;

namespace DelegateTest
{
class Test1
{
public void x1()
{
Console.WriteLine("Test1");
}
public void x2(int y)
{
Console.WriteLine("Test2 " + y);
}
}
class Test2
{
[STAThread]
public static void Main(string[] args)
{
Test1 = new Test1();
t1.p1();
t1.p2(100);
}
}
}

============출력결과================
Test1
Test2  100
====================================

이 소스랑 다른게 몬가? 출력결과는 똑같은데... --;;; 이제 공부해야지...자 찾아보자
소설같은 C#의 5장 Delegate를 보면...간단하게 설명되어 있다. 몬소린지 하나도 모르겠다. 정말..-_-;
나중에 더 자세히 설명해준다는데...것참....

그래서 "vs 2005로 배우는 C#게임프로그래밍" 책을 봐야겠다.
좀 더 자세하게 설명되어 있다.

Delegate[델리게이트, 대리자]
- 대리자라는 사람은 무엇인가의 일을 대신해주는 사람이란 뜻이다. 이와 같이 C#에서는 어떠한 기능을 대신 수행해주는 역할
- Delegate[델리게이트, 대리자]는 사용자 정의형으로 정의할 수 있다.
 사용자 정의형의 대표적인 예는 Class[클래스]이다.

사용자 정의 -- delegate string dele(string Str);     함수(메서드)형태로 정의함
                 -- Class Main{ }
                 -- Class A { }
                 -- Class B { }
위의 그림(?)을 보면 클래스와 같은 위치에서 선언된다. 맨 위의 소스를 보면 이해될 것이다.

delegate void pis1();
delegate void pis2(int x);
class Test1
{
....

보이지? 클래스보다 먼저 선언했다. 같은 위치라는 것... 그리고 함수 형태로 정의한다는 것..리턴형도 있고 말야..+_+ 인자값도 있고
Delegate[델리게이트, 대리자]의 대표적 기능은 메서드를 대신수행하는 역할이란 것이다.
C언어에서는 포인터함수라는 개념이 있다는데 난 C전혀! 모르므로 패스! (-.ㅜ...이러니 내가 미쳐)
예제가 있다. 그런데 위의 예제와 거의 비슷하다. 그래서 또 패스! 할려다가....나에게 좀 필요한 기초가 있어서 만들겠다.

using System;
using System.Collections.Generic;
using System.Text;
namespace DelegateTest
{
//델리게이트 선언
delegate void dele1();
delegate int dele2(int a, int b);
//계산 클래스
class MathClass
{
public void Intro()
{
Console.WriteLine("계산을 시작합니다.");
}
public int Sum(int a, int b)
{
return a+b;
}
}

//프로그램 시작점
class Program
{
static void Main(string[] args)
{
//Math(계산)클래스 선언 및 인스턴스화
MathClass MathClass = new MathClass(); //전에 적은 거 같은데 MathClass MathClass가 클래스 선언
                                                             //new MathClass()가 인스턴스화 시키는 거라고 생각하면 됨
//델리게이트 선언 및 인스턴스화(델리게이트와 클래스는 같은 위치에서 선언된다.)
dele1 Sum1 = new dele1(MathClass.Intro);
dele2 Sum2 = new dele2(MathClass.Sum);
Sum1();
Console.WriteLine("Result : {0}", Sum2(-10, 90));
}
}
}

============출력결과===============
계산을 시작합니다.
Result : 80
===================================

앞의 예제와 거의 비슷하다. -0- 

먼저 MathClass를 만들었다. 이 클래스는 두개의 메서드를 가지고 있다. 하나는 반환형과 매개변수 없는 Intro함수 
하나는 덧셈기능을 수행하여 리턴해주는 Sum함수
자  그럼 먼저 8-9줄에 선언된 Delegate[델리게이트,대리자]를 보면 MathClass가 가지고 있는 함수와 똑같은 형식을 갖춘 Delegate 
두개가 선언되어있다.
dele1은 반환값이나 매개변수가 존재하지 않고 dele2는 두개의 int형 매개변수를 가지고 있으며 int형 반환값을 가지고 있다.

32,33번째 줄을 보면 선언된 델리게이트를 인스턴스화 시켰다. 그리고 생성자에는 메서드가 참조된다.
만약 dele1 Sum1 = new dele1() 이렇게 선언하면 에러가 발생한다. 즉 델리게이트는 반드시 메서드를 참조 시켜야한다는 것!

그리고 만약 dele1에 MathClass.Sum과 같은 매개변수와 선언형식이 다른 메서드를 참조 시키려고 해도 에러가 난다.

한마디로 요약하자면 Delegate 는 연결될 메서드를 반드시 참조시켜야하고 매개변수와 선언형식도 같아야한다는 것이다!

이제까지 Delegate의 기본적인 면만 봤다. 이제 더 고난이도로 접근 -_-; 



Delegate의 연산
- Delegate는 Combine과 Remove라는 두개의 연산메서드를 지원한다. 이말은
 Delegate가 여러 메서드의 참조가 가능하다는 것이다.

Combine : 기존의 위임에 또 다른 위임을 추가합니다.[+] //말을 어렵게 했는데...그냥 추가한다고 보면된다.
Remove : 기존의 위임에 특정 위임을 제거합니다. [-]

using System;
using System.Collections.Generic;
using System.Text;
namespace DelegateTest
{
delegate void deleMath(int Vlue); //델리게이트 선언
class MathClass
{
public int Number;
public void Plus(int Value); //더하기
{
Number += Value; //Number = Number + Value 와 같은 뜻
}
public void Minus(int Value) //빼기
{
Number -= Value; //Number = Number - Value 와 같은 뜻
}
public void Multiply(int Value) //곱하기
{
Number *= Value; //Number = Number * Value 와 같은 뜻
}
}

class Program
{
static void Main(string[] args)
{
//Math 클래스 선언 및 인스턴스화
MatchClass MathClass = new MathClass();
//Delegate 선언 및 인스턴스화(덧셈)
deleMath Math = new deleMath(MathClass.Plus);
//위임 연산(뺄셈, 곱셈 추가)
Math += new deleMath(MathClass.Minus);
Math += new deleMath(MathClass.Multiply);
//결과
MathClass.Number = 10;
Math(10);
Console.WriteLine("Result : {0}", MathClass.Number);
//위임 연산 (뺄셈 제거)
Math -= new deleMath(MathClass.Minus);
//결과
MathClass.Number = 10;
Math(10);
Console.WriteLine("Result : {0}", MathClass.Number);
//위임 연산(곱셈 제거)
Math -= new deleMath(MathClass.Multiply);
//결과
MathClass.Number = 10;
Math(10);
Console.WriteLine("Result : {0}", MathClass.Number);
}
}
}

============출력결과=====================
Result : 100
Result : 200
Result : 20
=========================================

9줄에 정의된 MathClass 메서드들을 살펴보면 3개의 연산 메서드들이 존재한다. 이 메서드들은 매개변수로 받은 값을 Number라는 전역
변수와 연산을 하고 Number변수에 저장된다.

코드 34줄을 보면 Math 델리게이트는 선언과 동시에 Plus메서드를 기본으로 참조 시켰다. 그리고 37, 38줄을 보면 += 연산을 이용하여 추가로
메서드를 추가하고 있다. 이제 Math 델리게이트는 3개의 메서드가 참조된 것이다. 플러스, 마이너스, 곱셈 메서드....

41줄에서 10 이란 매개변수를 넣어 델리게이트를 실행하면 이 델리게이트는 Plus(10), Minus(10), Multiply(10) 이라는 세 개의 
메서드를 차례로 호출하는 것과 같은 의미다.
그래서 결국은 100 이 출력된다.
나머지는 뺄셈 메서드를 빼주면 되고, 나머지는 곱셈 메서드를 빼주면 계산결과가 나온다.


추가 - 내가 위에 클래스랑 같이 Delegate를 선언해야한다고 했는데 ... 꼭 그런건 아닌듯하다.
다음 소스를 보면...
using System;
using System.Text;
namespace DelegateTest
{
class AddFunc
{
static public void Add(int First, int Second)
{
int res = First + Second;
Console.WriteLine("{0}+{1}+{2}", First, Second, res);
Console.ReadLine();
}
}
class MathOp
{
public delegate void Opdelegate(int First, int Second);
public Opdelegate Op;
protected int _first, _second;
public MathOp(int First, int Second)
{
_first = First;
_second = Second;
}
public void Invoke()
{
if (Op != null)
Op(_first, _second);
}
}

class MathDelegateApp
{
static void Main(string[] args)
{
MathOp mo = new MathOp(42, 27);
mo.Op = new MathOp.Opdelegate(AddFunc.Add);
mo.Invoke();
}
}
}
반응형
반응형

자 이제 응용 프로그램의 설치와 배포에 대한 실습을 한번 해 보도록 하자. 준비물은 C#으로 만든 프로그램.. 간단한거 아무거나 좋다. ^^

프로젝트 에서 설치 및 배포 프로젝트에서 설치 프로젝트를 선택한다.

솔류션 탐색기의 파일 시스템 편집기를 선택한뒤 다음과 같이 파일을 추가해 보자.





솔류션 탐색기의 프로젝트를 선택한뒤 다음과 같이 속성도 지정해 주자.

솔류션 탐색기의 파일 형식 편집기를 선택한뒤 다음과 같이 파일형식을 추가해 보자.




솔류션 탐색기의 사용자 인터페이스 편집기를 선택한뒤 다음과 같이 대화상자를 추가해 보자.





솔류션 탐색기의 시작조건 편집기를 선택한뒤 다음과 같이 시작조건을 추가해 보자.






솔류션 탐색기의 속성을 선택한뒤 다음과 같이 속성을 설정해 보자.

자 이제 프로그램 메뉴에서 빌드 항목에 솔류션 빌드를 선택해 보자.

그러면 Debug 디렉토리안에 다음과 같은 파일이 생성된 것을 볼수 있다. 자.. 우리는 이제 이 생성된 파일을 배포하면 되는 것이다.

자.. 실제로 실행되는 화면을 한번 보자. 어.. 근데 실행 시키면 다음과 같은 메세지를 만날수 있을 것이다.

우리가 시작 조건에서 Windows Installer 의 조건을 걸때 Componentld 속성의 Guid 를 엉터리로 줬기 때문이다. 이 Guid를 Windows Installer의 Guid로 정해 주어야 하기 때문이다. 즉 Guid를 검색해서 조건이 맞는게 없기 때문에 null을 반환하고 에러를 표시한는 것이다. 참고로 구성 요소 ID를 확인하려면 응용 프로그램을 설치하고 Windows Installer 소프트웨어 개발 키트에 포함된 MSI Spy와 같은 도구를 사용해야 합니다. 자 그러면 우리는 잠시 실행결과를 보기위해 시작조건을 빼고 다시한번 빌드한뒤 실행해 보자. 다음은 실행 결과이다.

자.. 이로써 배포에 대한 강좌를 마칠까 하네염..^^ 히히~ 담에는 무슨 강좌를 올릴까염? 많은 의견 부탁드려염..^^ 끝까지 읽어주셔셔 감사해염...^^ 담에뵈영...ㅂㅂ

반응형

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

타임서버 데이타 취하기  (0) 2010.03.19
Delegate [대리자] 기본 개념과 사용법  (0) 2010.03.07
닷넷 응용 프로그램의 설치 및 배포 #1  (0) 2010.03.07
시스템 사양 알아내기~  (0) 2010.03.07
Delegate 사용 2  (0) 2010.03.07
반응형

이번 강좌는 Window 응용프로그램의 배포에 관한 강좌를 하겠습니다. 만약 우리들이 C#을 통해 어떠한 프로그램을 짜서 다른사람들에게 배포하고 싶을때 그냥 실행파일만 카피해 주는 방식이 아닌 설치 및 배포 프로젝트를 사용하여 인스톨 방식을 통해 사용자가 프로그램을 설치할수 있게 하는 것을 지금부터 배우겠습니다. 설치 및 배포 프로젝트는 Windows Installer을 이용하여 프로젝트를 만드는 것입니다. 설치및 배포 프로젝트를 만드실 사용자의 PC와 배포 프로젝트를 통해 만들어진 파일을 사용하실 PC에도 Windows Installer이 꼭 설치되어 있어야 합니다. 이것으로 고민하시는 분이 많은데요.. 우선 닷넷을 설치하시면 Windows Installer가 기본적으로 설치되며 또한 배포 프로젝트를 통해 만들어진 파일에도 Windows Installer를 추가할수 있기 때문에 이 점에 대해선 걱정 안하셔두 될것 같네영..^^ 자 이제 본격적인 강의에 들어가 보겠습니다.

VS.NET를 실행하시고 새 프로젝트에서 다음과 같이 설치및 배포 프로젝트에서 설치 프로젝트를 선택합니다.

그러면 다음과 같은 화면을 볼수 있을 것입니다.

각 화면에 대해 간단하게 설명하겠다.

1. 대상 컴퓨터의 파일 시스템의 목록이다.
2. 대상 컴퓨터의 파일 시스템에 해당하는 파일의 목록이다. 만약 1번에서 사용자 바탕화면을 선택하고 2번에서 자기가 넣고 싶은 파일을 선택해서 넣는다면 인스톨이 되고나서 그 파일이 대상 컴퓨터의 바탕화면에 설치된다는 말이다.
3. 여기에는 파일 시스템 편집기, 레지스트리 편집기, 파일형식 편집기, 사용자 인터페이스 편집기, 사용자 지정작업 편집기, 시작조건 편집기, 속성 등이 있다. 여기에 대해선 밑에서 자세히 설명하겠다.
4. 각 항목들에 대한 속성이다.

자 우선 기본적으로 배포 프로젝트로 만들어질 파일의 이름과 버전등 속성들을 설정해보자. 3번 솔류션 탐색기에서 프로젝트를 선택한후 속성창에 보면 버전과 나라, 이름등등 속성들을 설정하는 부분이 있으니 각자 맞게 설정해 보자.

이제는 설치에 필요한 파일이 어디에 어떻게 어떤형태로 저장될 것인지를 살펴보자. 3번 솔류션 탐색기에서 프로젝트를 선택한후 3번창의 맨끝에 속성을 선택해 본다. 그러면 다음과 같은 화면을 볼수 있을 것이다.

출력파일 이름에는 배포 프로젝트를 통해 만들어질 인스톨 파일의 이름을 정의하는 곳이다. 기본적으로 Debug 디렉토리 안에 생성된다.

패키지 파일에는 느슨하게 압축되지 않은 파일로, 설치 파일에서, CAB 파일에서 와 같이 세가지 항목이 있다. 이 항목은 설치 파일의 형태를 정의하는 항목이다.
느슨하게 압축되지 않은 파일로 항목은 모든 프로그램 파일과 데이터 파일들이 압축되지 않은 상태 그대로 설치파일에 포함된다.
설치 파일에서 항목은 모든 파일들이 MSI 인스톨 파일안에 합쳐지고 압축되어진 형태이다.
CAB 파일에서 항목은 파일들이 CAB 파일로 만들어 지며 MSI 인스톨 파일은 이 CAB 파일을 읽어서 설치하는 역활만 한다.

부트스트래퍼는 실제 인스톨 프로그램이 실행되기 전에 반드시 실행되어야 하는 프로그램을 말한다. 부트스트래퍼에는 없음, Windows Installer 부트스트래퍼, 웹 부트스트래퍼 와 같이 3가지 항목이 있다.

앞에서 배포 프로젝트를 통해 만들어진 인스톨 파일을 실행할려면 Windows Installer이 꼭 설치되어 있어야 된다고 설명했다. 자 여기서 부트스트래퍼의 항목중 Windows Installer 항목을 선택하면 인스톨 파일에 Windows Installer 이 자동으로 포함된다. 그러므로 Windows Installer 이 설치되어 있지 않은 피씨에서도 자동으로 Windows Installer이 자동으로 설치되어 인스톨이 가능하다. Windows Installer을 선택하면 Windows Installer 파일을 포함하는데 필요한 용량 3MB 정도가 늘어난다. 웹 부트스트래퍼는 웹을 통해 부트스트래퍼를 다운받고 설치한다. 장점은 인스톨 파일의 크기가 작아진다는 장점이 있지만 단점은 인터넷이 꼭 연결되어 있어야 한다는 단점이 있다.

압충에는 속도 최적화, 크기 최적화, 없음 와 같이 세가지 항목이 있으며 항축 형태를 설정하는 곳이다.
속도 최적화를 선택하면 인스톨 속도는 빨라질 것이나 크기는 커질 것이며 크기 최적화를 선택하면 인스톨 파일의 크기는 작아지나 인스톨 속도가 느려질 것이다.

자 이제는 실제 인스톨 파일에 파일을 추가하고 인스톨 진행과정을 제어하는 방법을 살펴보자. 위에서 잠깐 살펴보았던 솔류션 탐색기를 보자.

프로젝트를 선택한 상태에서 3번.. 첫번째 파일 편집기를 선택해 보자. 그러면 다음과 같은 화면을 볼수 있을 것이다.

1. 바탕화면에 아이콘을 추가할수 있다.
2. 응용프로그램의 바로가기를 시작 메뉴에 추가할 경우 사용한다.
3. 실행 파일과 기타 파일들이 저장되는 곳이다.

위 항목에서 각 항목에 파일이나 아이콘, 폴더등을 추가하고 싶으면 각 항목을 클릭한뒤 마우스 오른쪽 버튼을 클릭하면 다음 그림과 같이 추가하는 항목이 나온다.

 

프로젝트를 선택한 상태에서 3번.. 두번째 레지스트리 편집기를 선택해 보자. 그러면 다음과 같은 화면을 볼수 있을 것이다.

이 항목은 사용자가 레지스트리를 추가할수 있는 항목이다. 각 레지스트리 항목에 대한 레지스트리를 추가할려면 레지스트리 항목을 선택한후 마우스 오른쪽 버튼을 누르면 새키 항목을 통해 추가할수 있다. 레지스트리 편집기는 프로그램의 버전이나 데모버젼일 경우 사용기간 등을 설정할수 있어서 편리하다.

프로젝트를 선택한 상태에서 3번.. 세번째 파일형식 편집기를 선택해 보자. 그러면 다음과 같은 화면을 볼수 있을 것이다.

파일 형식 편집기란 만약 설치한 응용 프로그램이 특정한 파일형식(파일 확장자)을 사용한다면 탐색기에서 그 파일 형식을 더블 클릭했을때 자동적으로 응용프로그램이 실행되도록 하는 것이 좋을것이다. 그 파일형식을 파일형식 편집기에 등록시켜 두면 그 파일형식에 해당하는 파일을 더블클릭 했을때 응용프로그램이 실행되는 것이다. 파일 형식을 추가하는 방법은 위 그림에서 대상 컴퓨터의 파일형식을 선택한 다음 마우스 오른쪽 버튼을 누르고 파일 형식 추가를 누르면 다음과 같은 화면을 볼수 있을 것이다.

위의 그림에서 1번 새 문서 형식 #1에 문서 형식의 이름을 정해주고 문서 형식을 클릭한뒤 속성창을 보면 다음과 같이 파일 형식의 속성을 정의해 줄수 있다.

1. 파일 형식을 설명하는 이름이다. 이 이름은 파일 형식 편집기에 표시되고 레지스트리에도 등록된다. 또한 이름은 고유하여야 한다.
2. 이것은 사용자가 지정한 파일형식을 더블 클릭했을때 실행된 실행 파일을 지정하는 곳이다.
3. 파일 형식에 대한 추가적인 설명을 적는 곳이다.
4. 파일 형식을 지정하는 곳입니다. 파일 형식은 앞에 점(.)을 빼고 XXX 식으로 입력합니다. 여러개의 확장자라면 XXX;YYY;ZZZ 식으로 입력하시면 됩니다.
5. 등록할 파일 형식의 아이콘을 지정하는 곳입니다.
6. 선택한 파일형식과 연결할 하나 이상의 MIME를 설정합니다.

위의 윗 그림에서 2번 open 속성을 살펴보자. open을 선택하고 속성창을 보면 다음과 같은 화면을 볼수 있다.

이 속성은 선택한 파일형식이 더블클릭 될때 실행파일에 어떠한 인수를 줄 것인가를 정해주는 속성이다.
1. 파일 형식을 호출할때 사용될 Action 의 이름 즉 이름을 정의하는 곳이다.
2. 파일 형식이 호출될때 실행 파일에 던져줄 인수 즉 Arguments를 정의해주는 곳이다. 기본적으로 %1 이며 %1의 의미는 파일이름이다.
3. 파일 형식이 호출될때 실행 파일에 던져줄 또 다른 하나의 인수이다. 각각을 구분할때 사용한다. 만약 해당하는 파일형식이 더블클릭 했을때 바로 프린트가 되는것이라면 print 인수를 주어 실행파일에 print 가 전달된다. 그리고 실행파일에서는 print 라는 인수를 식별하여 프린터를 해준다. 만약 그냥 파일 open 을 해야 한다면 open 인수를 주는것이 적당할 것이다.

프로젝트를 선택한 상태에서 3번.. 네번째 사용자 인터페이스 편집기를 선택해 보자. 그러면 다음과 같은 화면을 볼수 있을 것이다.

사용자 인터페이스 편집기는 인스톨할때 대화상자를 지정할수 있습니다.
1. 대상 시스템에 응용프로그램을 설치하는 일반적인 설치를 의미한다.
2. 응용프로그램의 이미지를 네트웍 공유를 통해 설치하는 경우에 사용한다.

사용자 인터페이스 편집기는 시작, 진행률, 끝이라는 세단계 진행 상태로 나누어 진다.
각각의 시작, 진행률, 끝 이라는 단계에는 지정된 대화상자를 추가해 줄수 있다. 시작, 진행률, 끝 항목중 하나를 선택하여 마우스 오른쪽 버튼을 누르면 대화상자 추가라는 항목을 통해 대화상자를 추가해 줄수 있다.

대화상자에는 여러가지가 있으며 사용자가 필요한것을 선택하여 추가해 줄수 있다.

프로젝트를 선택한 상태에서 3번.. 다섯번째 사용자 지정작업 편집기를 선택해 보자. 그러면 다음과 같은 화면을 볼수 있을 것이다.




사용자 지정작업 편집기는 설치, 커밋, 롤백, 제거 도중에 실행될 응용프로그램을 지정할수 있다. 각 항목을 선택하고 마우스 오른쪽 버튼을 클릭한후 사용자 지정작업 추가 항목을 통해 응용프로그램을 추가할수 있다.

프로젝트를 선택한 상태에서 3번.. 여섯번째 시작조건 편집기를 선택해 보자. 그러면 다음과 같은 화면을 볼수 있을 것이다.

시작조건 편집기는 설치가 일어나기 전에 대상 시스템이 갖추어야 할 필요조건을 지정해 줄수 있다.

1. 대상 컴퓨터의 시작조건을 검색할수 있다. 조건을 추가하는 방법은 대상 컴퓨터의 검색을 선택하고 마우스 오른쪽 버튼을 누르면 파일 시작 조건 추가, 레지스트리 시작 조건 추가, Windows Installer 등등 과 같은 시작 조건을 추가해 줄수 있다. 여기서 Componentld 속성은 {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} 형식을 사용하여 GUID로 형식이 지정된 문자열을 사용합니다. 여기에서 X는 16진수(0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F)이며 괄호는 필수적입니다. 또한 고유하여야 합니다.
2. 실제적으로 1번의 시작조건을 검사해서 True 이면 설치가 시작된다. False 이면 에러메세지를 뿌려준다. 1번의 시작조건을 설정하는 방법은 2번의 시작조건을 선택한뒤 마우스 오른쪽 버튼을 클릭하고 시작 조건을 추가하고 2번의 추가된 시작조건을 클릭한뒤 속성창을 보면 Contition 이란 속성을 통해 1번에서 추가된 시작조건을 선택할 수 있다. 만약 1번의 시작조건이 True이면 설치가 시작된다.

자 이제 여기까지 성공적으로 왔으면 마지막으로 빌드.. 즉 디버그를 해보자. 메뉴 항목에서 빌드항목 아래 솔류션 빌드를 선택하자. 그러면 이제 인스톨 파일이 만들어져 있을 것이다. 이제 성공적으로 끝났다.



자.. 이로써 배포에 관한 설명을 마치고 다음에는 배포에 관한 실습을 따라 해보도록 하자.

반응형

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

Delegate [대리자] 기본 개념과 사용법  (0) 2010.03.07
닷넷 응용 프로그램의 설치 및 배포 #2  (0) 2010.03.07
시스템 사양 알아내기~  (0) 2010.03.07
Delegate 사용 2  (0) 2010.03.07
Delegate 사용  (0) 2010.03.07
반응형


시스템 사양 아는 방법 홍지선 / yellowbug  
홍지선님께 메시지 보내기    홍지선님의 블로그가 없습니다  

 C#을 이용해서 다음과 같은 컴의 사양에 대해 알 수는 없을까요?

Environment로는 OS 종류하고 버전정도만 알 수 있는거 같던데, 다른 방법이 있겠죠?

 

1. OS (servicepack 포함)

2. CPU

3. VGA

4. Browser

5. RAM

6. NIC

 

 

고수님들의 지도 부탁드립니다.

감사 .

이 글에 평점 주기:  
  2005-03-08 오후 5:16:57   /  번호: 47834  / 평점:  (5.0)  
 Re: 아는 것만 ^^ 김성경 / lovelyk2  
김성경님께 메시지 보내기    김성경님의 블로그가 없습니다  

내용을 보니 그 중에 몇몇은 레지스트리에서 값을 읽어오면 될꺼 같군요.

 

http://www.theeldergeek.com/registry_edits.htm

 

여기에 보면 재미있는 레지스트리 값이 많이 있군요 ^^;;

 

혹시 필요한게 있나 찾아보세요~

 

HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion

 

여기에 필요한 데이터들이 거의 다 있군요

 

RegEdit 쓰는 방법은 아시죠?

 

 

이 글에 평점 주기:  
  2005-03-10 오후 4:39:25   /  번호: 47950  / 평점:  (9.0)  
 Re: 첨부파일 있음여러가지 방법이 있을것 같네요 김태형 / kth0301  
김태형님께 메시지 보내기    김태형님의 블로그가 없습니다  

사실 저도 초보지만 데브피아에서 많은걸 배웁니다. 초보들한테는 참 좋은 사이트라 생각이 됩니다

 

사실 C#공부 하면서 많은 정보를 MSDN에서 배우게 됩니다. 님께서도 아시리라 믿습니다

 

일단 서버 및 로컬 컴의 시스템 자원 및 정보를 가져오는 방법은 여러가지가 있을거라 생각이 됩니다

 

1. 레지스트리를 가져오는 방법, 2. WMI를 이용하는 방법, 3. PerformanceCounter를 이용하는 방법 등

 

제가 아는 방법중에 WMI라는걸 이용하는 방법과 PerformanceCounter를 이용하는 방법이 있는데요

 

첫번째 방법 :  WMI는 Windows Management Instrumentation의 약자인데요 NT이상에서 지원하는 방법이라고 알고 있습니다

 

예제

                string cmd_info = "/C net use \\\\"+treeView1.SelectedNode.Text.ToString()+"\\c$"+" "+svr_passwd+" /user:"+svr_user;

 

                ProcessStartInfo startInfo = new ProcessStartInfo("cmd.exe",cmd_info);        

                startInfo.WindowStyle = ProcessWindowStyle.Hidden;      //화면에 표시 되지 않고 실행되는 프로퍼티

                Process.Start(startInfo);

 

                ConnectionOptions options = new ConnectionOptions();

 

                options.Username=svr_user;

                options.Password=svr_passwd;

                string address=treeView1.SelectedNode.Text.ToString();

 

                ManagementScope scope = new ManagementScope(@"\\"+address+"\\root\\cimv2",options);

                scope.Connect();

 

                ObjectQuery objQuery = new ObjectQuery("Select * from Win32_Processor");

                ManagementObjectSearcher query = new ManagementObjectSearcher(scope,objQuery);

                ManagementObjectCollection queryCollection = query.Get();       

 

                foreach(ManagementObject mo in queryCollection)

                {

                //  MessageBox.Show(mo["Caption"].ToString());

                }

 

설명 : 이건 제가 만든 소스인데요. 상단에 부분은 서버 연결 부분이고 ManagementScope부터 서버 정보 가져오는겁니다

 

         Win32_Processor로 CPU 관련된 모든걸 알수가 있습니다.

         Win32_OperatingSystem으로는 운영체제에 관한 모든걸 알아 낼수가 있구요

        

         MSDN에서 win32관련 내용과 ManagementScope 및 관련 내용을 조회하시면 다양한 샘플이 있을겁니다

         그걸 이용하시면 될것 같구요

 

두번째 방법 : PerformanceCounter는 NT이상에서 지원하는 성능모니터의 기능을 가져다 사용하는 방법입니다

 

예제

 

        PerformanceCounter를 이용하여 로컬컴의 CPU사용량 및 F드라이브 여유 공간을 실시간 확인하는 소스입니다

 

        소스를 보시면 아시겠지만 timer를 사용하면 interval로 값을 불러와서 뿌려주도록 되어 있습니다

 

        만약에 원격 컴퓨터에 있는 정보를 보실려면 WMI를 이용하거나 net use를 사용하여 서버에 임시로 연결한 후

    

        PC1.MachineName = 원격컴아이피;

        PC2.MachineName = 원격컴아이피;

 

        이런식으로 해주시면 되구요

 

        다양한 카테리고리 및 카운터 네임 그리고 인스턴스는 성능모니터에서 확인하실 수가 있습니다

 

        그렇게 되면 모든 서버 자원에 대한 정보를 알수가 있을겁니다

 

        이거 알아내는데 1주일 넘게 걸렸습니다. 아무리 찾아봐도 없더라구요.

 

        함 해보시고 궁금하신거 있으시면 연락주세요.

 

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

        {           

            PC1 = new PerformanceCounter("Processor","% Processor Time","_Total");

            PC2 = new PerformanceCounter("LogicalDisk","% Free Space","F:");

        }

 

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

        {

            textBox1.Text = PC1.NextValue().ToString();

            textBox2.Text = PC2.NextValue().ToString();

        }       

 

짧은 지식으로 대충 적었네요. 참으로 프로그래머의 길은 멀고도 험한것 같습니다. 다같이 화이팅!!!

 

수고 하시고 제가 아는 선에선 정보 공유하겠습니다. 리플 다시구요

 

위에 내용 알아내는데 뭐 엄청난 시간이 지났습니다. 머리가 나쁜게 죄죠...ㅋㅋ

     

반응형
반응형

책찾아보시면 많이나오죠. 근데 저도 막상 보면 잘모르겟던데요.

그래서 제가 나름데로 개발할때 경험상으로 이해한것을 설명해보겠습니다.

 

 

1. 개념

 만약 어떤 기능을 실행하는 메소드를 호출할 필요가 있을때, 그냥 직접적으로 호출하는 형식입니다.

 델리게이트는 호출하는코드랑 호출되는메서드 사이에 있다고 생각하면 됩니다.

 

어떤 메소드를 호출한다고할때.

델리게이트 사용       : 메소드호출 ---> 델리게이트 ---> 호출당하는 메소드

일반적인메소드호출 : 메소드호출 ---> 호출당하는 메소드

 

즉. 메소트호출쪽은 호출되는메소드를 직접 부르는 것이 아니고 델리게이트를 대신 부르게 됩니다.

 

이처럼 중간자(대리자)역활을 합니다.

 

2. 왜 사용하는지

한마디로 유연성입니다.

저도 첨에책으로만 볼땐 그래서 머! 걍호출하면 되지 웨 써 복잡하게라고 생각했었습니다.

예를들어 폼위에 버튼을 올려놓고 클릭을 하게 되면 클릭이벤트로 가게되죠?

이건 마소에서 처음 .net을 개발할때 버튼콘트롤 소스하고 버튼클릭이벤트메소드(예:button1_Click)

를 델리게이트로 연결했기 때문에 우리가 볼땐 콘트롤을 누르면 이벤트메소드로 가는것처럼 보이는겁니다.

뒤에서는 델리게이트가 중간에 연결을 해주고 있죠!

좀이해가 안가시죠. 그럼 어디에 사용하는지 한번 보죠.

 

3. 어디에 사용하는지

이걸 응용하자면 엄청 많을 것 같은데..

1..한번에 10개의 매소드를 실행하고 싶을때.

    10개의 메소드를 전부 호출하는 것이 아니고.

    델리게이트를 걸어서 델리게이트를 한번호출합니다.

  

2..비동기처리에 사용합니다.

    예를들어 어떤 이벤트가 발생되서 실행되는

    A라는 메소드가 있는데 이놈은 한번 실행하면 최소 10분이상걸리는 놈입니다.]

    보통 델리게이트를 사용하지 않았을 경우 이놈이 끝날때까지 기다린후,다른 처리를 하게되는데.

    델리게이트걸어 비동기화 시켜주게되면 일단 실행이 되고,

    실행이 끝나던말든 다음처리를 할수 있습니다.

    이걸,멀티캐스팅이라고 하나요.

 

  ↓비동기처리의 예  

 using System;

delegate void ShowMessageDelegate(string msg);

class Callable {
  internal void ConcreteShowMessage(string msg) {
    System.Threading.Thread.Sleep(5 * 1000);
    Console.WriteLine(msg);
  }
}

class Caller {
  internal ShowMessageDelegate ShowMessage;

  internal IAsyncResult CallDelegate() {
 
    IAsyncResult ret
        = ShowMessage.BeginInvoke("hello, from delegate!",
            new AsyncCallback(Callback), ShowMessage);
    return ret;
  }

  internal void Callback(IAsyncResult ar) {
    ShowMessageDelegate usedDelegate
        = (ShowMessageDelegate)ar.AsyncState;
    usedDelegate.EndInvoke(ar);
  }
}

class App {
  static void Main() {
    Callable callable = new Callable();
    Caller caller = new Caller();

    caller.ShowMessage
        = new ShowMessageDelegate(callable.ConcreteShowMessage);

    IAsyncResult ar = caller.CallDelegate();

    while(!ar.IsCompleted) {
      System.Threading.Thread.Sleep(500);
      Console.Write(".");
    }
    Console.WriteLine("Done.");
  }
}

소스 설명은 뺐습니다.

[출처] Delegate 사용 2|작성자 니시오카


반응형
반응형

//델리게이트 & 이벤트
//메서드의 대리자
//메서드를 보다 효율적으로 사용하기 위하여 특정 메서드 자체를 캡슐화 할수 있게 만들어 주는 방법
//델리게이트를 사용하려면 어떤 메서드를 호출할 것인지 컴파일할 시점이 아닌 프로그램을 실행하는 런타임 상황에서 결정할 수 있다.

//1단계 - Delegate할 메서드 정의
using System;
class Atype
{
 public void F1() //Delegate 할 Method1
 {
  Console.WriteLine("Atype.F1");
 }
 public void F2(int x) //Delegate 할 Method2
 {
  Console.WriteLine("Atype.F2 =x"+x);
 }
}

//2단계 - Method에 맞는 Delegate 선언
using System;
delegate void SimpleDelegate1(); // Delegate 선언1 --> void F1()
delegate void SimpleDelegate2(); // Delegate 선언2 --> void F2()
class Atype
{
 public void F1() //Delegate 할 Method1
 {
  Console.WriteLine("Atype.F1");
 }
 public void F2(int x) //Delegate 할 Method2
 {
  Console.WriteLine("Atype.F2 =x"+x);
 }
}

//3단계 - 임의의 객체 생성
//Delegate할 Method를 정하고 Delegate를 선언했다면, 이제 Method가 포함된 Class의 객체를 만들어야 한다. 그래야 특정 객체의 메서드가 하나의 독립적인 Delegate로서 활동 할수 있다.
using System;
delegate void SimpleDelegate1(); // Delegate 선언1 --> void F1()
delegate void SimpleDelegate2(int i); // Delegate 선언2 --> void F2()
class Atype
{
 public void F1() //Delegate 할 Method1
 {
  Console.WriteLine("Atype.F1");
 }
 public void F2(int x) //Delegate 할 Method2
 {
  Console.WriteLine("Atype.F2 =x"+x);
 }
}
class DeleTest
{
 public static void Main()
 {
  Atype atype = new Atype(); // 객체(Instance) 생성
 }
}

//4단계 - Delegate 생성과 호출
using System;
delegate void SimpleDelegate1(); // Delegate 선언1 --> void F1()
delegate void SimpleDelegate2(int i); // Delegate 선언2 --> void F2()
class Atype
{
 public void F1() //Delegate 할 Method1
 {
  Console.WriteLine("Atype.F1");
 }
 public void F2(int x) //Delegate 할 Method2
 {
  Console.WriteLine("Atype.F2 =x"+x);
 }
}
class DeleTest
{
 public static void Main()
 {
  Atype atype = new Atype(); // 객체(Instance) 생성
  SimpleDelegate1 s1 = new SimpleDelegate1(atype.F1); //Delegate 생성
  SimpleDelegate2 s2 = new SimpleDelegate2(atype.F2); //Delegate 생성
  
  s1(); //Delegate를 이용한 호출1
  s2(1000); // Delegate를 이용한 호출2
 }
}

 

 


//메소드 호출 : 일반 메소드 & 인스턴스 메소드
using System;

namespace delegateEX
{
 public delegate void MyDelegate(int x); //델리게이트 선언

 class MyClass
 {
  public static void Test(int x)
  {
   Console.WriteLine("Test 메서드의 x 값은 : {0}",x);
  }
  public static void Test2(int x)
  {
   Console.WriteLine("Test2 메서드의 x 값은 : {0}",x);
  }

  class Class1
  {
   [STAThread]
   static void Main(string[] args)
   {
    MyClass.Test(100);  //일반 메소드 호출 형식

    //Test델리게이트 Instance 생성
    MyDelegate MD = new MyDelegate(MyClass.Test);
    MD(100); // 델리게이트를 통한 호출

    MD = new MyDelegate(MyClass.Test2);
    MD(200);
   }
  }
 }
}

 

 

//하나의 Delegate에 여러개의 Delegate등록

using System;
delegate void TopDelegator(string str); //Delegate Declare

class Top
{
 public static void StaticMethod(string str)
 {
  Console.WriteLine("static Method \t");
  Console.WriteLine(str);
 }
 public void NormalMethod(string str)
 {
  Console.WriteLine("Normal Method \t");
  Console.WriteLine(str);
 }
}
class MultiDelegateTest
{
 public static void Main()
 {
  Top t = new Top(); //클래스 인스턴스화
  TopDelegator bank;
  TopDelegator td1 = new TopDelegator(t.NormalMethod); // 델리게이트 인스턴스화
  TopDelegator td2 = new TopDelegator(Top.StaticMethod); // 델리게이트 인스턴스화

  bank = td1;
        bank("add TopDelegator 1"); // 하나의 delegate 호출
  
  
bank += td2;
  bank("add TopDelegator 2"); // 두개의 delegate 호출

  
 
 bank -= td1;
  bank("remove topDelegator 1"); // Delegate 제거
 }
}

 

 

using System;

namespace DelegateEX
{
 public delegate void MyDelegate(string message);//Delegate 선언

 class MyClass
 {
  public string message; //string 변수선언

  public void Process(MyDelegate mydelegate) // 공용 메소드 선언
  {
   mydelegate(message);//선언한 string 변수 삽입
  }
 }

 class Class1
 {
  public static void DisplayMessage1(string message)
  {
   Console.WriteLine("DisplayMessage1 : {0}", message);
  }
  public void DisplayMessage2(string message)
  {
   Console.WriteLine("DisplayMessage2 : {0}",message);
  }

  [STAThread]
  static void Main(string[] args)
  {
   MyClass mc = new MyClass(); // 초기화

 

   mc.message ="cookee";
   MyDelegate md1 = new MyDelegate(DisplayMessage1); 

   mc.Process(md1);

 

   mc.message = "cookee2";
   MyDelegate md2 = new MyDelegate(new Class1().DisplayMessage2);
   mc.Process(md2);

  }
 }
}


 

using System;

namespace DelegateEX
{
 public delegate int MyDelegate(int i, string s);//반환값이 Int, string 매개변수가 두개인 델리게이트 선언.

 class Class1
 {
  public MyDelegate my; //MyDelegate형 my 선언.

  public int Hello(int i,string s) // my가 호출되었을때 실행할 메소드 작성
  {
   Console.WriteLine("Hello MyDelegate my -- {0}",s);
   return i;
  }

  [STAThread]
  static void Main(string[] args)
  {
   Class1 cs = new Class1();//초기화..

   cs.my = new MyDelegate(cs.Hello);//my변수에 처리할 메소드 Hello 할당.
   int i = cs.my(2,"cookee"); // 델리게이트형 변수 my 실행.
   Console.WriteLine(i);
  }
 }
}

using System;

namespace DelegateEX
{
 public delegate void MyDelegate();//void 이고 인자가 없는 delegate 선언.

 class Class1 //서로다른 클래스 : Class1
 {
  public event MyDelegate my; //MyDelegate형 my 선언(my는 이벤트 변수가 된다.) event 키워드 붙인다.

  public void Hello() // my가 실행할 메소드 작성
  {
   Console.WriteLine("다른 클래스에서 인자없고 반환없는 델리게이트 실행한 결과");
  }

  public void TriggerEvent() //이벤트 받는 메소드
  {
   this.my(); //이벤트 메소드 내에서 Event my()실행.
  }
 }

 class execute  //서로다른 메인클래스
 {
  [STAThread]
  static void Main(string[] args)
  {
   Class1 cs = new Class1();//초기화..

   cs.my += new MyDelegate(cs.Hello);//델리게이트변수 my가 처리할 메소드 Hello를 할당.
   cs.TriggerEvent(); //이벤트를 받을 메소드 부름.
  }
 }
}

  Delegate - 7.Event 와 Delegate

//=========================== 1 =========================================================

using System;

namespace DelegateEX
{
 public delegate void MyEventHandler2(string s); //델리게이트 선언

 class myButton2
 {
  public event MyEventHandler2 Event; // delegate형 이벤트 등록.
  
  public void button2_Click(string val)
  {
   Console.WriteLine("{0} : 버튼에서 이벤트 발생 :",val);
  }
  public void button3_Click(string val)
  {
   Console.WriteLine("{0} : 버튼에서 또 이벤트 발생 :",val);
  }
  static void Main(string[] args)
  {
   myButton2 ok = new myButton2();

   //이벤트 발생
   ok.Event += new MyEventHandler2(ok.button2_Click);

   //이벤트 추가
   ok.Event += new MyEventHandler2(ok.button3_Click);
   ok.Event("cookee");
  }
 }
}

 

Delegate - 8. 윈폼띄우기

using System;
using System.Drawing;//솔루션탐색기에서 참조추가 해준다.
using System.Windows.Forms;//마찬가지로 참조추가 해준다.

namespace DelegateEX
{
 public class EventCall
 {
  public static void Hello(object sender, EventArgs e)
  {
   Console.WriteLine("Hello 메소드 실행!!");
  }

  public static void cookee(object sender, EventArgs e)
  {
   int x = 100, y=100;
   Console.WriteLine("cookee");
   Form frm1 = new Form();

   Button btn2 = new Button();
   btn2.Text = "버튼2";

   x = x + btn2.Location.X;
   y = y + btn2.Location.Y;

   btn2.Location = new Point(x,y);
   frm1.Controls.Add(btn2);
   frm1.Show();

   Console.WriteLine("cookee매소드 실행!!");
  }

  public static void Main(string[] args)
  {
   Form f = new Form();

   Button btn = new Button();

   //버튼 속성 지정
   btn.Text = "버튼을 누르면 Hello 메소드실행! ";
   btn.Location = new Point(100,100); // point 구조체의 생성자에 할당.
   btn.Size = new Size(200,30);//size 구조체의 생성자에 할당.
   btn.ForeColor = Color.Black;
   btn.BackColor = Color.Red;

   f.Controls.Add(btn);
   btn.Click += new EventHandler(Hello);

   Button btn1 = new Button();
   btn1.Text = "버튼위에 마우스커서를 올려놓으면 cookee 메소드실행 ";
   btn1.Size = new Size(180,50);
   btn1.BackColor = Color.Yellow;
   btn.Location = new Point(50,100);
   f.Controls.Add(btn1);
   btn1.MouseHover += new EventHandler(cookee);//버튼위에 올려놓으면 새로운 Form이 뜬다.

   Application.Run(f);
  }
 }  
}


 

 

 [출처] Delegate 의 사용|작성자 니시오카  

 

 

 

 

반응형
반응형

Javascript 사용 방법 [koxo 자바스크립트 매뉴얼 사이트 참조]

이전 페이지에서 연결을 통하여 사용자가 새로운 페이지에 도달하였을 때만 새로운 페이지에서 속성값을 반환한다. 아니거나 연결이 안전사이트로 부터 이루어지면 document.referrer는 빈 문자열을 반환한다.

예를들어 PageB.html 연결이 있는 PageA.html 페이지에서 사용자가 클릭하면 연결되고 PageB.html 페이지에서 document.referrer가 "PageA.html" 문자열을 반환한다.
그러나 사용자가 PageA.html 페이지에서 브라우저의 주소칸에 PageB.html을 입력하거나 파일 메뉴에서 파일을 선택하여 PageB.html 문서를 열면 document.referrer는 빈 문자열을 반환한다.

피씨에서는 정상적으로 참조되어도 빈 문자열을 반환한다

<script type="text/javascript">
    document.write("document.referrer = " + document.referrer);
</script>

 

C# 사용방법 [MSDN 참조]

현재 URL에 연결된 클라이언트의 이전 요청 URL에 대한 정보를 가져옵니다.

Uri MyUrl = Request.UrlReferrer;
Response.Write("Referrer URL Port: " + Server.HtmlEncode(MyUrl.Port.ToString()) + "<br>");
Response.Write("Referrer URL Protocol: " + Server.HtmlEncode(MyUrl.Scheme) + "<br>");


 

ASP 사용방법 [재우니형 참조 ㅋ]

get_URL = Request.ServerVariables("HTTP_REFERER") 

반응형

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

Delegate 사용 2  (0) 2010.03.07
Delegate 사용  (0) 2010.03.07
주사위놀이  (0) 2010.02.09
Silent Install of MS .Net Framework Version 2  (0) 2010.02.05
텍스트박스에 워터 마크 추가(배경)  (0) 2010.01.26
반응형


Sussman 이란 사람 外 여러명이 쓴 "[WroxPress]David Sussmans Beginning DynamicWebsitesWith ASP Dot NET Web Matrix" 에서 웹매트리스를 이용하여 만든 게임입니다. VB코드로 된것을 C#Code로 바꿔 봤습니다..

웹메트리스가 VS.NET 버젼으로..평이한 코드이나 랜덤 함수에 대해 약간이 지식이 생길듯 하군요..

게임 룰은 짜시다보면 금방 이해 하실수 있을테고 (코드만 봐도..) 아무튼 디자인이 끝난 상태 입니다.

디자인 감각이 별루 없어서...쩝..

좀더 멋지게 하셔도 되구요.. Table,Label,Button,ImageControl로 구성 되었습니다.

귀찮으시다구요?,, 저와 같이 디자인에는 소질이 없으시다면.. 요밑에 HTML 쏘스를 긁어다... ㅎㅎ

 

[HTML Code]

 

<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="SnakeEye.WebForm1" %>
<HTML>
 <HEAD>
  <title>WebForm1</title>
  <meta content="Microsoft Visual Studio .NET 7.1" name="GENERATOR">
  <meta content="C#" name="CODE_LANGUAGE">
  <meta content="JavaScript" name="vs_defaultClientScript">
  <meta content="http://schemas.microsoft.com/intellisense/ie5" name="vs_targetSchema">
 </HEAD>
 <body MS_POSITIONING="GridLayout">
  <form id="Form1" method="post" runat="server">
   <strong><font size="4">
     <table style="WIDTH: 500px; HEIGHT: 150px" height="150" width="500">
      <tbody>
       <tr>
        <td>
         <p align="left"><strong><font size="5"><IMG src="Images/Dice.gif">주사위 게임(Snake Eye)</font></strong>
         </p>
        </td>
       </tr>
       <tr>
        <td>둘다 1이 나올때 까지 진행 하세요~!</td>
       </tr>
       <tr>
        <td><asp:label id="Label1" runat="server" borderstyle="Solid" height="44px" font-size="X-Large"
          font-names="Verdana">0</asp:label><asp:label id="Label2" runat="server" borderstyle="Solid" height="44px" font-size="X-Large"
          font-names="Verdana">0</asp:label>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
         <asp:label id="Label3" runat="server" visible="False" ForeColor="Blue" Font-Bold="True">허걱~!
                        첨부터 다시 하셔야 겠어요. ㅜ,.ㅜ</asp:label></td>
       </tr>
       <tr>
        <td>
         <p align="left"><FONT face="굴림"><asp:image id="Image1" runat="server" ImageUrl="Images/1.gif"></asp:image>&nbsp;
           <asp:image id="Image2" runat="server" ImageUrl="Images/6.gif"></asp:image></FONT></p>
        </td>
       </tr>
       <TR>
        <TD>
         <P align="left"><asp:button id="Button1" runat="server" Text="계속 눌러주세용~!"></asp:button></P>
        </TD>
       </TR>
       <tr>
        <td>
         <p align="left"><asp:label id="Label4" runat="server" font-size="X-Large" font-names="Verdana" forecolor="Red">0</asp:label>&nbsp;
          (점수는 위의 주사위 수의 합이 누적된 값입니다.)
         </p>
        </td>
       </tr>
       <tr>
        <td>마지막게임점수&nbsp;:
         <asp:label id="Label5" runat="server" font-size="X-Large" font-names="Verdana" forecolor="Red">0</asp:label></td>
       </tr>
       <tr>
        <td>
         <p align="left">지금까지 최고점수 :
          <asp:label id="Label6" runat="server" font-size="X-Large" font-names="Verdana" forecolor="Red">0</asp:label>
          <asp:Button id="Button2" runat="server" Text="최고점수 리셋"></asp:Button></p>
        </td>
       </tr>
      </tbody>
     </table>
    </font></strong>
   <!-- Insert content here --></form>
 </body>
</HTML>

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

손쉽게 끝!!!.

 

다음은 CS 코드 부분입니다...

 

[CS Code]

 

using System;
using System.Collections;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Web;
using System.Web.SessionState;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.HtmlControls;

namespace SnakeEye
{
 /// <summary>
 /// WebForm1에 대한 요약 설명입니다.
 /// </summary>
 public class WebForm1 : System.Web.UI.Page
 {
  protected System.Web.UI.WebControls.Label Label1;
  protected System.Web.UI.WebControls.Label Label2;
  protected System.Web.UI.WebControls.Label Label3;
  protected System.Web.UI.WebControls.Label Label4;
  protected System.Web.UI.WebControls.Label Label5;
  protected System.Web.UI.WebControls.Button Button1;
  protected System.Web.UI.WebControls.Button Button2;
  protected System.Web.UI.WebControls.Image Image1;
  protected System.Web.UI.WebControls.Image Image2;
  protected System.Web.UI.WebControls.Label Label6;
 
  private void Page_Load(object sender, System.EventArgs e)
  {
   // 여기에 사용자 코드를 배치하여 페이지를 초기화합니다.
  }

  #region Web Form 디자이너에서 생성한 코드
  override protected void OnInit(EventArgs e)
  {
   //
   // CODEGEN: 이 호출은 ASP.NET Web Form 디자이너에 필요합니다.
   //
   InitializeComponent();
   base.OnInit(e);
  }
  
  /// <summary>
  /// 디자이너 지원에 필요한 메서드입니다.
  /// 이 메서드의 내용을 코드 편집기로 수정하지 마십시오.
  /// </summary>
  private void InitializeComponent()
  {   
   this.Button1.Click += new System.EventHandler(this.Button1_Click);
   this.Button2.Click += new System.EventHandler(this.Button2_Click);
   this.Load += new System.EventHandler(this.Page_Load);

  }
  #endregion

     
  private void Button1_Click(object sender, System.EventArgs e)
  {
   Label3.Visible = false;

   Random random = new Random();
    int lbl1 = random.Next(1,7);
   int lbl2 = random.Next(1,7);

   
   Label1.Text = lbl1.ToString();
   Label2.Text = lbl2.ToString();

   Image1.ImageUrl = "Images/" + Label1.Text + ".gif";
   Image2.ImageUrl = "Images/" + Label2.Text + ".gif";
   
   if(lbl1 == 1 && lbl2 == 1)
   {       
    Label5.Text = Label4.Text;  
    
    if(int.Parse(Label5.Text) >= int.Parse(Label6.Text))
    {
     Label6.Text = Label5.Text;
    }
    Label4.Text = "0";   
    Label3.Visible = true;
   }
   else
   {
    int _Sum = (int.Parse(Label4.Text) + lbl1 + lbl2);
    Label4.Text = _Sum.ToString();
   }
  }

  private void Button2_Click(object sender, System.EventArgs e)
  {
   Label6.Text = "0";
  }
 }
}


푸른색 부분만 코딩 하시면 되겠죠.. 아니면?? 이것도  Ctrl + V.. ㅎㅎㅎ

디자인폼에서 Button1과 Button2를 더블 클릭 하면 자동으로 이벤트핸들러가 생성.. 코딩하기 좋게.. Button1_Click 이벤트 핸들러 와 Button2_Click 이벤트 핸들러 안에서 깜박 거리겠죠.. 저빨간색 바탕으로 칠해논것과 셋트로 자동 생성 됩니다.. (자세한건 이벤트와 델리게이트 참고..)

윈폼이 더 럭셔리해보일거 같은데.. 혹시 해보시고싶으신 분은 WinForm 으로 해보시는것도..좋겠네요... 사실은 최고 점수 갱신때는 등록 하는것도 있긴한데..누가 이런 재미 없는 게임을 하고 최고 점수 냈다고 등록을 할까요?? ㅎㅎ 그래도 해봐야겠지요.. 다음에는 오늘 이 웹페이지에 덧붙여서 계속 올려 볼까 합니다...  아흑~

 

필요한 이미지도 같이 올려요~!! 제일 위쪽 오른쪽에 있습니다...

 

허접허다.. ㅠ,.ㅠ

즐거운 주말들 되세요~!!

 

[출처] 주사위놀이 -1|작성자 쿠키

반응형
반응형

http://sojoe.info/2006/11/01/silent-install-of-ms-net-framework-version-2/

Many applications now require Microsoft .NET Framework Version 2.0 (I’ll call it .NET2 for short), and it is one of the recommended updates if you go to Windows Update or Microsoft Update. If you’re building a silent distribution of Windows XP, or any applications to install on Windows XP, then you’ll want to be able to silently install .NET2. Following are instructions on one way of doing this (their’s always another way).

First, you’ll need to download .NET2 from Microsoft. All of the links to downloads assume you are using the more common 32-bit version of the English version of Windows XP. You can get the download here:

www.microsoft.com/downloads/details.aspx?FamilyID=0856eacb-4362-4b0d-8edd-aab15c5e04f5&DisplayLang=en

By default, double-clicking the dotnetfx.exe file you downloaded will unpack it to a temp directory, and then give you an interface to click through for the installation. We don’t want to have to click all those buttons, we’d prefer a silent installation. The available command-line switches for this download are:

  • /Q Quiet modes for package
  • /T: Specifies temporary working folder
  • /C Extract files only to the folder when used also with /T
  • /C: Override Install Command defined by author

I originally didn’t know how to get the downloaded file to install silently by itself, so in the blockquote below you’ll see instructions on how to extract the file and then install the components silently. Thankfully, a reader suggested a command-line that works with the original downloaded file, so I’ll explain the simpler method instead, as the process is much easier. I’ve retained the original instructions below in blockquote in case something in that method lends itself to your particular situation.

So, you’ve got the dotnetfx.exe file, now we need to tell it how to install silently. You could use the ‘Run’ command under the Start Menu to do this, but you’d have to type the full command in each time you wanted to do an install, which isn’t going to save much time. Instead, let’s create a batch file that we can simply double-click, and the rest will happen silently. We’re going to want to have all the necessary files together in one directory, so create a directory called ‘dotnet2′ (you can use a different name, and put it in any location that is convenient).

So, in the ‘dotnet2′ directory, right-click on a blank spot and from the pop-up menu choose ‘New’ and then ‘Text Document’. A file will be created and will want you to name it, call it ‘Install.bat’. You’ll be asked if you’re sure you want to change the extension, answer ‘Yes’. Now, right-click the Install.bat file you just created and choose ‘Edit’. A blank window will open, probably Notepad, where you can now type in the commands you want to include.

I generally start my batch files with some text that will appear on the script window explaining what’s going on, so I’ll start with this:

@ECHO OFF ECHO Installing DotNet 2

The first line tells the window not to show ever single step we take, and the second shows the text ‘Installing DotNet 2′.

Next, we’ll tell the install file to run. As long as the Install.bat file is in the same directory as the install.exe file, we don’t need to specify the path:

start /wait dotnetfx.exe /q:a /c:"install.exe /q"

Notice that I used the /q switch, which initiates the ‘quiet mode’, and the /c switch to pass along a command line switch to the files which will be extracted, so the magic part that wasn’t clear from the start was the additional ‘:a’ following the /q switch. The ’start /wait’ part simply tells the window to stay open until the installation is finished, which is useful if you want to know if it’s still running, and also useful if you’ll be doing anything else with the script after the installation. What, you thought this was it? Nope, their’s more. You now have a script that will install .NET2 silently on any machine, but their are patches available for .NET which should be applied as well. As I write this, their are two patches available:

  • KB917283
    • http://www.microsoft.com/downloads/details.aspx?familyid=56A1777B-9758-489F-8BE8-5177AAF488D1&displaylang=en
    • Download NDP20-KB917283-X86.exe
  • KB922770
    • http://www.microsoft.com/downloads/details.aspx?familyid=34C375AA-2F54-4416-B1FC-B73378492AA6&displaylang=en
    • Download NDP20-KB922770-X86.ex

These files have the following command-line switches:

  • /extract Extract files to the directory specified
  • /q Quiet install mode

The ‘Quiet mode’ does work on these, so the process is pretty simple, just run each of the exe’s with the /q switch, and you are just about done. All you need to do is add one more command to tell the command window to go away when it’s done. Just add an ‘exit’ at the end.

So, your final Install.bat script will probably look like this:

@ECHO OFF
ECHO Installing DotNet 2

start /wait dotnetfx.exe /q:a /c:"install.exe /q"

start /wait NDP20-KB917283-X86.exe /q
start /wait NDP20-KB922770-X86.exe /q

exit

Copy your dotnet2 folder to a network share, or throw it on a CD or thumbdrive and all you need to do is double-click the Install.bat file or call it from another batch file and Microsoft .NET Framework Version 2.0 will silently install with patches on that computer. When the black command window disappears, it’s installed.

Here’s the previous method, which works, but is a little more complicated:

Notice their is a switch for ‘Quiet mode’, which you’d think would be the same as ‘Silent Install’, but if you try it, you’ll find you get the same prompts as a normal installation. So, what we’ll need to do is extract the files from this download. You can do this by using the /C and /T switches. Probably the easiest way to do this part is to go to your Start Menu and choose ‘Run’. When the run window opens, clear any text that might be in the ‘Open’ field, and then drag the dotnetfx.exe file into it, which will automatically type in the path to the file (you can also use the browse button to go find it). Now, before you click ‘OK’, add the following after the name of the file ” /C /T:c:\dotnet2″. If you had downloaded the dotnetfx.exe file to your desktop, you’re Run box whould look something like this:

“C:\Documents and Settings\username\Desktop\dotnetfx.exe” /C /T:c:\dotnet2

Click OK and this will instruct the file to expand to a directory on your C: drive named ‘dotnet2′. You can change the ‘c:\dotnet2′ part to any location that is convenient, but if it contains spaces you’ll need to wrap it in quotes.

So, now you’ve got a bunch of files at c:\dotnet2. Open that directory and you’ll see one of the files is named ‘Install.exe’. That’s the file we’ll be using to do our silent installation. It has some command-line switches as well:

  • /l Name of verbose msi log
  • /lang 4-digit language code
  • /q Quiet install mode
  • /qu Quiet uninstall mode

The /q switch is the one we’re looking for. Now we could use the ‘Run’ command to do this, but it would only help us on this one machine. If we want a hands-off approach on other machines as well, we’ll want to create a script to tell the install.exe file what to do. So, in the ‘c:\dotnet2′ directory, right-click on a blank spot and from the pop-up menu choose ‘New’ and then ‘Text Document’. A file will be created and will want you to name it, call it ‘Install.bat’. You’ll be asked if you’re sure you want to change the extension, answer ‘Yes’. Now, right-click the Install.bat file you just created and choose ‘Edit’. A blank window will open, probably Notepad, where you can now type in the commands you want to include.

I generally start my batch files with some text that will appear on the script window explaining what’s going on, so I’ll start with this:

@ECHO OFF ECHO Installing DotNet 2

The first line tells the window not to show ever single step we take, and the second shows the text ‘Installing DotNet 2′.

Next, we’ll tell the install file to run. As long as the Install.bat file is in the same directory as the install.exe file, we don’t need to specify the path:

start /wait install.exe /q

The ’start /wait’ part simply tells the window to stay open until the installation is finished, which is useful if you want to know if it’s still running, and also useful if you’ll be doing anything else with the script after the installation. What, you thought this was it? Nope, their’s more. You now have a script that will install .NET2 silently on any machine, but their are patches available for .NET which should be applied as well. As I write this, their are two patches available:

  • KB917283
    • http://www.microsoft.com/downloads/details.aspx?familyid=56A1777B-9758-489F-8BE8-5177AAF488D1&displaylang=en
    • Download NDP20-KB917283-X86.exe
  • KB922770
    • http://www.microsoft.com/downloads/details.aspx?familyid=34C375AA-2F54-4416-B1FC-B73378492AA6&displaylang=en
    • Download NDP20-KB922770-X86.ex

These files have the following command-line switches:

  • /extract Extract files to the directory specified
  • /q Quiet install mode

The ‘Quiet mode’ does work on these, so the process is pretty simple, just run each of the exe’s with the /q switch, and you are just about done. All you need to do is add one more command to tell the command window to go away when it’s done. Just add an ‘exit’ at the end.

So, your final Install.bat script will probably look like this:

@ECHO OFF ECHO Installing DotNet 2 start /wait install.exe /q start /wait NDP20-KB917283-X86.exe /q start /wait NDP20-KB922770-X86.exe /q exit

Copy your c:\dotnet2 folder to a network share, or throw it on a CD or thumbdrive and all you need to do is double-click the Install.bat file or call it from another batch file and Microsoft .NET Framework Version 2.0 will silently install with patches on that computer. When the black command window disappears, it’s installed.

반응형
반응형

방법.. 잘 읽어봐라..-_-;


Form 페이지 만든 후 Form의 Designer.cs 페이지에서 해당프로젝트.Controls.TextBoxExt() 라고 InitializeComponent와 

제일 아래 private Head1Ton.Dev.Controls.TextBoxExt txtBoxExt1; 이라고 변경해줘야만 한다.





텍스트 박스에 값을 입력하지 않으면 배경에 특정 메시지를 보여주고, 값을 입력하면 보이지 않게 해달라는 요청을 받았습니다.

 

그런데!!

 

텍스트박스는 OnPaint를 기본적으로 쉽게 사용할 수 없습니다. SetStyle 메서드에서 UserPaint 플래그를 설정하면 가능하지만, 글씨 같은 부분도 모두 처리해 줘야되기 때문에 여간 번거로운게 아닙니다. 제가 원하는건 오직배경에 뭔가 처리!’ 그것 뿐이라구요..ㅠㅡㅠ

 

그래서 WndProc 를 재정의해서 이벤트를 받아오는 방법을 사용했습니다. 주의라고 까진 아니지만, 유심히 보실 부분은 WM_KILLFOCUS 도 처리해주고 있는것인데요. 텍스트 박스를 멀티라인 지원으로 해놓으면, WM_PAINT 가 발생을 하지 않더군요. 그래서 WM_PAINT 에서도 처리를 해주었습니다.

 

using System;

using System.Drawing;

using System.Windows.Forms;

 

namespace TextBoxPaintSample.Controls

{

    public class TextBoxExt : TextBox

    {

        private string waterMarkText = string.Empty; // 워터마크로 사용할 문자열

        private Color waterMarkColor = Color.Gray;   // 워터마크로 사용할 문자색

 

        protected override void WndProc(ref Message m)

        {

            // base.WndProc 중복 호출을 피하기 위해서

            bool isCallAlready = false;

 

            // WM_PAINT 메세지를 받아서 처리

            if (m.Msg == 0x000F) // WM_PAINT = 0x000F

            {

                // 원래 처리해야될 로직을 먼저 호출해서 처리해줌

                base.WndProc(ref m);

                isCallAlready = true;

 

                DrawWaterMarkText();

            }

            // Multiline == true 일때는 포커스 빠질때 WM_PAINT가 발생 안하므로

            else if (m.Msg == 0x0008 && this.Multiline) // WM_KILLFOCUS = 0x0008

            {

                DrawWaterMarkText();

            }

 

            if (false == isCallAlready)

                base.WndProc(ref m);

        }

 

        // 텍스트박스의 크기를 계산해서 워터마크를 그려줌

        private void DrawWaterMarkText()

        {

            if (string.IsNullOrEmpty(this.Text) &&

                false == string.IsNullOrEmpty(this.WaterMarkText) &&

                this.IsHandleCreated &&

                false == this.Focused &&

                this.Visible)

            {

                using (Graphics g = Graphics.FromHwnd(this.Handle))

                {

                    // 텍스트의 vertical 정렬을 하기 위한 계산들

                    StringFormat sf = new StringFormat();

                    float textHeight = g.MeasureString(this.WaterMarkText, this.Font, this.Width, sf).Height;

                    float textY = ((float)this.Height - textHeight) / (float)2.0;

                    RectangleF bounds = new RectangleF(

                        0, textY, (float)this.Width, (float)this.Height - (textY * (float)2.0));

 

                    g.DrawString(this.WaterMarkText, this.Font, new SolidBrush(this.WaterMarkColor),bounds, sf);

                }

            }

        }

 

        public string WaterMarkText

        {

            get { return waterMarkText; }

            set { waterMarkText = value; }

        }

 

        public Color WaterMarkColor

        {

            get { return waterMarkColor; }

            set { waterMarkColor = value; }

        }

 

    }

}


 

샘플 결과

사용자 삽입 이미지

 

이러한 코드를 바탕으로 텍스트 박스의 배경으로 이미지를 넣는다던지 하는 처리도 가능하리라 생각됩니다.

 

소스코드 전체를 첨부합니다. VS2005에서 작성 및 테스트되었습니다.
 

 


ps. 사실, 이 포스트의 내용은 제가 원래 작성하고자 했던 내용은 아닙니다만, 문제해결의 과정을, 혹은 진짜 이 내용이 필요하신분을 위해서 먼저 작성하였습니다. 다음 포스트에서는 TextBox를 상속받을 수 없는경우, 즉 다른 사람이 만든 TextBox에서 컨트롤만 얻어올 수 있을때의 대처 방법을 포스팅 하겠습니다.


반응형

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

주사위놀이  (0) 2010.02.09
Silent Install of MS .Net Framework Version 2  (0) 2010.02.05
윈도우 폼 간 도킹 기능  (0) 2010.01.26
윈폼 파일 드레그하기  (0) 2010.01.26
C# 초보자분들을 위한 기초강좌  (0) 2010.01.24
반응형
 

도킹 기능을 사용하려고 
맨날맑음 님께서 제공해주신 Tip을  보다가 수정을 좀 해봤습니다. ^^
기본 로직은 신의아그니스님이 올려주신 것처럼 좌표를 비교해서 도킹합니다.
 
 

 
FormUtil.cs
 

using System;

using System.Windows.Forms;

using System.Drawing;

 

// Author RevMinho

 

namespace DockingTest

{

    class FormUtil

    {

        private Form current;   //현재 폼 윈도우

        private Point prevLoc;  //폼의 이전 위치

 

        public FormUtil(Form form)

        {

            current = form;

            prevLoc = form.Location;

        }

 

        public void docking(Form target, int dockGap)

        {

            // 만약 소유한 폼들이 있고 도킹되어 있다면 함께 이동한다.

            foreach (Form temp in current.OwnedForms)

            {

                if (isDocked(temp))

                {

                    temp.SetDesktopLocation(

                        temp.Location.X + (current.Location.X - prevLoc.X),

                        temp.Location.Y + (current.Location.Y - prevLoc.Y));

                }

            }

 

            // 폼의 현재위치를 저장해둔다.

            prevLoc = current.Location;

 

            // 도킹 대상 폼윈도우의 오른쪽에 접근시

            if (checkRange(current.Left, target.Right, dockGap) && checkYLine(target))

            {

                current.Left = target.Right;

            }

 

            // 도킹 대상 폼윈도우의 왼쪽에 접근시

            if (checkRange(current.Right, target.Left, dockGap) && checkYLine(target))

            {

                current.Left = target.Left - current.Width;

            }

 

            // 도킹 대상 폼윈도우의 아래쪽에 접근시

            if (checkRange(current.Top, target.Bottom, dockGap) && checkXLine(target))

            {

                current.Top = target.Bottom;

            }

 

            // 도킹 대상 폼윈도우의 위쪽에 접근시

            if (checkRange(current.Bottom, target.Top, dockGap) && checkXLine(target))

            {

                current.Top = target.Top - current.Height;

            }

        }

 

        // 대상 폼과 도킹되어 있는가

        private bool isDocked(Form target)

        {

            return (target.Left == prevLoc.X + current.Width ||

                    target.Right == prevLoc.X ||

                    target.Top == prevLoc.Y + current.Height ||

                    target.Bottom == prevLoc.Y);

        }

 

        // 대상 폼 윈도우 테두리의 범위안으로 이동했는가

        private bool checkRange(int curVal, int tarVal, int range)

        {

            return (curVal > tarVal - range) && (curVal < tarVal + range);

        }

 

        // 대상 폼 윈도우와 수평선상에 있는가

        private bool checkXLine(Form target)

        {

            return (current.Left < target.Right) && (current.Right > target.Left);

        }

 

        // 대상 폼 윈도우와 수직선상에 있는가

        private bool checkYLine(Form target)

        {

            return (current.Top < target.Bottom) && (current.Bottom > target.Top);

        }        

    }

}

 


 

 

사용하기

위의 객체를 만들고서 도킹을 하고 싶은 대상 폼 윈도우를 지정해주면 됩니다. 

그리고 도킹 범위를 지정할 수 있습니다.

private void Form1_Move(object sender, EventArgs e)

{

formutil.docking(child, 10);

}


 

private void Form2_Move(object sender, EventArgs e)

{

formUtil.docking(this.Owner, 10);

}


 

 

(참고)메인 폼 윈도우가 다른 폼 윈도우를 소유하기

AddOwnedForm(targetForm);

위의 메서드를 사용합니다.

메인 폼 윈도우에서는 OwnedForms 으로 소유한 폼들을 관리하고

대상 폼 윈도우에서는 Owner 으로 메인 폼 윈도우에 접근할 수 있습니다.


 

전체 소스는 파일 첨부 했습니다.

반응형
반응형


- Fig1. 곰플레이어에서 파일 Drag&Drop을 이용하여 실행시키는 장면 - 

 
많은 어플리케이션이 위와같이 파일을 Drag&Drop만으로 열 수 있는 기능을 제공해 주고있습니다. 실제 파일을 다루는 어플리케이션을 개발하다보면 빠지면 서운한 기능일텐데요. .Net WinForm에서는 아주 쉬운방법으로 위와 같은 기능을 구현 할 수 있습니다.

#1. Windows Forms 프로젝트 생성.

- Fig2. Windows Forms 프로젝트 생성 -


#2. ListBox 추가

- Fig3. Form에 ListBox를 추가한 모습 -

여기서 ListBox는 Form위로 드레그 되어 들어온 파일의 정보를 보기위한 간단한 용도입니다.

#3. 드레그 드롭 관련 지식
public virtual bool AllowDrop { get; set; }
- 사용자가 컨트롤로 끌어 온 데이터가  컨트롤에서 허용되는지 여부를 나타내는 값을 가져오거나 설정.
- 반환 값: 끌어서 놓기 작업을 수행할 수 있으면 true이고, 그렇지 않으면 false, 기본값은 false


Controls의 AllowDrop 프로퍼티를 이용하면 Control에 Drag로 데이터를 끌어올 수 있는지 없는지 설정 할 수 있습니다. 이말은 즉 Controls를 상속받는 많은 WinForm과 Control들이 AllowDrop 프로퍼티를 사용 할 수 있다는 말입니다.

Drag&Drop 관련 이벤트
Controls.DragDrop
 : 드레그 작업이 완료되면 발생.
Controls.DragOver : 드레그한 개체가 컨트롤 위로 올라오면 발생.
Controls.DragEnter : 드레그한 개체가 컨트롤 범위 안으로 들어오면 발생.
Controls.DragLeave : 드레그한 개체가 컨트롤 범위 밖으로 나가면 발생.

4개의 이벤트가 있는데요. 우리가 사용 할 이벤트는 DragDrop과 DragOver 두개 입니다.

DragEventHandler DragEventArgs
X : 화면 좌표로 나타난 마우스 포인터의 x좌표.
Y : 화면 좌표로 나타난 마우스 포인터의 Y좌표.
Data.GetDataPresent : 인스턴스에 저장된 데이터가 지정된 형식과 관련 있는지 확인.
Effect : 드레그한 개체에대한 마우스 커서를 결정.

이벤트관련 클래스의 사용법도 알아보았으니 이제 구현을 해봅니다!

#4. 코드 구현

using System.Windows.Forms;

 

namespace DragSample

{

    public partial class Form1 : Form

    {

        public Form1()

        {

            InitializeComponent();

            //드레그를 허용

            this.AllowDrop = true;

            //Drag관련이벤트 연결------------------------------------------------

            this.DragOver += new DragEventHandler(Form1_DragOver);

            this.DragDrop+=new DragEventHandler(Form1_DragDrop);            

        }

 

        //드레그한 개체가 폼위로 올라올때--------------------------------------

        void Form1_DragOver(object sender, DragEventArgs e)

        {

            //드레그하는 개체가 파일이면

            if (e.Data.GetDataPresent(DataFormats.FileDrop))

            {

                //마우스 커서를 Copy모양으로 바꿔준다.

                e.Effect = DragDropEffects.Copy;

            }

            else

            {

                //아닐경우 커서의 모양을 θ 요런 모양으로 바꾼다.

                e.Effect = DragDropEffects.None;

            }

        }

 

        //드레그한 개체를 폼위에 올려 놓았을때----------------------------------

        void Form1_DragDrop(object sender, DragEventArgs e)

        {

            //객체들의 이름을 string 배열에 얻어온다.

            string[] files = e.Data.GetData(DataFormats.FileDrop) as string[];

            if(files!=null)

            {

                foreach (string file in files)

                {

                    //끌어온 파일명을 리스트박스에 달아준다.

                    listBox1.Items.Add(file);

                }                

            }

        }

    }

}

간단한 소스라 따로 설명은 없어도 될 것 같습니다. 유의 할 점은 e.Data.GetData(DataFormats.FileDrop)에서 반환값이 string 배열이라는 점입니다.

이와같은 방법을 응용하면 ListBox간 아이템 이동이라던지, 대부분의 Drag&Drop 기능은 구현 가능 할 것 같습니다.

맨날맑음- Hoons 에서 스크랩
반응형
반응형

C# 초보자분들을 위한 기초강좌





2009/02/12



반응형
반응형

WMI를 잘 활용하면 원격서버제어및 모니터링 프로그램을 손쉽게 만들 수가 있다.
즉, 개발자가 소켓이나 리모팅등의 직접 구현없이 원격지서버를 모니터링 하거나 제어 할수 있게 되는 것이다.

 

[WMI를 활용한 원격(로컬)컴퓨터 CPU사용량 실시간 모니터링]
 
[들어가기에 앞서..]

WMI(Windows Management Instrumentation)

윈도우즈 관리 수단 쯤으로 해석할수 있을것이다.

 

로컬뿐만 아니라 원격지 컴퓨터의 리소스까지 관리대상이 될수가 있다.

관리대상이 되는 리소스라 함은

Operating System , Computer System, System Processor , System Bios , System Time Zone,

Logical Memory Configuration , Network Connection , Video Controller 및
윈도우의 각종 서비스및 프로세서
등을 말한다.

 

이 WMI를 잘 활용하면 원격서버제어및 모니터링 프로그램을 손쉽게 만들 수가 있다.

즉, 개발자가 소켓이나 리모팅등의 직접구현없이 원격지서버를 모니터링 하거나 제어 할수 있게
되는 것이다.

 

예를 들면 원격지 서버의 서비스를 시작하거나 중지 할수가 있으며

각종 프로세서의 조작이 가능하며, CPU및 메모리의 사용량에 대한 모니터링,이벤트로그,
네트워크카드등의 관리가 있을수 있겠다.

 

이번 아티클에서는 원격지서버의 CPU사용량을 실시간으로 모니터링 하는 프로그램을 만들어 볼것이다.

 

[참고자료]

- * Obtaining Raw Performance Data

    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/example__obtaining_raw_performance_data.asp

 

- * Performance Counter Class들 목록

   ms-help://MS.VSCC.2003/MS.MSDNQTR.2003FEB.1042/wmisdk/wmi/performance_counter_classes.htm

 

- * WMI Samples - Performance Counters

    http://www.activexperts.com/activmonitor/windowsmanagement/wmi/samples/performancecounters/#Win32_PerfRawData_PerfOS_Processor.htm

[실행화면]



 

 
* 전체 실행 코드는 첨부파일을 다운받아서 실행해 주십시요

[핵심코드]

 1.WMI연결부분.. (로컬및 원격연결)

public void Connect()

        {   

            //관리작업의 범위를 설정한다.           

            if (!isRemote) //로컬컴퓨터로의 연결일 경우 서버와 네임스페이스 경로를 파라메타로 전달한다.

            {               

                scope = new ManagementScope("\\\\" + serverIP + "\\root\\cimv2");               

            }

            else

            {

                //원격컴퓨터로의 연결일 경우 WMI연결에 필요한 설정정보를 지정하는 ConnectionOptions을 추가로 전달한다

                //옵션사항으로는 id와pw 정보를 포함한다.

                options  = new ConnectionOptions();

                options.Username = userID;

                options.Password = userPwd;         

                scope = new ManagementScope("\\\\" + serverIP + "\\root\\cimv2",options);           

            }           

            try

            {

                //ManagementScope을 실제 WMI 범위에 연결

                scope.Connect();                                    

            }

            catch(Exception ex)

            {

                throw new Exception(ex.Message);                

            }                               

        }   

 

 

 

 

 

2.CPU사용량 관련 WMI 개체생성 부분..

public void SetWMIObject()

        {

            //WMI 개체에 대한 경로

            this.cpuPath = new ManagementPath();                                 

            //서버 및 네임스페이스 부분 제외한 상대 경로(클래스명:Win32_PerfRawData_PerfOS_Processor,키:Name 만을 설정)         

            //Win32_PerfRawData_PerfOS_Processor Class 의 정의 : Represents counters that measure aspects of processor activity

            //프로세서 활동을 측정하는 카운터를 대표하는 클래스(참고자료 참조)

            //Name속성값으로는 0,1,.. 이런식의 인덱스나 아래와 같이 _Total 으로 줄수 있다(cpu의 갯수와 관련있어 보인다)

            this.cpuPath.RelativePath = "Win32_PerfRawData_PerfOS_Processor.Name='_Total'";                

            //지정된 WMI 경로에 바인딩된 데이터 관리개체 생성

            this.cpuObject = new ManagementObject(this.scope,cpuPath,null);

        }

 

 

3. CPU사용량 계산 부분..

/**********************************************************************************************************     

         * 프로세서 사용률을 실제 계산하는 메서드

         * 현재의 PercentProcessorTime및TimeStamp_Sys100NS 와 바로이전의
           PercentProcessorTime및TimeStamp_Sys100NS 로 사용량을 계산한다

* *******************************************************************************************************/

        public decimal ObtainCpuPerformance()

        {

            decimal percentProcessorTime = 0;

            //관리 개체에 바인딩한다

            cpuObject.Get();

            //관리개체의 프로세서관련 값을 가져온다

            ulong  u_CurrentCPU = (ulong)cpuObject.Properties["PercentProcessorTime"].Value;

            ulong u_CurrentNano = (ulong)cpuObject.Properties["TimeStamp_Sys100NS"].Value;                                  

            decimal d_CurrentCPU = Convert.ToDecimal(u_CurrentCPU);

            decimal d_CurrentNano = Convert.ToDecimal(u_CurrentNano);

            decimal d_OldCPU = Convert.ToDecimal(oldCpuValue);

            decimal d_OldNano = Convert.ToDecimal(oldNanoValue);                        

            percentProcessorTime = (1 - ((d_CurrentCPU-d_OldCPU)/(d_CurrentNano - d_OldNano)))*100m;            

                            

            oldCpuValue = u_CurrentCPU;

            oldNanoValue = u_CurrentNano;                        

            return percentProcessorTime;

        }   

 

 

정리

WMI로 참으로 많을 것을 할수 있음에 감사한다.
원격지 컴퓨터의 컨트롤의 경우 소켓이나 채널을 개발자가 직접 만들지 않아도
된다는 점이 우선 사용성의 편리함이 있다고 보여진다.
또한 리소스검색에 아주 다양한 클래스와 SQL까지 지원하는 강력한 Search기능이 돋보인다.

기회가 닿는다면 원격서버의 디스크,이벤트로그,서비스,프로세서,메모리등을
모니터링하는 아티클을 게재하고 싶습니다.

또한 많은 분들이 관심을 가져 훌륭하고 강력한 모니터링 툴을 개발해보고 싶은 욕심이 납니다.

관련 의문점및 수정사항이 있다면
http://www.mkex.pe.kr 로 알려주시면 감사하겠습니다

반응형

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

윈폼 파일 드레그하기  (0) 2010.01.26
C# 초보자분들을 위한 기초강좌  (0) 2010.01.24
XML 파일 읽기  (0) 2010.01.20
(펌) Active Directory 의 추가, 수정, 삭제 및 쿼리하기  (0) 2010.01.19
Remote Monitoring  (0) 2009.07.19
반응형
반응형
반응형
http://www.codeproject.com/KB/system/everythingInAD.aspx


Introduction

When it comes to programmatically accessing Microsoft's Active Directory a lot of people seem to have quite a difficult time tying all the pieces together to accomplish exactly what they want to. There are so many technologies available for communicating with LDAP that many programmers end up with a mix between COM+ ADSI calls and .NET class calls mixed into their code. ADSI code is so difficult to understand and follow that the creator of that code usually owns it for the entirety of it's lifecycle since no one else wants to support it.

This article attempts to tie together the most commonly used elements involved in Active Directory Management in the simplest, most clean manner possible. I interact with Active Directory in nearly all of my applications (web & forms) and I have had to solve a lot of integration issues for many customers. When I was starting out with this technology I had a lot of growing pains so this is an attempt to help those programmers who may have a need to interact with the Directory but do not want to have to become experts in the issue. However, certain rudimentary knowledge and concepts are required in order to utilize the code. You must be familiar with such terms as: distinguishedName, ldap paths, fully qualified domain names, object attributes (single string & multi-string), and general knowledge of ldap schemas.

Background

There is a great collection of sample code available on MSDN's website for the v1.1 System.DirectoryServices assembly but there seems to be a void when it comes to the new functionality available in the v2.0 System.DirectoryServices.ActiveDirectory assembly. Since this article's original publishing, Generics have gained widespread acceptance and I encourage anyone borrowing from this resource to replace the archaic ArrayList collections with List<T> or appropriate generic collections.

Points of Concern

In order to communicate with Active Directory one must take into account network security, business rules, and technological constraints. If you're using Active Directory code from an ASP.NET page you must ensure that the code has the appropriate level of permission to access and interact with the directory. For development purposes or proof of concept you can enable impersonation at the ASP.NET level (in web.config) and the IIS level and if the IIS server and the directory domain controller reside on the same machine this will work. However, if these entities are not co-located on the same server (as they never are in production) you can wrap the code around an impersonation class (such as the Zeta Impersonator which will execute the Directory calls under the token of the impersonated user. It's strongly recommended that you do not do this for security reasons unless absolutely necessary.. The authorized method for granting the ASP.NET application permission to the directory is by way of either a privileged IIS Application Pool running under the identity of a service account or by way of a COM+ entity running under the identity of a service account.

If you plan on running this code from a desktop assembly then you're going to want to ensure that your machine is attached to a domain and can communicate with that domain. The impersonation is not necessary if the user running the code has sufficient privileges on the domain.

Running Code in Batch Processes

It is also important to note that if you plan on running this code from an ASP.NET page in batch, ASP.NET will time out on you if you try to run batch processes from it's primary thread. There are several things to consider in this scenario but be aware that for example, if you're creating x number of accounts through an ASP.NET application (or performing any batch operation in general) that you must plan to use queues, a back-end scheduler, or some other mechanism outside the scope of the page itself to prevent timing out during your processes. As with any ASPNET design, it's never a good idea to use ASPNET itself for anything but the "View" part of the solution. The best architecture would queue tasks into a SQL database or something to that effect and then a back-end windows service or similar application would pick up the tasking and perform the actual Directory operations.

This is typically how I engineer Active Directory management solutions for customers.

A Note on Method Parameters

You will notice that most of the methods require the same parameters. Rather than identify each time I will outline them now:

  • friendlyDomainName: the non qualified domain name (contoso - NOT contoso.com)
  • ldapDomain: the fully qualified domain such as contoso.com or dc=contoso,dc=com
  • objectPath: the fully qualified path to the object: CN=user, OU=USERS, DC=contoso, DC=com(same as objectDn)
  • objectDn: the distinguishedName of the object: CN=group, OU=GROUPS, DC=contoso, DC=com
  • userDn: the distinguishedName of the user: CN=user, OU=USERS, DC=contoso, DC=com
  • groupDn: the distinguishedName of the group: CN=group,OU=GROUPS,DC=contoso,DC=com

A Note on System.DirectoryServices.DirectoryEntry

You'll notice in all the samples that we're binding directly to the directoryEntry and not specifying a server or credentials. If you do not want to use an impersonation class you can send credentials directly into the DirectoryEntry constructor. The impersonation class is helpful for those times when you want to use a static method and don't want to go through the trouble of creating a DirectoryContext object to hold these details. Likewise you may want to target a specific domain controller.

Target Specific Domain Controllers or Credentials

Everywhere in the code that you see: LDAP:// you can replace with LDAP://MyDomainControllerNameOrIpAddress as well as everywhere you see a DirectoryEntry class being constructed you can send in specific credentials as well. This is especially helpful if you need to work on an Active Directory for which your machine is not a member of it's forest or domain or you want to target a DC to make the changes to.

Collapse Copy Code
//Rename an object and specify the domain controller and credentials directly


public static void Rename(string server,
    string userName, string password, string objectDn, string newName)
{
    DirectoryEntry child = new DirectoryEntry("LDAP://" + server + "/" + 
        objectDn, userName, password);
    child.Rename("CN=" + newName);
}

Managing local accounts with DirectoryEntry

It is important to note that you can execute some of these methods against a local machine as opposed to an Active Directory if needed by simply replacing the LDAP:// string with WinNT:// as demonstrated below

Collapse Copy Code
//create new local account

DirectoryEntry localMachine = new DirectoryEntry("WinNT://" + 
    Environment.MachineName);
DirectoryEntry newUser = localMachine.Children.Add("localuser", "user");
newUser.Invoke("SetPassword", new object[] { "3l!teP@$$w0RDz" });
newUser.CommitChanges();
Console.WriteLine(newUser.Guid.ToString());
localMachine.Close();
newUser.Close();

Managing local groups with DirectoryEntry

A few configuration changes need to be made to the code but it's pretty straightforward. Below you can see an example of using DirectoryEntry to enumerate the members of the local "administrator" group.

Collapse Copy Code
DirectoryEntry localMachine = new DirectoryEntry
    ("WinNT://" + Environment.MachineName + ",Computer");
DirectoryEntry admGroup = localMachine.Children.Find
    ("administrators", "group");
object members = admGroup.Invoke("members", null);

foreach (object groupMember in (IEnumerable)members)
{
    DirectoryEntry member = new DirectoryEntry(groupMember);
    Console.WriteLine(member.Name);
}

Managing IIS with DirectoryEntry

In addition to managing local & directory services accounts, the versatile DirectoryEntry object can manage other network providers as well, such as IIS. Below is an example of how you can use DirectoryEntry to provision a new virtual directory in IIS.

Collapse Copy Code
//Create New Virtual Directory in IIS with DirectoryEntry()


string wwwroot = "c:\\Inetpub\\wwwroot";
string virtualDirectoryName = "myNewApp";
string sitepath = "IIS://localhost/W3SVC/1/ROOT";

DirectoryEntry vRoot = new DirectoryEntry(sitepath);
DirectoryWntry vDir = vRoot.Children.Add(virtualDirectoryName, 
                            "IIsWebVirtualDir");
vDir.CommitChanges();

vDir.Properties["Path"].Value = wwwroot + "\\" + virtualDirectoryName;
vDir.Properties["DefaultDoc"].Value = "Default.aspx";
vDir.Properties["DirBrowseFlags"].Value = 2147483648;
vDir.Commitchanges();
vRoot.CommitChanges();

Active Directory Code

The code below is broken apart logically into usage categories. Again, this is not intended to be a complete library, just the code that I use on a daily basis.

Active Directory Management

Collapse Copy Code
//These methods require these imports

//You must add a references in your project as well

using System.DirectoryServices;
using System.DirectoryServices.ActiveDirectory;

Translate the Friendly Domain Name to Fully Qualified Domain

Collapse Copy Code
public static string FriendlyDomainToLdapDomain(string friendlyDomainName)
{
    string ldapPath = null;
    try
    {
        DirectoryContext objContext = new DirectoryContext(
            DirectoryContextType.Domain, friendlyDomainName);
        Domain objDomain = Domain.GetDomain(objContext);
        ldapPath = objDomain.Name;
    }
    catch (DirectoryServicesCOMException e)
    {
        ldapPath = e.Message.ToString();
    }
    return ldapPath;
}

Enumerate Domains in the Current Forest

Collapse Copy Code
public static ArrayList EnumerateDomains()
{
    ArrayList alDomains = new ArrayList();
    Forest currentForest = Forest.GetCurrentForest();
    DomainCollection myDomains = currentForest.Domains;

    foreach (Domain objDomain in myDomains)
    {
        alDomains.Add(objDomain.Name);
    }
    return alDomains;
}

Enumerate Global Catalogs in the Current Forest

Collapse Copy Code
public static ArrayList EnumerateDomains()
{
    ArrayList alGCs = new ArrayList();
    Forest currentForest = Forest.GetCurrentForest();
    foreach (GlobalCatalog gc in currentForest.GlobalCatalogs)
    {
        alGCs.Add(gc.Name);
    }
    return alGCs;
}

Enumerate Domain Controllers in a Domain

Collapse Copy Code
public static ArrayList EnumerateDomainControllers()
{
    ArrayList alDcs = new ArrayList();
    Domain domain = Domain.GetCurrentDomain();
    foreach (DomainController dc in domain.DomainControllers)
    {
        alDcs.Add(dc.Name);
    }
    return alDcs;
}

Create a Trust Relationship

Collapse Copy Code
public void CreateTrust(string sourceForestName, string targetForestName)
{
    Forest sourceForest = Forest.GetForest(new DirectoryContext(
        DirectoryContextType.Forest, sourceForestName));

    Forest targetForest = Forest.GetForest(new DirectoryContext(
        DirectoryContextType.Forest, targetForestName));

    // create an inbound forest trust

    sourceForest.CreateTrustRelationship(targetForest,
        TrustDirection.Outbound);
}

Delete a Trust Relationship

Collapse Copy Code
public void DeleteTrust(string sourceForestName, string targetForestName)
{
    Forest sourceForest = Forest.GetForest(new DirectoryContext(
        DirectoryContextType.Forest, sourceForestName));

    Forest targetForest = Forest.GetForest(new DirectoryContext(
        DirectoryContextType.Forest, targetForestName));

    // delete forest trust

    sourceForest.DeleteTrustRelationship(targetForest);
}

Enumerate Objects in an OU

The parameter OuDn is the Organizational Unit distinguishedName such as OU=Users,dc=myDomain,dc=com

Collapse Copy Code
public ArrayList EnumerateOU(string OuDn)
{
    ArrayList alObjects = new ArrayList();
    try
    {
        DirectoryEntry directoryObject = new DirectoryEntry("LDAP://" + OuDn);
        foreach (DirectoryEntry child in directoryObject.Children)
        {
            string childPath = child.Path.ToString();
            alObjects.Add(childPath.Remove(0,7)); 
                //remove the LDAP prefix from the path

            child.Close();
            child.Dispose();
        }
        directoryObject.Close();
        directoryObject.Dispose();
    }
    catch (DirectoryServicesCOMException e)
    {
        Console.WriteLine("An Error Occurred: " + e.Message.ToString());
    }
    return alObjects;
}

Enumerate Directory Entry Settings

One of the nice things about the 2.0 classes is the ability to get and set a configuration object for your directoryEntry objects.

Collapse Copy Code
static void DirectoryEntryConfigurationSettings(string domainADsPath)
{
    // Bind to current domain

    DirectoryEntry entry = new DirectoryEntry(domainADsPath);
    DirectoryEntryConfiguration entryConfiguration = entry.Options;

    Console.WriteLine("Server: " + entryConfiguration.GetCurrentServerName());
    Console.WriteLine("Page Size: " + entryConfiguration.PageSize.ToString());
    Console.WriteLine("Password Encoding: " + 
        entryConfiguration.PasswordEncoding.ToString());
    Console.WriteLine("Password Port: " + 
        entryConfiguration.PasswordPort.ToString());
    Console.WriteLine("Referral: " + entryConfiguration.Referral.ToString());
    Console.WriteLine("Security Masks: " + 
        entryConfiguration.SecurityMasks.ToString());
    Console.WriteLine("Is Mutually Authenticated: " + 
        entryConfiguration.IsMutuallyAuthenticated().ToString());
    Console.WriteLine();
    Console.ReadLine();
}

Active Directory Objects

Collapse Copy Code
//These methods require these imports

//You must add a references in your project as well

using System.DirectoryServices;

Check for the Existence of an Object

This method does not need you to know the distinguishedName, you can concat strings or even guess a location and it will still run (and return false if not found).

Collapse Copy Code
public static bool Exists(string objectPath)
{
    bool found = false;
    if (DirectoryEntry.Exists("LDAP://" + objectPath))
    {
        found = true;
    }
    return found;
}

Move an Object from one Location to Another

It should be noted that the string newLocation should NOT include the CN= value of the object. The method will pull that from the objectLocation string for you. So object CN=group,OU=GROUPS,DC=contoso,DC=com is sent in as the objectLocation but the newLocation is something like: OU=NewOUParent,DC=contoso,DC=com. The method will take care of the CN=group.

Collapse Copy Code
public void Move(string objectLocation, string newLocation)
{
    //For brevity, removed existence checks

    DirectoryEntry eLocation = new DirectoryEntry("LDAP://" + objectLocation);
    DirectoryEntry nLocation = new DirectoryEntry("LDAP://" + newLocation);
    string newName = eLocation.Name;
    eLocation.MoveTo(nLocation, newName);
    nLocation.Close();
    eLocation.Close();
}

Enumerate Multi-String Attribute Values of an Object

This method includes a recursive flag in case you want to recursively dig up properties of properties such as enumerating all the member values of a group and then getting each member group's groups all the way up the tree.

Collapse Copy Code
public ArrayList AttributeValuesMultiString(string attributeName,
     string objectDn, ArrayList valuesCollection, bool recursive)
{
    DirectoryEntry ent = new DirectoryEntry(objectDn);
    PropertyValueCollection ValueCollection = ent.Properties[attributeName];
    IEnumerator en = ValueCollection.GetEnumerator();

    while (en.MoveNext())
    {
        if (en.Current != null)
        {
            if (!valuesCollection.Contains(en.Current.ToString()))
            {
                valuesCollection.Add(en.Current.ToString());
                if (recursive)
                {
                    AttributeValuesMultiString(attributeName, "LDAP://" +
                    en.Current.ToString(), valuesCollection, true);
                }
            }
        }
    }
    ent.Close();
    ent.Dispose();
    return valuesCollection;
}

Enumerate Single String Attribute Values of an Object

Collapse Copy Code
public string AttributeValuesSingleString
    (string attributeName, string objectDn)
{
    string strValue;
    DirectoryEntry ent = new DirectoryEntry(objectDn);
    strValue = ent.Properties[attributeName].Value.ToString();
    ent.Close();
    ent.Dispose();
    return strValue;
}

Enumerate an Object's Properties: The Ones with Values

Collapse Copy Code
public static ArrayList GetUsedAttributes(string objectDn)
{
    DirectoryEntry objRootDSE = new DirectoryEntry("LDAP://" + objectDn);
    ArrayList props = new ArrayList();

    foreach (string strAttrName in objRootDSE.Properties.PropertyNames)
    {
        props.Add(strAttrName);
    }
    return props;
}

Get an Object DistinguishedName: ADO.NET search (ADVANCED)

This method is the glue that ties all the methods together since most all the methods require the consumer to provide a distinguishedName. Wherever you put this code, you must ensure that you add these enumerations as well. This allows the consumers to specify the type of object to search for and whether they want the distinguishedName returned or the objectGUID.

Collapse Copy Code
public enum objectClass
{
    user, group, computer
}
public enum returnType
{
 distinguishedName, ObjectGUID
}

A call to this class might look like:

myObjectReference.GetObjectDistinguishedName(objectClass.user, returnType.ObjectGUID, "john.q.public", "contoso.com")

Collapse Copy Code
public string GetObjectDistinguishedName(objectClass objectCls,
    returnType returnValue, string objectName, string LdapDomain)
{
    string distinguishedName = string.Empty;
    string connectionPrefix = "LDAP://" + LdapDomain;
    DirectoryEntry entry = new DirectoryEntry(connectionPrefix);
    DirectorySearcher mySearcher = new DirectorySearcher(entry);

    switch (objectCls)
    {
        case objectClass.user:
            mySearcher.Filter = "(&(objectClass=user)
        (|(cn=" + objectName + ")(sAMAccountName=" + objectName + ")))";
            break;
        case objectClass.group:
            mySearcher.Filter = "(&(objectClass=group)
        (|(cn=" + objectName + ")(dn=" + objectName + ")))";
            break;
        case objectClass.computer:
            mySearcher.Filter = "(&(objectClass=computer)
            (|(cn=" + objectName + ")(dn=" + objectName + ")))";
            break;
    }
    SearchResult result = mySearcher.FindOne();

    if (result == null)
    {
        throw new NullReferenceException
        ("unable to locate the distinguishedName for the object " +
        objectName + " in the " + LdapDomain + " domain");
    }
    DirectoryEntry directoryObject = result.GetDirectoryEntry();
    if (returnValue.Equals(returnType.distinguishedName))
    {
        distinguishedName = "LDAP://" + directoryObject.Properties
            ["distinguishedName"].Value;
    }
    if (returnValue.Equals(returnType.ObjectGUID))
    {
        distinguishedName = directoryObject.Guid.ToString();
    }
    entry.Close();
    entry.Dispose();
    mySearcher.Dispose();
    return distinguishedName;
}

Convert distinguishedName to ObjectGUID

Collapse Copy Code
public string ConvertDNtoGUID(string objectDN)
{
    //Removed logic to check existence first

    DirectoryEntry directoryObject = new DirectoryEntry(objectDN);
    return directoryObject.Guid.ToString();
}

Convert an ObjectGUID to OctectString: The Native ObjectGUID

Collapse Copy Code
public static string ConvertGuidToOctectString(string objectGuid)
{
    System.Guid guid = new Guid(objectGuid);
    byte[] byteGuid = guid.ToByteArray();
    string queryGuid = "";
    foreach (byte b in byteGuid)
    {
        queryGuid += @"\" + b.ToString("x2");
    }
    return queryGuid;
}

Search by ObjectGUID or convert ObjectGUID to distinguishedName

Collapse Copy Code
public static string ConvertGuidToDn(string GUID)
{
      DirectoryEntry ent = new DirectoryEntry();
      String ADGuid = ent.NativeGuid;
      DirectoryEntry x = new DirectoryEntry("LDAP://{GUID=" + ADGuid + ">"); 
          //change the { to <>

      return x.Path.Remove(0,7); //remove the LDAP prefix from the path

}

Publish Network Shares in Active Directory

Collapse Copy Code
 //Example

 private void init()
{
    CreateShareEntry("OU=HOME,dc=baileysoft,dc=com",
        "Music", @"\\192.168.2.1\Music", "mp3 Server Share");
    Console.ReadLine();
}

//Actual Method

public void CreateShareEntry(string ldapPath,
    string shareName, string shareUncPath, string shareDescription)
{
    string oGUID = string.Empty;
    string connectionPrefix = "LDAP://" + ldapPath;
    DirectoryEntry directoryObject = new DirectoryEntry(connectionPrefix);
    DirectoryEntry networkShare = directoryObject.Children.Add("CN=" + 
        shareName, "volume");
    networkShare.Properties["uNCName"].Value = shareUncPath;
    networkShare.Properties["Description"].Value = shareDescription;
    networkShare.CommitChanges();

    directoryObject.Close();
    networkShare.Close();
}

Create a New Security Group

Note: by default if no GroupType property is set, the group is created as a domain security group.

Collapse Copy Code
public void Create(string ouPath, string name)
{
    if (!DirectoryEntry.Exists("LDAP://CN=" + name + "," + ouPath))
    {
        try
        {
            DirectoryEntry entry = new DirectoryEntry("LDAP://" + ouPath);
            DirectoryEntry group = entry.Children.Add("CN=" + name, "group");
            group.Properties["sAmAccountName"].Value = name;
            group.CommitChanges();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message.ToString());
        }
    }
    else { Console.WriteLine(path + " already exists"); }
}

Delete a group

Collapse Copy Code
public void Delete(string ouPath, string groupPath)
{
    if (DirectoryEntry.Exists("LDAP://" + groupPath))
    {
        try
        {
            DirectoryEntry entry = new DirectoryEntry("LDAP://" + ouPath);
            DirectoryEntry group = new DirectoryEntry("LDAP://" + groupPath);
            entry.Children.Remove(group);
            group.CommitChanges();
        }
        catch (Exception e)
        {
            Console.WriteLine(e.Message.ToString());
        }
    }
    else
    { 
        Console.WriteLine(path + " doesn't exist"); 
    }
}

Active Directory Users Tasks

Collapse Copy Code
//These methods require these imports

//You must add a references in your project as well

using System.DirectoryServices;

Authenticate a User Against the Directory

Per John Storer, thanks for sharing.

Collapse Copy Code
private bool Authenticate(string userName,
    string password, string domain)
{
    bool authentic = false;
    try
    {
        DirectoryEntry entry = new DirectoryEntry("LDAP://" + domain,
            userName, password);
        object nativeObject = entry.NativeObject;
        authentic = true;
    }
    catch (DirectoryServicesCOMException) { }
    return authentic;
}

Add User to Group

Collapse Copy Code
public void AddToGroup(string userDn, string groupDn)
{
    try
    {
        DirectoryEntry dirEntry = new DirectoryEntry("LDAP://" + groupDn);
        dirEntry.Properties["member"].Add(userDn);
        dirEntry.CommitChanges();
        dirEntry.Close();
    }
    catch (System.DirectoryServices.DirectoryServicesCOMException E)
    {
        //doSomething with E.Message.ToString();

    }
}

Remove User from Group

Collapse Copy Code
public void RemoveUserFromGroup(string userDn, string groupDn)
{
    try
    {
        DirectoryEntry dirEntry = new DirectoryEntry("LDAP://" + groupDn);
        dirEntry.Properties["member"].Remove(userDn);
        dirEntry.CommitChanges();
        dirEntry.Close();
    }
    catch (System.DirectoryServices.DirectoryServicesCOMException E)
    {
        //doSomething with E.Message.ToString();

    }
}

Get User Group Memberships of the Logged in User from ASP.NET

Collapse Copy Code
public ArrayList Groups()
{
    ArrayList groups = new ArrayList();
    foreach (System.Security.Principal.IdentityReference group in
        System.Web.HttpContext.Current.Request.LogonUserIdentity.Groups)
    {
        groups.Add(group.Translate(typeof
            (System.Security.Principal.NTAccount)).ToString());
    }
    return groups;
}

Get User Group Memberships

This method requires that you have the AttributeValuesMultiString method earlier in the article included in your class.

Collapse Copy Code
public ArrayList Groups(string userDn, bool recursive)
{
    ArrayList groupMemberships = new ArrayList();
    return AttributeValuesMultiString("memberOf", userDn,
        groupMemberships, recursive);
}

Create User Account

Collapse Copy Code
public string CreateUserAccount(string ldapPath, string userName, 
    string userPassword)
{
    try
    {
        string oGUID = string.Empty;
        string connectionPrefix = "LDAP://" + ldapPath;
        DirectoryEntry dirEntry = new DirectoryEntry(connectionPrefix);
        DirectoryEntry newUser = dirEntry.Children.Add
            ("CN=" + userName, "user");
        newUser.Properties["samAccountName"].Value = userName;
        newUser.CommitChanges();
        oGUID = newUser.Guid.ToString();

        newUser.Invoke("SetPassword", new object[] { userPassword });
        newUser.CommitChanges();
        dirEntry.Close();
        newUser.Close();
    }
    catch (System.DirectoryServices.DirectoryServicesCOMException E)
    {
        //DoSomethingwith --> E.Message.ToString();

    }
    return oGUID;
}

Dealing with User Passwords

There are some specifics to understand when dealing with user passwords and boundaries around passwords such as forcing a user to change their password on the next logon, denying the user the right to change their own passwords, setting passwords to never expire, to when to expire, and these tasks can be accomplished using UserAccountControl flags that are demonstrated in the proceeding sections. Please refer to this great MSDN article: Managing User Passwords for examples and documentation regarding these features. (thanks to Daniel Ocean for identifying this resource)

Collapse Copy Code
//Add this to the create account method

int val = (int)newUser.Properties["userAccountControl"].Value; 
     //newUser is DirectoryEntry object

newUser.Properties["userAccountControl"].Value = val | 0x80000; 
    //ADS_UF_TRUSTED_FOR_DELEGATION

All UserAccountControl flags

Collapse Copy Code
CONST   HEX
-------------------------------
        SCRIPT 0x0001
        ACCOUNTDISABLE 0x0002
        HOMEDIR_REQUIRED 0x0008
        LOCKOUT 0x0010
        PASSWD_NOTREQD 0x0020
        PASSWD_CANT_CHANGE 0x0040
        ENCRYPTED_TEXT_PWD_ALLOWED 0x0080
        TEMP_DUPLICATE_ACCOUNT 0x0100
        NORMAL_ACCOUNT 0x0200
        INTERDOMAIN_TRUST_ACCOUNT 0x0800
        WORKSTATION_TRUST_ACCOUNT 0x1000
        SERVER_TRUST_ACCOUNT 0x2000
        DONT_EXPIRE_PASSWORD 0x10000
        MNS_LOGON_ACCOUNT 0x20000
        SMARTCARD_REQUIRED 0x40000
        TRUSTED_FOR_DELEGATION 0x80000
        NOT_DELEGATED 0x100000
        USE_DES_KEY_ONLY 0x200000
        DONT_REQ_PREAUTH 0x400000
        PASSWORD_EXPIRED 0x800000
        TRUSTED_TO_AUTH_FOR_DELEGATION 0x1000000

Enable a User Account

Collapse Copy Code
public void Enable(string userDn)
{
    try
    {
        DirectoryEntry user = new DirectoryEntry(userDn);
        int val = (int)user.Properties["userAccountControl"].Value;
        user.Properties["userAccountControl"].Value = val & ~0x2; 
            //ADS_UF_NORMAL_ACCOUNT;

        user.CommitChanges();
        user.Close();
    }
    catch (System.DirectoryServices.DirectoryServicesCOMException E)
    {
        //DoSomethingWith --> E.Message.ToString();

    }
}

Disable a User Account

Collapse Copy Code
public void Disable(string userDn)
{
    try
    {
        DirectoryEntry user = new DirectoryEntry(userDn);
        int val = (int)user.Properties["userAccountControl"].Value;
        user.Properties["userAccountControl"].Value = val | 0x2; 
             //ADS_UF_ACCOUNTDISABLE;

        user.CommitChanges();
        user.Close();
    }
    catch (System.DirectoryServices.DirectoryServicesCOMException E)
    {
        //DoSomethingWith --> E.Message.ToString();

    }
}

Unlock a User Account

Collapse Copy Code
public void Unlock(string userDn)
{
    try
    {
        DirectoryEntry uEntry = new DirectoryEntry(userDn);
        uEntry.Properties["LockOutTime"].Value = 0; //unlock account

        uEntry.CommitChanges(); //may not be needed but adding it anyways

        uEntry.Close();
    }
    catch (System.DirectoryServices.DirectoryServicesCOMException E)
    {
        //DoSomethingWith --> E.Message.ToString();

    }
}

Alternate Lock/Unlock Account

It's hard to find code to lock an account. Here is my code to lock or unlock an account. dEntry is class variable already set to a user account. Shared by dextrous1.

Collapse Copy Code
/// <summary>
/// Gets or sets a value indicating if the user account is locked out
/// </summary>
public bool IsLocked
{
    get { return Convert.ToBoolean(dEntry.InvokeGet("IsAccountLocked")); }
    set { dEntry.InvokeSet("IsAccountLocked", value); }
}

Reset a User Password

Collapse Copy Code
public void ResetPassword(string userDn, string password)
{
    DirectoryEntry uEntry = new DirectoryEntry(userDn);
    uEntry.Invoke("SetPassword", new object[] { password });
    uEntry.Properties["LockOutTime"].Value = 0; //unlock account

    uEntry.Close();
}

Rename an Object

Collapse Copy Code
public static void Rename(string objectDn, string newName)
{
    DirectoryEntry child = new DirectoryEntry("LDAP://" + objectDn);
   child.Rename("CN=" + newName);
}

Conclusion

I would have liked to include a sample project but my professional code is so tightly integrated with customer proprietary code that it was not feasible at this time.

UPDATE

If you would like to see an extremely simple implementation of some of this code check out the DirectoryServicesBrowserDialog I posted some time ago. This should demonstrate to those of you who are having trouble adding the proper references or having other difficulties.

I hope this helps out all those programmers that were like me and had to get up to speed really quickly and lost countless hours studying the System.DirectoryServices assembly trying to dig up answers on how to do AD tasks.

If you have some additional segments of code that are small but efficient that you'd like to include send them to me and I'll add them to this document. 

반응형

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

C# 초보자분들을 위한 기초강좌  (0) 2010.01.24
WMI 를 활용한 원격(로컬)서버 CPU사용량 실시간 모니터링하기  (0) 2010.01.21
XML 파일 읽기  (0) 2010.01.20
Remote Monitoring  (0) 2009.07.19
알림창, Messenger  (0) 2009.07.19
반응형
반응형
반응형
반응형

+ Recent posts

반응형