펌 : http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=72&MAEULNO=28&no=4257&page=1

다 읽고 보니, 가슴이 찡한 먼가가 느껴지네요..
오늘 퇴근할 때, 귤 한봉지 사가면 울 마눌이 이럴껍니다.
인간이 용돈이 남아 도는구나.. 담주부터 삭감 ㅜㅜ;;;

울 이뿐 와이프 사랑한데이~



기존에 서비스 프로그램을 다루면서, 프로그램적으로 서비스를 멈추는 것은
단순하게 ControlService(핸들, SERVICE_CONTROL_STOP, 개체포인터);
이런식으로 처리가 가능하다.

그런데? 중지 시키려고 하는 서비스의 종속된 서비스가 존재한다면?
그냥 중지 시키면 ERROR_DEPENDENT_SERVICES_RUNNING 메시지를 받게된다.
즉, 종속된 서비스가 존재하기 때문에 해당 서비스를 중지 할 수 없다.

그럴 경우 EnumDependentServices API를 이용하여, 종속 서비스 목록을 얻어와서
해당 서비스를 하나 하나 전부 종료 시켜주어야 한다.


아.. 참으로 오랜만에 공개글을 올리게 되네요..
한동안 회사일로 바쁘다 보니, 비공개 자료만 쌓아왔는데.. ^^;

MSDN 에서 보면 GetVersionEx() 함수를 이용하여, 윈도우의 각종 버전 및 플래폼
그리고, 서비스팩 정보를 이용할 수 있는 샘플 자료가 있습니다.

위 자료를 좀 개선해서, Windows 2008 Servers 및 Windows Vista 에서도 정상적으로 동작하는
모델을 만들어 봤습니다. 클래스의 이름은 CWindowsVersion 입니다.

또한, 기존에 32 비트시스템에서 제작되어진 어플리케이션이 64 비트 윈도우에서 구동될 때
호환모드로 구동하게 되는데, 별다른 문제는 없지만 레지스트리를 액세스할 때 다음과 같은
문제가 발생합니다.

32비트 호환모드로 구동되는 프로그램은 HKEY_LOCAL_MACHINE\SOFTWARE 경로를
직접 액세스 할 수 없습니다.

64비트 전용으로 생성된 소프트웨어는 문제가 없지만 32비트로 만들어져 호환모드로 동작할 경우는
HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node 아래처럼 32비트 응용프로그램을 위한
전용 경로를 사용해야합니다.

이것을 일일이 하나씩 변경하기 힘들어서, 64비트 운영체제에서 테스트해야하는 프로그램중에
32트로 만들어져 있고, 위의 경로및 그 경로의 하위를 액세스하는 경우 자동으로 경로를 변경해주어
동작하는데 문제가 없도록 API를 만들어 봤습니다.

소스는 Visual C++ 6.0, SP6, Enterprize Edition, 영문버전에서 작성되었으며
테스트는 Windows XP 32/64, Windows Server 2003 32/64, Windows Vista 32/64, Windows Server 2008 64
에서 테스트를 마쳤습니다.




칼라다이알로그 박스는 꼭 확인 버튼을 눌러야 색상을 얻어올 수 있습니다.
이걸 색상을 변경할 때마다 다이알로그를 닫지 않고도, 색상을 얻어올 수 있도록
수정하여 보았읍니다.

첫번째 아래그림은 그냥 색상을 선택해 본거구요..

사용자 삽입 이미지



아래 그림은 창을 닫지 않은 상태에서 다른색상을 선택해 본것입니다.
사용자 삽입 이미지




며칠전에 건국대학교에서 한국 마이크로소프트 에서 주최하는 SQL 서버 관리 솔루션 페어가 있었습니다.
지금 다니는 회사에서도 세션을 하나 발표하는 관계로 저도 강제 참석 ^^;; 하게 되었네요.

MS에서 발표한 내용은 Windows 와 SQL Server의 상호 작용에 관한 내용이었고 개별 참석 회사들이 발표한
내용은 DB보안의 중요성, 데이터 백업의 중요성등등..  대/내외 적으로 발생하는 각종 안전불감증 혹은 내부자
정보 유출에 대한 대응을 어떻게 할 것인가에 대한 것들이었습니다.

