2012년 12월 27일 목요일

C# 서비스등록하기



먼저 이 게시물의 내용은 VS2003 한글판에서 작업된 것임을 알립니다.
VS 버젼에 따라서 약간씩 메뉴 구성이 다릅니다.  인터페이스가 조금씩 다를뿐 방법은 동일합니다.
이 게시물의 게시 목적은 간략한 서비스 생성 및 등록 방법에 대한 정보 공유입니다.

1. 프로젝트 생성
- 아래와 같이 C# 프로젝트 내의 WINDOWS 서비스 프로젝트를 선택하여 프로젝트를 생성 시킵니다.
( 다른 언어도 WINDOWS 서비스 프로젝트가 제공된다면 동일하게 작업 가능합니다.)


2. 서비스 기본 정보 설정
- 서비스의 이름을 정의합니다.  아래와 같이 프로젝트 생성후에 기본적으로 생성되는 WindowsService.cs 의 디자인 모드에서
오른쪽 마우스 버튼을 클릭하여 [속성]을  누르면 기본 정보를 설정하는 메뉴가 나타납니다.
- 이 메뉴에서 서비스 이름을 저장하십시오.


3. 서비스 설치 관리자 추가
- 속성 창 하단에 있는 설치 관리자를 클릭하여 주십시오.
- 설치 관리자는 윈도우 서비스 목록에 현재 작업 중인 서비스를 등록하는 역할을 담당합니다.


- 설치관리자 추가 후 솔루션 탐색기의 모습입니다.
참조에 못보던 모듈 2개가 추가되어 있습니다.  상세 정보는 MSDN 을 참고 하십시오.


4. 세부 작업 정의
- 기본적인 서비스로서의 등록에 필요한 작업은 여기까지입니다.
- 지금부터 남은것은 코딩입니다.  이 서비스가 하는 일을 정의 해야겠죠?  서비스 관리자에서 서비스를 시작할때 호출되는 메소드가 OnStart 이며 종료시에 호출되는 메소드가 OnStop 입니다. 이 각각의 메소드들 내부에 원하는 작업에 대한 코딩을 하시면 됩니다.


여기까지 완료되었다면 빌드를 하십시오.

5. INSTALLUTIL 로 서비스 등록하기
- 닷넷 프레임웍을 설치하면 각각의 버젼마다 INSTALLUTIL 이라는 파일이 설치 됩니다.
- 이 INSTALLUTIL 은 윈도우즈 서비스 프로젝트로 생성된 결과물을 실제 윈도우 서비스에 등록하는 역할을 합니다.


결과물의 파일명이 SVC_TEST.EXE 라면 커멘드 창을 여신 후에 결과물이 있는 폴더로 이동 하신 후 아래와 같은 명령어를 쳐 주시면 등록이 완료 됩니다.

C:\WINDOWS\Microsoft.NET\Framework\v1.1.4322\INSTALLUTIL  SVC_TEST.EXE /i

서비스 목록에 작업한 서비스가 등록되었는지에 대한 확인은 [관리도구]-[서비스]를 찾아가시면 됩니다.



이상으로 C# 을 이용한 윈도우 서비스 생성에 대한 설명을 마칩니다.

주의사항~

ServiceProcessInstaller 의 Account 값은 별다른 목적이 없으면 LocalSystem 으로 설정 하여야 합니다.

등록시 계정 관련 오류가 나는 경우 대부분 계정 정보가 잘못 등록된 케이스..





http://www.devpia.com/Maeul/Contents/Detail.aspx?BoardID=18&MAEULNO=8&no=1713&page=12


C# timer 쓰레드타이머


출처 : http://msdn.microsoft.com/ko-kr/library/swx5easy.aspx
출처 : http://blog.daum.net/starkcb/117


닷넷 프레임워크에는 무려 3가지 서로 다른 Timer 를 제공하고 있다는 겁니다. 바로 아래 3가지 Timer 입니다
1. System.WIndows.Forms.Timer
2. System.Threading.Timer
3. System.Timers.Timer

