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
에서 테스트를 마쳤습니다.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// 작성 : 2008-09-11 CROWBACK
// 수정 : 2008-09-17 CROWBACK
// 기존에 클래스 형태에서 WOW 라는 네임스페이스로 감싼 API 형태로 변경함.
// 특별히 멤버를 가진것도 아닌데, 객체를 선언하고 사용하는 불편을 제거하기 위함.
//
// 32비트로 컴파일된 실행파일이 64비트 시스템의 HKEY_LOCAL_MACHINE 의 SOFTWARE
// 영역 액세스의 접근이 차단되는 것을 하위 Wow6432Node로 자동 매칭 시킨다.
// 이러한 작업을 위한 Win32 API를 래핑한 함수들.
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#define MAX_REGVALUE 8196
typedef struct
{
_bstr_t name;
BYTE* data;
DWORD length;
DWORD type;
} TEnumValue;
//////////////////////////////////////////////////////////////////////////////
// 아래 항목들은 Win32 API 양식을 그대로 따른다. 기존 API들과 문법상 차이를 두지
// 않음으로써, 기존 소스 포팅에 도움이 되도록 한다.
LONG RegOpenKey(__in HKEY hKey, __in_opt LPCSTR lpSubKey, __out PHKEY phkResult);
LONG RegOpenKeyEx(__in HKEY hKey, __in LPCTSTR lpSubKey, DWORD ulOptions, __in REGSAM samDesired, __out PHKEY phkResult);
LONG RegCloseKey(__in HKEY hKey);
//////////////////////////////////////////////////////////////////////////////
// 아래 항목들은 작업의 편의를 위해서 새로 추가한 항목들이다.
// 각각의 기능은 별도로 석으로 설명한다.
// 열기에 실패하면 키를 강제로 생성한다. 그래로 실패하면 에러리턴.
LONG RegOpenKeyForce(__in HKEY hKey, __in_opt LPCSTR lpSubKey, __out PHKEY phkResult);
// 서브 아이템 여부와 상관없이, 키 전체를 강제로 삭제함.
LONG RegDeleteKeyForce(__in HKEY hKey, __in LPCTSTR lpSubKey);
// 서브 아이템 여부와 상관없이, 키 이름만 남기고 안에꺼를 몽땅 지움. (서브키 및 밸류 모두..)
LONG RegDeleteKeyEmpty(__in HKEY hKey, __in LPCTSTR lpSubKey);
// 특정 레지 경로를 지정한 후, 값을 직접 지정할 수 있다.
LONG RegSetValueDirect(__in HKEY hKey, __in LPCTSTR lpSubKey, __in LPCTSTR lpValueName, __in DWORD dwType, __in const BYTE* lpData, __in DWORD cbData);
// 특정 레지 경로의 값들을 vector에 한꺼번에 몽땅 읽어와 목록을 작성한다.
LONG RegEnumValueList(__in HKEY hKey, __in LPCTSTR lpSubKey, std::vector<TEnumValue>& array);
// RegEnumValueList 함수와 매칭되는 것으로, 읽어온 것들은 내부적으로 메모리를 할당하므로
// 모두 사용한 후에는 아래 함수를 이용하여 해제하여준다.
VOID RegEnumValueClear(std::vector<TEnumValue>& array);
// 특정 레지 경로의 하위 키 값들을 vector 에 한꺼번에 읽어와 목록을 작성한다.
LONG RegEnumKeyList(__in HKEY hKey, __in LPCTSTR lpSubKey, std::vector<_bstr_t>& array);
// 지정된 키를 다른 곳으로 복사한다. (하위 키 및 모든 값 포함..)
LONG RegCopyKey(HKEY hkeySrc, LPCTSTR szSrcSubKey, HKEY hkeyDest);
// 지정된 키를 다른 곳으로 이동한다. (하위 키 및 모든 값 포함..)
LONG RegMoveKey(HKEY hkeySrc, LPCTSTR szSrcSubKey, HKEY hkeyDest);
// 지정된 키의 이름을 변경한다.
LONG RegRenameKey(HKEY hkeySrc, const char* szSrcSUB, HKEY hkeyDest, const char* szDstSUB);
// 지정된 키 값을 reg 파일로 익스포트 시킨다.
BOOL RegExport(LPCTSTR szSrcKey, LPCTSTR szFilePath);
};
// TODO: Return a different brush if the default is not desired
return hbr;
}
///////////////////////////////////////////////////////////////////////
// 요건 소스에서 불러다 사용할 때 쓰는 코드입니다.
void CallBack(COLORREF rgb)
{
// this changed color
}
void CColorTestDlg::OnButton1()
{
// 스태틱의 색깔을 읽는 관계로 항상 넓게 펼처놓습니다.
CColorDialogNotify dlg(0, CC_FULLOPEN, this);
dlg.SetCallback(CallBack);
dlg.DoModal();
}
며칠전에 건국대학교에서 한국 마이크로소프트 에서 주최하는 SQL 서버 관리 솔루션 페어가 있었습니다. 지금 다니는 회사에서도 세션을 하나 발표하는 관계로 저도 강제 참석 ^^;; 하게 되었네요.
MS에서 발표한 내용은 Windows 와 SQL Server의 상호 작용에 관한 내용이었고 개별 참석 회사들이 발표한 내용은 DB보안의 중요성, 데이터 백업의 중요성등등.. 대/내외 적으로 발생하는 각종 안전불감증 혹은 내부자 정보 유출에 대한 대응을 어떻게 할 것인가에 대한 것들이었습니다.
발표 자료들인데 링크는 MS 사이트이고, 관리는 mplanners 입니다만 공개된 홍보용 자료들이니 올려놔도 상관없겠죠 ^^;;;
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비트 머신의 잔제가 남았다는 거겠죠.)
위의 헤더와 소스를 걍 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; }
입력없는 콘솔 프로그램 같은 경우는 그냥 서비스에서 바로 수행하면 되지만 입력을 받는 콘솔이나, 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 일 경우에 프로그램을 기동시키면 정확하게 로그온된 시점에 프로그램이 구동하게 된다.