발표 자료들인데 링크는 MS 사이트이고, 관리는 mplanners 입니다만 공개된 홍보용 자료들이니
올려놔도 상관없겠죠 ^^;;;

MS SQL Server 보안에 대해 관심있으시면 한번 다운받아서 살펴보세요..
PS. http://channel8.msdn.com/ 에 가보시면 젬난거 몇가지 있어요 ^^;

Time

Session

09:00~09:30

등록

Keynote
09:30~10:10

SQL OS 작동 메커니즘 (한국마이크로소프트)

10:10~10:30

휴식(Track 분리) 전시관람

 

Track 1 (대강당)

Track 2 (국제회의장)

Session1
10:30~11:20

Microsoft SQL Server 2005환경을 위한
차세대 백업 기술과 재해복구 구현 방안
(한국이엠씨컴퓨터시스템즈)

케이포솔루션과 함께하는 Microsoft SQL Server 2005 One-Stop(감사,접근제어, 튜닝)
솔루션 (케이포솔루션)

11:20~12:30

점심식사

Session2
12:30~13:20

가볍지만 강력한 Glass SQL 이용한 Microsoft SQL Server 2005 튜닝 방법론
(필라넷)

외부 공격 내부자의 정보 침해 방지를 위한 데이타베이스 보안 취약점 분석 해법
(웨어밸리)

13:20~13:30

휴식

Session3
13:30~14:20

Microsoft SQL Server 2005 신속하고
정확한 시스템 데이터베이스 복구 방안
(시만텍)

Microsoft SQL Server 2005 환경하의
DB
보안기법 (모니터랩)

14:20~14:30

휴식

Session4
14:30~15:20

Microsoft SQL Server 2005 Database
백업을 위한 HyperBac 압축 백업 솔루션
소개 (데이터웍스)

메모리 DB SQL 파서에 기반한
신개념의 데이터베이스 보안 (신시웨이)

15:20~15:30

휴식

Session5
15:30~16:20

ERwin for Microsoft SQL Server 2005   (제니시스기술)

아이데라(IDERA) 활용하여 Microsoft SQL Server 2005 쉽고 빠르게 관리하기!!
(실크로드소프트)

16:20~16:30

휴식

Session6
16:30~17:20

SSMA(SQL Server Migration Assistance) 활용한 효율적인 마이그레이션 전략 사례 (온디멘드)

QCSS 이용한 Microsoft
SQL Server 2005
효율적인 관리 방안
(
퀘스트소프트웨어)

17:20~17:30

경품추첨

Session7

DB보안 Data거버넌스 솔루션(에스컴)



 

 

C나 C++에서 수식을 연산하다 잘못된 메모리를 읽거나, 저장된 파일을 읽어들이거나
0으로 나누거나 등등의 이유로 실수값에 -1.#INF0 과 같은 값이 지정된 것을 본적이
있을것이다. 혹은 -1.#IND0 과 같은 것도 있다.

.#IND Not a Number <= 숫자가 아닌값(실수체계에서 나올 수 없는 값과 같은 경우)
.#INF Infinite Number <= 0으로 나누는 등으로 인하여 무한히 큰값

숫자가 아닌값을 검출할 때는 CRT의 _isnan()
무한대의 숫자를 검출할 때는 CRT의 _finite()
주어진 숫자가 의심스러울 때 혹은 상태를 검출할 때는 _fpclass()
모두 float.h 에 선언되어 있습니다.

제목에서 처럼 0으로 나누어졌을 때는 숫자가 무한히 커져 .#INF00 의 무한값을 나타내고
다른 예로 double a = sqrt(-1) (데브피아 Neo.L 님께서 제시해주신 예시) 과 같은 경우는
실수체계에서 표현할 수 없는 숫자라는 의미로 .#IND00 과 같이 표기됩니다.

이 부분은  IEEE754,854 문서에 표기된 표준 표기에 의한것 (데브피아 stiletto 님께서 표기해줌)
으로 모두 정상적인 표기 방법입니다.