닷넷이 이 3가지 Timer 를 각각 제공하는 이유가 무엇일까요?
필자는 이 문제(?)에 대해, 몇 년전에 의구심을 가졌읍니다만, 당시 의구심만 가진채 그냥 세월을 보내 버렸습니다 --;
그리고는 대략 아는 지식으로 대략 적절 할 것 같은(?) Timer을 사용해 왔던 것 같습니다
게을렀던 거죠. 의구심이 들면 파고들어 정복하는 사람이 성공합니다. ㅋㅋ , 기술이든 인생이든...
예기가 다른 길로 세네요.. ㅎ,

우선 이 세가지 서로다른 Timer 의 msdn 설명을 볼까요

1) System.Windows.Forms.Timer
사용자가 정의한 간격마다 이벤트를 발생시키는 타이머를 구현합니다. 이 타이머는 Windows Forms 응용 프로그램에서
사용할 수 있도록 최적화되었으며 창에서 사용해야 합니다

2) System.Threading.Timer
지정된 간격으로 메서드를 실행하는 메커니즘을 제공합니다

3) System.Timers.Timer
응용 프로그램에 되풀이 이벤트를 생성합니다


msdn 설명을 봐도,
'System.WIndows.Forms.Timer 가 윈도우 응용프로그램에 최적화 되었다' 라는 말 빼고는 거의 차이점을 느낄 수 없네요

물론 msdn은 보다 상세한 내용을 더 기술되어 있습니다만, 이 글에서는 이 세가지 Timer 의 차이점을 크게 두 가지 측면에서
살펴 볼까 합니다
1. 사용법상의 차이점
2. 수행되는 Thread 환경의 차이점

* 사용법의 차이
먼저 사용법의 차이를 알아보죠
사용법의 차이는 말 그대로 사용법입니다. 이것이 원리는 아니죠.
원리가 다르기 때문에 사용법이 다른 것이지, 사용법이 다르기 때문에 원리가 다른건 아닙니다
그럼에도, 사용법 차이점부터 알아 보는 것은.......... 쉽기 때문이죠 ^^;
(개발자 여러분, 사용법만 익히지 말고 원리를 익힙시다)

1. System.Windows.Forms.Timer 사용법
윈도우 응용프로그램 개발자들에겐 아마 가장 익숙한 Timer 일 것입니다
- 객체 생성
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
- 반복 주기 및 작업 설정
timer.Interval = 1000; //주기 설정
timer.Tick += new EventHandler(timer_Tick); //주기마다 실행되는 이벤트 등록
void tmrWindowsFormsTimer_Tick(object sender, System.EventArgs e)
{
      //수행해야할 작업
}
- Timer 시작
timer.Enable = true 또는 timer.Start();
- Timer 중지
timer.Enable = false 또는 timer.Stop();


2. System.Threading.Timer 사용법
- 객체 생성
Timer 객체를 생성할 때, 반복적으로 실행하게 될 메서드를 콜백 메서드로 등록해야 합니다
System.Threading.Timer timer = new System.Threading.Timer(CallBack);
- 반복 주기 및 작업 설정
이 Timer 에는 Change 메서드가 있는데, 이 메서드는 dueTime과 period 를 입력받습니다
dueTime은 Timer 가 시작하기 전 대기(지연)시간이며 period는 반복 주기입니다
timer.Change(0, 1000);

그리고 반복 실행 작업이,
윈도우 응용프로그램의 UI Thread와 연관된다면, Cross Thread 문제가 발생하기 때문에 Invoke나 BeginInvoke를
통해 핸들링 해야 합니다.
앞서, Timer 객세 생성시 등록한 콜백 메서드에서 BeginInvoke를 통해 UI 쓰레드를 핸들링 할 수 있습니다

delegate void TimerEventFiredDelegate();
void CallBack(Object state)
{
    BeginInvoke(new TimerEventFiredDelegate(Work));
}
       
private void Work()
{
     //수행해야할 작업(UI Thread 핸들링 가능)
}