다행한것이 설계를 그렇게 한것인지, 저런 값이 나온다고 하여 연산하다가 죽는 경우는 없다는
점이다. 단, 필요한 요소에서 꼭 확인하여 엉뚱한 결과로 인한 피해를 줄이자.

사용자 삽입 이미지

















잘 모르는 내용을 올리다가 망신살도 좀 뻗치고.. 아무튼 이런 기회에 새로운걸 공부해 보네요.

스크롤바를 사용하다 보면 32767 unsigned short의 양의값을 넘어가는 경우가
간혹 발생할 수 있다.

그래픽 디자인 프로그램과 같은 것들을 설계할 때야 줌 팩터등을 이용하여 따로 처리하니
별 문제가 없겠지만 간단한 먼가를 만들 때 100000의 값을 써야한다면 먼가 설계를 해야하나?

그렇다면 우선 MSDN을 살펴보자.

CScrollBar::SetScrollRange
Set nMinPos and nMaxPos to 0 to hide standard scroll bars.
Do not call this function to hide a scroll bar while processing a scroll-bar notification message.

If a call to SetScrollRange immediately follows a call to the SetScrollPos member function, set bRedraw in SetScrollPos to 0 to prevent the scroll bar from being redrawn twice.

The difference between the values specified by nMinPos and nMaxPos

must not be greater
than 32,767

. The default range for a scroll-bar control is empty

일단 스크롤바의 SetScrollRange의 인자는 int 값인데, 어쩌구 저쩌구한 이유로 32767이 최대
라고 한다. 인자가 int라면 그렇다면 먼가를 처리하면 최대 양수 20억까지 쓸수 있다는 야근데..



SetScrollRange Function Win32API

 

However, because the SetScrollInfo, SetScrollPos, SetScrollRange, GetScrollInfo,
GetScrollPos, and GetScrollRange

functions support 32-bit scroll bar position data,

there is a way to circumvent the 16-bit barrier of the WM_HSCROLL and WM_VSCROLL


messages. See GetScrollInfo for a description of the technique.

MSDN에서 API의 도움말을 살펴보았다. Remark를 자세히 보다보니 32비트를 쓸 수 있는데
16비트 베리어에 쌓여 있단다.. 그렇다면 위의 두 메시지에서 오는 UINT nPos 가 정수
범위임에도 불구하고 쇼트크기를 가진다면 저걸 않쓰면 가능하지 않을까...


(16비트 베리어라는 의미는 내부적으로 먼가를 처리할 때 아무생각 없이 16비트를 썼다는
내용을 아주 점잔하게 표시한 것으로 추측된다.. 즉, 16비트 머신의 잔제가 남았다는 거겠죠.)

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

일단 여기까지 살펴보면 32비트를 쓸 수 있는데도 불구하고, 메시지 WM_VSCROLL과
WM_HSCROLL의 UINT nPOS는 16비트 최대값을 가진다고 결론을 내릴 수있다.
즉, 저걸 않쓰면 된다는 이야기겠고..

그럼 저걸 않쓰고 프로그램을 구성하면 되겠죠..
이전 장에 있던 샘플은 0 부터 100까지 크기를 가지는 샘플이었습니다.
이걸 그럼 위의 지식을 기반으로 0부터 1000000 크기를 가지는 샘플로 바꿔보죠.

#define SCROLL_MIN   0
#define SCROLL_MAX 

1000000


마이스크롤.SetScrollRange(SCROLL_MIN, SCROLL_MAX);
마이스크롤.SetScrollPos(50);
마이스크롤.EnableScrollBar(ESB_ENABLE_BOTH);

요렇게 설정을 해놓고 WM_VSCROLL 핸들러를 다음과 같이 수정해봅니다.
어짜피 이전 샘플에서도 nPos를 쓰는데는 스크롤바를 트래킹할 때만 썻으므로
이 샘플에서도 바뀌는 부분은 거기 밖에는 없습니다.

void CSampleDlg::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    UINT nCurPos;
    if(pScrollBar->GetDlgCtrlID() == IDC_SCROLLBAR5)
    {
        nCurPos = pScrollBar->GetScrollPos();
        switch(nSBCode)
        {
       

// 중간 생략.. 원본과 다른바 없음.

       case SB_THUMBPOSITION:
       {
          SCROLLINFO si = {0};
          si.cbSize = sizeof(SCROLLINFO);
          si.fMask = SIF_TRACKPOS;
          ::GetScrollInfo(pScrollBar->m_hWnd, SB_CTL, &si);

          // 메시지의 nPos 대신에 그냥 트랙포스를 가져다 쓴다.


          pScrollBar->SetScrollPos(si.nTrackPos);
       }
       break;

       case SB_THUMBTRACK:
       {
          SCROLLINFO si = {0};
          si.cbSize = sizeof(SCROLLINFO);
          si.fMask = SIF_TRACKPOS;
          ::GetScrollInfo(pScrollBar->m_hWnd, SB_CTL, &si);

          // 메시지의 nPos 대신에 그냥 트랙포스를 가져다 쓴다.


          pScrollBar->SetScrollPos(si.nTrackPos);
       }
       break;
      // 아래도 생략.. 원본과 같음
    }
 
    CDialog::OnVScroll(nSBCode, nPos, pScrollBar);
}

헉.. 그냥 않쓰면 되네.. ㅜㅜ;;;
그럼 수고하세요..

scroll02.zip
0.03MB

 

 

아주 오랜만에 컨트롤에 대하여 올려봅니다.
자주 않쓰다보니.. ^^;

리스트컨트롤을 사용하다 보면 서브아이템도 에디팅해야 할때가 있습니다.
요거 하나 쓸라구 클래스 만들기도 귀찬죠.. (이놈에 귀차니즘 -_-)

코드그루에 있는 자료를 좀 수정해서 사용해봅니다.
http://www.codeguru.com/cpp/controls/listview/editingitemsandsubitem/article.php/c1077/

위 자료인데요..
이것도 리스트를 서브클래싱해야되나서 좀 수정해서 걍 그런거 없이 써봅시다.

요건 에디트컨트롤 서브클래싱한 클래스인데요..
않한다면서도 필요하내요 -_-;;;

/////////////////////////////////////////////////
// 에디트컨트롤 헤더

class CLVEdit : public CEdit
{
// Construction
public:
   CLVEdit() { m_nEdit=-1; }
   void BeginEdit(CListCtrl* pList, int ndx);
   void EndEdit(CListCtrl* pList, NMHDR* pNMHDR);

// Attributes
public:
   CRect m_rc;
   BOOL  m_nEdit;

protected:
   //{{AFX_MSG(CLVEdit)
   afx_msg void OnWindowPosChanging(WINDOWPOS FAR* lpwndpos);
   //}}AFX_MSG
   DECLARE_MESSAGE_MAP()
};

///////////////////////////////////////////////////////////////
// 에디트 컨트롤 소스

BEGIN_MESSAGE_MAP(CLVEdit, CEdit)
   //{{AFX_MSG_MAP(CLVEdit)
   ON_WM_WINDOWPOSCHANGING()
   //}}AFX_MSG_MAP
END_MESSAGE_MAP()

void CLVEdit::OnWindowPosChanging(WINDOWPOS FAR* lpwndpos)
{
   lpwndpos->x=m_rc.left;
   lpwndpos->y=m_rc.top;

   if(m_rc.Width() > 0)
      lpwndpos->cx = m_rc.Width();
   if(m_rc.Height() > 0)
      lpwndpos->cy = m_rc.Height();

   CEdit::OnWindowPosChanging(lpwndpos);
}

void CLVEdit::BeginEdit(CListCtrl* pList, int ndx)
{
    CPoint posMouse;
    GetCursorPos(&posMouse);
    pList->ScreenToClient(&posMouse);

    LV_COLUMN lvc;
    lvc.mask=LVCF_WIDTH;

    CRect rcItem;
    pList->GetItemRect(ndx,rcItem,LVIR_LABEL);

    if(rcItem.PtInRect(posMouse))
        m_nEdit=0;

    int nCol=1;
    while(m_nEdit==-1 && pList->GetColumn(nCol,&lvc))
    {
        rcItem.left=rcItem.right;
        rcItem.right+=lvc.cx;

        if(rcItem.PtInRect(posMouse))
            m_nEdit=nCol;

        nCol++;
    }

    if(m_nEdit==-1)
       return;
   
    m_rc = rcItem;
    SetWindowText(pList->GetItemText(ndx, m_nEdit));
}