- Timer 시작
위의 Change 메서드의 dueTime 이 0 이므로 그 즉시 시작된다. Start와 같은 별도의 시작 명령이 존재하지 않음
- Timer 중지
timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
dueTime와 period 를 무한대로 잡아서 Timer 가 실행되지 않도록 하는 것이 중지하는 것과 같습니다


3. System.Timers.Timer 사용법
- 객체 생성
System.Timers.Timer timer = new System.Timers.Timer();

- 반복 주기 및 작업 설정
timer.Interval = 1000;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);  //주기마다 실행되는 이벤트 등록

이 Timer 역시 UI Thread를 핸들링 하기 위해서 Invoke 나 BeginInvoke를 이용해야 합니다
delegate void TimerEventFiredDelegate();
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
    BeginInvoke(new TimerEventFiredDelegate(Work));           
}
private void Work()
{
     //수행해야할 작업(UI Thread 핸들링 가능)
}

- Timer 시작
timer.Enable = true 또는 timer.Start();
- Timer 중지
timer.Enable = false 또는 timer.Stop();

결론적으로 보면,
Timer 객체의 사용법 자체는 그리 어렵지 않습니다. 또한 세 가지 Timer 의 사용법은 대동소이 함을 알 수 있습니다
다만 윈도위 응용프로그램에서 Timer 를 사용할 때 System.WIndows.Forms.Timer 를 제외하고는
UI Thread 에서 만들어진 컨트롤에 접근하려면 크로스 쓰레드 문제가 있으므로 마샬링 된 호출(Invoke / BeginInvoke) 를
이용해야 하는 차이점이 있습니다
msdn의 설명처럼 System.Windows.Forms.Timer 는 윈도우 응용프로그램에 최적화 되어 있나 보네요..

그럼 왜 System.Windows.Forms.Timer 는 크로스쓰레드 문제가 발생하지 않을까요?
그리고 정말 사용법 처럼 크게 차이가 나지 않는 걸까요?

다음에 설명할, 두번째 관점인 '수행되는 Thread 환경의 차이점'에서 이를 알아보도록 하죠



* 수행되는 쓰레드(Thread) 환경의 차이
앞서 사용법에서 UI Thread 라는 말을 했습니다
윈도우 응용프로그램을 예로 들어, 버턴이나 각종 컨트롤이 생성되고 핸들링 되는 것은 UI Thread 상에서 이루어집니다

이와 다른 개념이 Work Thread 인데요, 기본 쓰레드(Default Thread) 이외에
개발자가 별도의 쓰레드를 생성하여 작업을 실행한다면 이는 Work Thread(작업자 쓰레드) 라 합니다
또한 UI Thread 입장에서는, 닷넷의 ThreadPool 에 의해 실행되는 쓰레드도 Work Thread 로 볼 수 있습니다

쓰레드가 다르다면 쓰레드의 고유번호도 당연히 다릅니다
System.Threading.Thread.CurrentThread.IsThreadPoolThread 속성은 현재 쓰레드의 고유 식별자 값을 가져 옵니다
우린 이 속성을 통해 Timer 객체가 수행되는 쓰레드를 알아 보도록 하겠습니다

1. System.Windows.Forms.Timer 의 쓰레드 환경
윈도우 응용프로그램에 최적회 되어 있다는 이 Timer 는 윈도우 응용프로그램 기본 쓰레드와 동일한 쓰레드 상에서 동작합니다
이를 확인하기 위해, 다음과 같이 코드 중간에 IsThreadPoolThread  속성을 확인해 봅니다

- 기본 쓰레드의 고유 번호를 확인한다
윈도우응용프로그램 생성자나 기타 이벤트에서 아래 코드를 기입합니다
MessageBox.Show(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());