void CLVEdit::EndEdit(CListCtrl* pList, NMHDR* pNMHDR)
{
     LV_DISPINFO* pDispInfo=(LV_DISPINFO*)pNMHDR;
     CString sEdit=pDispInfo->item.pszText;
 
    if(!sEdit.IsEmpty())
       pList->SetItemText(pDispInfo->item.iItem,m_nEdit,sEdit);
 
    m_nEdit=-1;
    pList->SetItemState(pDispInfo->item.iItem,0,LVNI_FOCUSED|LVNI_SELECTED);
}



위의 헤더와 소스를 걍 Paste 하시거나 파일로 만들어서 첨부하시면 되구요..
다이알로그나 뷰등에서 리스트 컨트롤을 올려다가 사용하실 때..
다음과 같이 처리하시면 됩니다.

헤더에 아래를 하나 선언한다.
CLVEdit m_LVEdit;

소스에서 각 이벤트를 다음과 같이 핸들링한다.
void 마이다이알로그::OnBeginlabeleditList1(NMHDR* pNMHDR, LRESULT* pResult)
{
    LV_DISPINFO* pDispInfo=(LV_DISPINFO*)pNMHDR;
    *pResult=1;

    // 리스트컨트롤에 내장된 에디트컨트롤을 내가 맹글걸로 연결한다.
    HWND hWnd=(HWND)m_list.SendMessage(LVM_GETEDITCONTROL);
    ASSERT(hWnd!=NULL);
    VERIFY(m_LVEdit.SubclassWindow(hWnd));

    // 내부에서 좌표찾아서 에디트를 적당한 곳에 위치시킨다.
    m_LVEdit.BeginEdit(&m_list, pDispInfo->item.iItem);
    *pResult=0;
}

void 마이다이알로그::OnEndlabeleditList1(NMHDR* pNMHDR, LRESULT* pResult)
{
    LV_DISPINFO* pDispInfo=(LV_DISPINFO*)pNMHDR;
 
    // 에디트가 끝나면 그 값을 읽어다가, 선택된 셀에다가 쓴다.
    m_LVEdit.EndEdit(&m_list, pNMHDR);

    // 연결시킨걸 해제한다.
    VERIFY(m_LVEdit.UnsubclassWindow()!=NULL);

    *pResult=0;
}

얼마전 대명콘도에 나들이 갔다가 찍은 사진입니다.

사용자 삽입 이미지


현이가 어느새 커서 7살이 되었네요..

눈 썰매장에서 찍은 거인디, 시설이 진짜로 꽝입니다.
그나마 오션월드는 괜찬더군요...
여름에 시간나면 한번 더 가볼 생각입니다.

사용자 삽입 이미지


우리 이쁜 딸래미 은이.. 춥다고 눈만 내논 사진
감기 걸릴까바 모자를 씌웠는데, 자꾸 자구 흘러 내려서 저렇게 되네요.. ㅎㅎ

세월이 참 빠른거 같습니다. 어느세 저렇게 컷는지..
무탈하고 건강하게 잘 자라는 모습을 보니, 조금은 아빠가 되어가는거 같습니다.
앞으로도 건강하게 잘 자라다오...

PS. 조과장님 사진 감사해요 ^^;


서비스 프로그램을 작성하다 보면..
간혹 데스크탑과 연동해야 하는 일이 발생한다.

입력없는 콘솔 프로그램 같은 경우는 그냥 서비스에서 바로 수행하면 되지만
입력을 받는 콘솔이나, GUI를 가진 프로그램을 띄워야할 경우는
프로세스 목록에만 나오고.. 깜깜 무소식이다.

일반 응용프로그램에서야 WTSRegisterSessionNotification 함수를 이용하여 아주 쉽게
로그온된 시점을 구할 수 있지만 불행하게도 서비스에서는 사용할 수 없다.


또한 서비스 시작시에 응용 프로그램을 수행시키면, 아직 로그온 되기 전이기때문에
저런 설정을 다 해놓는다 하여도 로그온된 화면에는 아무것도 보이지 않을 수 있다.