- Timer 쓰레드의 고유번호를 확인한다
Timer 의 Tick 이벤트에서 다음의 코드를 기입합니다
void timer1_Tick(object sender, EventArgs e)
{
     MessageBox.Show(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
     //수행해야할 작업
}

이렇게 확인 해 보면 두 쓰레드는 동일한 고유번호를 반환하게 됩니다
이 말은 곧, 윈도우응용프로가램의 기본 쓰레드인 UI Thread 상에서 Timer이 동작한다는 것을 짐작할 수 있습니다
즉 멀티 쓰레드 환경이 아닌 것이죠
예로, Tick 이벤트에 시간이 긴~ 작업이 수행된다면 프로그램은 그 시간 동안 블럭 된 대기 한다는 것입니다
Timer 의 동작이 기본 프로그램 동작과 독립적으로 수행된다고 생각하시면 안됩니다

2. System.Threading.Timer 의 쓰레드 환경
역시 앞서와 같이 기본 쓰레드와 Timer 쓰레드의 고유번호를 확인 해 봅니다

- 기본 쓰레드의 고유 번호를 확인한다
윈도우응용프로그램 생성자나 기타 이벤트에서 아래 코드를 기입합니다
MessageBox.Show(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());

- Timer 쓰레드의 고유번호를 확인한다
CallBack 메서드에서 다음과 같이 코드를 기입합니다
void CallBack(Object state)
{
      MessageBox.Show(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
      BeginInvoke(new TimerEventFiredDelegate(Work));
 }

어떻습니까? 두 쓰레드는 다른 고유번호를 반환하지요
즉 UI Thread 와 다른 쓰레드, 즉 Work Thread(작업자 쓰레드)임을 알 수 있습니다
이는 곧 멀티 쓰레드가 된 셈이죠. 두 쓰레드는 서로 독립적으로 수행될 것입니다
앞서, System.Windows.Forms.Timer 객체와는 달리 CallBack 메서드에 시간이 오래 걸리는 작업을 수행해도
프로그램이 대기상태로 빠지는 않죠. Timer 동작이 기본 프로그램의 동작과는 독립적으로 수행되는 것이죠.

참고로 이 Timer 는 닷넷의 ThreadPool(쓰레드풀) 에서 관리합니다

3. System.Timers.Timer 의 쓰레드 환경
결론부터 말하자만, 이 Timer 는 기본 쓰레드에서 수행될 수도 있고, 작업자 쓰레드에서 수행될 수도 있습니다
만일, SynchronizingObject 속성을 폼객체로 한다면 Timer는 UI 쓰레드 상에서 동작할 것이며
이 속성을 지정하지 않는다면, 작업자 쓰레드 상에서 동작하게 됩니다

아래와 같이 SynchronizingObject 속성의 설정 여부에 따른 ManagedThreadid 값을 확인해 보기 바랍니다
timer.SynchronizingObject = this;

타이머 쓰레드의 고유번호를 알기 위해 Elapsed 이벤트에
MessageBox.Show(System.Threading.Thread.CurrentThread.ManagedThreadId.ToString());
를 확인해 보세요

---
결국 Timer 의 실행이 기본 쓰레드에서 하느냐, 작업자 쓰레드 에서 하느냐에 차이인데요,
앞서, 사용법의 차이를 살펴 봤을 때 System.Windows.Forms.Timer 객체를 제외하고는 윈도우응용프로그램의 UI 컨트롤
핸들링 시 크로스 도메인 문제가 발생했던 원인이 되는 것입니다



* 기타 차이점 및 요약, 참조
아래 표는 msdn magazine에 소개된 세 Timer 의 차이점에 대한 표입니다
우리가 알아 본 내용 이외에도, 쓰레드 안정성(동기화 문제)에 대한 내용도 있습니다

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /> 
System.Windows.Forms
System.Timers
System.Threading
Timer event runs on what thread?
UI thread
UI or worker thread
Worker thread
Instances are thread safe?
No
Yes
No
Familiar/intuitive object model?
Yes
Yes
No
Requires Windows Forms?
Yes
No
No
Metronome-quality beat?
No
Yes*
Yes*
Timer event supports state object?
No
No
Yes
Initial timer event can be scheduled?
No
No
Yes
Class supports inheritance?
Yes
Yes
No
* Depending on the availability of system resources (for example, worker threads)


마지막으로 msdn의 설명을 옮기며 마칩니다
 

서버 타이머, Windows 타이머 및 스레드 타이머

Visual Studio 및 .NET Framework에는 세 개의 타이머 컨트롤 즉, 도구 상자의 구성 요소 탭에서 볼 수 있는 서버 기반 타이머, 도구 상자의 Windows Forms 탭에서 볼 수 있는 표준 Windows 기반 타이머 및 프로그래밍 방식으로만 사용할 수 있는 스레드 타이머가 있습니다.

Windows 기반 타이머는 Visual Basic 1.0 이상의 버전에 있으며 지금까지 크게 변경되지 않았습니다.
이 타이머는 Windows Forms 응용 프로그램에서 사용하도록 최적화되어 있습니다.
서버 기반 타이머는 일반 타이머를 서버 환경에서 최적으로 실행되도록 업데이트한 것입니다.

스레드 타이머는 이벤트 대신 콜백 메서드를 사용하는 간단한 소형 타이머로서 스레드 풀 스레드에서 제공합니다.
Win32 아키텍처에는 UI 스레드와 작업자 스레드라는 두 종류의 스레드가 있습니다.
UI 스레드는 대부분의 시간을 유휴 상태로 보내며 메시지 루프에 메시지가 도착할 때까지 기다립니다. 메시지가 도착하면
이 메시지를 처리하고 다음 메시지가 도착할 때까지 기다립니다. 이에 비해 작업자 스레드는 백그라운드 처리를 수행하는 데 사용하며 메시지 루프를 사용하지 않습니다.

Windows 타이머와 서버 기반 타이머는 모두 Interval 속성을 사용하여 실행됩니다.
스레드 타이머의 간격은 <?XML:NAMESPACE PREFIX = MSHelp NS = "http://msdn.microsoft.com/mshelp" />Timer 생성자에서 설정됩니다.
스레드에서 타이머를 다루는 방식을 보면 알 수 있듯이 각 타이머의 용도는 서로 다릅니다.
  • Windows 타이머는 UI 스레드가 프로세싱을 수행하는 데 사용하는 단일 스레드 환경을 위해 설계되었습니다. Windows 타이머의 정확도는 55밀리초로 제한되어 있습니다. 이 일반 타이머는 사용자 코드에서 사용할 수 있는
    UI 메시지 펌프가 필요하며 항상 동일한 스레드에서 실행되거나 다른 스레드로 마샬링됩니다.
    이 기능은 COM 구성 요소의 성능을 저하시킵니다. 
  • 서버 기반 타이머는 다중 스레드 환경에서 작업자 스레드와 함께 사용하도록 설계되었습니다. 두 스레드는 서로 다른 아키텍처를 사용하므로 서버 기반 타이머가 Windows 타이머보다 정확합니다.
    서버 타이머는 스레드 사이를 이동하면서 발생한 이벤트를 처리할 수 있습니다. 
  • 스레드 타이머는 메시지가 스레드에서 펌프되지 않는 경우에 유용합니다.
    예를 들어, Windows 기반 타이머는 운영 체제의 타이머 지원 기능에 의존하며 스레드에서 메시지를 펌프하지 않을 경우에는 타이머 관련 이벤트가 발생하지 않습니다. 이 경우에는 스레드 타이머가 보다 더 유용합니다.
Windows 타이머는 System.Windows.Forms 네임스페이스에, 서버 타이머는 System.Timers 네임스페이스에 그리고 스레드 타이머는 System.Threading 네임스페이스에 있습니다.

오류 : An error occurred while validating. HRESULT = '8000000A'


오류 : An error occurred while validating.  HRESULT = '8000000A'

오류가 발생하는 파일 Setup.vdproj 을 메모장(notepad) 로 열고 , 그 안에 내용 중에 

“SccProjectName” = “8:”
“SccLocalPath” = “8:”
“SccAuxPath” = “8:”
“SccProvider” = “8:”

이 내용을 삭제하고 빌드를 하거나 삭제 후 저장 다시 원복 후에 저장하여 빌드해도

문제 없이 오류가 발생하지 않는다.

2012년 12월 26일 수요일

MakeCert.exe download - Certificate Creation tool

MakeCert.exe download - Certificate Creation tool



Certificate Creation tool ( MakeCert.exe )


The Certificate Creation tool generates X.509 certificates for testing purposes only. It creates a public and private key pair for digital signatures and stores it in a certificate file.



http://gallery.technet.microsoft.com/Certificate-Creation-tool-5b7c054d

ContextSwitchDeadlock was detected when debugging in Visual Studio 2005

I've been having issues with a "ContextSwitchDeadlock was detected" error message popping up when debugging. The message text is a baffling

The CLR has been unable to transition from COM context 0x197060 to COM context 0x196ef0 for 60 seconds. The thread that owns the destination context/apartment is most likely either doing a non pumping wait or processing a very long running operation without pumping Windows messages. This situation generally has a negative performance impact and may even lead to the application becoming non responsive or memory usage accumulating continually over time. To avoid this problem, all single threaded apartment (STA) threads should use pumping wait primitives (such as CoWaitForMultipleHandles) and routinely pump messages during long running operations.

I've read a few things about a config file, but I couldn't get this to work reliably. Instead, you can switch the MDA off by going to:

Debug -> Exceptions -> Managed Debug Assistants

and unchecking the ContextSwitchDeadlock item. Thanks to Scott Munro.

CLR에서 60초 동안 COM 컨텍스트 0x606b88에서 COM 컨텍스트 0x606e68(으)로 전환하지 못했습니다


CLR에서 60초 동안 COM 컨텍스트 0x606b88에서 COM 컨텍스트 0x606e68(으)로 전환하지 못했습니다.
대상 컨텍스트/아파트를 소유하는 스레드가 펌프 대기를 수행하지 않거나, Windows 메시지를 펌프하지 않고 매우 긴 실행 작업을 처리하고 있는 것 같습니다.
이러한 상황은 대개 성능에 부정적인 영향을 주며 응용 프로그램이 응답하지 않거나 시간이 흐름에 따라 메모리 사용이 증가하는 문제로 이어질 수도 있습니다.
이 문제를 방지하려면 모든 STA(Single Threaded Apartment) 스레드가 펌프 대기 기본 형식(예: CoWaitForMultipleHandles)을 사용하고
긴 실행 작업 동안 지속적으로 메시지를 펌프해야 합니다.





해결방법은
VS->디버그->예외->Managed Debug Assistants->ContextSwitchDeadlock 체크해제 하기

출처 : http://monsterwave.tistory.com/48



2012년 12월 25일 화요일

실제 근무일수 구하기


select id
, substring(CONVERT(VARCHAR(20), date, 120), 1, 4) as year
, substring(CONVERT(VARCHAR(20), date, 120), 6, 2) as month
, (COUNT(0) - COUNT(hday)) as cnt
from timecard as a
left outer join
(
select substring(CONVERT(VARCHAR(20), date, 120), 1, 10) as hday
from holiday
) as h
on substring(CONVERT(VARCHAR(20), a.date, 120), 1, 10) = h.hday
where datename(dw,date)<>'토요일' and datename(dw,date)<>'일요일'
and a.status<>'퇴근'
group by id, substring(CONVERT(VARCHAR(20), date, 120), 1, 4)
, substring(CONVERT(VARCHAR(20), date, 120), 6, 2)
order by year, month, id



1.     근무일은 출근/지각(퇴근 제외) 된 일수에서 토,일요일을 제외하고 휴일을 제외한 일수입니다.
2.     공휴일날 출근할 경우 근무일수에서 제외됩니다.
3.     토요일, 일요일 출근한 경우 근무일수에서 제외됩니다.
4.     공가, 휴가에 출근한 경우 출근일로 계산됩니다.
A.     공휴일과 주말이 겹치는 경우 공휴일로 계산되지 않습니다. (주말에서 빠졌기 때문에)

mssql 요일 구하기

mssql 요일 구하기


select count(case when a.days not in ('일요일') then 1 end)
  from (
         select dateadd(d, number, '2010-08-01') dt
              , datename(dw, dateadd(d, number, '2010-08-01')) days
           from master..spt_values 
          where type = 'P' 
            and number <= datediff(d, '2010-08-01', '2010-08-10')
       ) a