1. 꼭 서비스로 관리해야하는가?
2. 입력을 가지는 콘솔이나 GUI를 가졌는가?
3. 서비스 구동시에 시작해야 하는가?
4. 로그온 된 데스크탑에 보여주어야 하는가?

위 사항들을 잘 판단하고 그래도 꼭 필요하다면 다음과 같은 정보를 구해야한다.
1. 서비스로 개발하기
2. 데스크탑과 연동하는 옵션
3. 로그온된 시점 구하기

1. 서비스로 개발하기
위 사항은 이미 자료가 많이 나와있고, 대부분 알려진 자료이므로 그냥 좋은 자료를 찾는다 ^^;

2. 데스크탑과 연동하는 옵션
       첫번째로 가장 쉬운 방법은 서비스 관리자를 이용하는 방법이다.

사용자 삽입 이미지

     그림에서 보이는것 처럼 로그온 설정시에 서비스와 데스크탑 상호 작용 허용에 첵크를
     해줌으로써 아주 간단하게 해결할 수 있다.

     두번째는 서비스를 인스톨할 때, 즉 CreateService 함수를 이용하여 서비스를 설치할 때
     다섯번째 인자에 SERVICE_INTERACTIVE_PROCESS 를 함께 넣어주는 것이다.
     이것은 첫번째 방법을 프로그램적으로 해결하는 것이다.

3. 로그온된 시점 구하기
로그온된 시점을 구하는 방법도 그리 어렵지 않다. 하지만 제약사항이 많아서..
특히나 지금 처리하는 방법은 Windows XP 이상, Windows Server 2003 이상에서만
동작하는 방법이므로, 하위 버전 윈도우를 처리하는 경우에는 해당사항이 없다. -_-;;;
(예전에는 이벤트 로그를 뒤적거려서 처리했었다...)

서비스를 핸들하기 위하여 RegisterServiceCtrlHandler 함수 대신에 좀더 구체적인 정보를
전달해주는 RegisterServiceCtrlHandlerEx 를 이용하는 방법이다.

RegisterServiceCtrlHandlerEx API는 시스템의 세션정보 변화를 감지하는 이벤트인
WM_WTSSESSION_CHANGE 를 처리할 수 있도록 해준다.

SetServiceStatus 에서 SERVICE_STATUS 값의 dwControlsAccepted 값에 꼭
SERVICE_ACCEPT_SESSIONCHANGE 를 함께 넣어주도록 하자.

이렇게 RegisterServiceCtrlHandlerEx 에 전달된 콜백함수에는 다음과 같은 인자가 있다.
DWORD WINAPI HandlerEx(
  [in]                 DWORD dwControl,
  [in]                 DWORD dwEventType,
  [in]                 LPVOID lpEventData,
  [in]                 LPVOID lpContext
);

저기서 첫번째 인자에 SERVICE_CONTROL_SESSIONCHANGE 가 넘어오면 로그온 세션에
무언가 변화가 생긴것이다.

두번째 인자로 어떤 변화가 발생하였는지를 감지할 수 있다. MSDN의 설명을 참고해보면...
Value Meaning

WTS_CONSOLE_CONNECT
0x1

A session was connected to the console terminal.

WTS_CONSOLE_DISCONNECT
0x2

A session was disconnected from the console terminal.

WTS_REMOTE_CONNECT
0x3

A session was connected to the remote terminal.

WTS_REMOTE_DISCONNECT
0x4

A session was disconnected from the remote terminal.

WTS_SESSION_LOGON
0x5

A user has logged on to the session.

WTS_SESSION_LOGOFF
0x6

A user has logged off the session.

WTS_SESSION_LOCK
0x7

A session has been locked.

WTS_SESSION_UNLOCK
0x8

A session has been unlocked.

WTS_SESSION_REMOTE_CONTROL
0x9

A session has changed its remote controlled status. To determine the status, call GetSystemMetrics and check the SM_REMOTECONTROL metric.


그러므로 위시점에서 WTS_SESSION_LOGON 일 경우에 프로그램을 기동시키면 정확하게
로그온된 시점에 프로그램이 구동하게 된다.

+ Recent posts