Visual Studio 2010 팀 블로그 운영진 분의 직강을 들으실 수 있는 특별한 세미나 세 편이 연속 기획되어있습니다. 이번 시리즈 세미나에서는 Visual Studio 2010을 통하여, 그 중에서도 특별히 Visual C++ 개발자들을 위한 세미나 두 편과 함께, Visual Studio 2010의 발전 가능성과 방향성을 진단해볼 수 있는 세미나로 구성되어있습니다.

그 중에서도 이번 12월 2일 세미나에서는 Visual C++ MVP이신 최흥배 MVP님의 강연이 있을 예정이며, Visual C++ 개발자 여러분들의 많은 관심과 참여를 부탁드립니다. :-)

개발자 여러분 안녕하세요.
Visual Studio 2010 이 여러분들의 개발자로서의 삶에 어떤 혁신적인 변화를 가져다 드릴 수 있는지 알려드리는
Visual Studio 2010 세미나 시리즈가 개최됩니다.
총 3회로 이루어진 본 Visual Studio 2010 세미나 시리즈는 개발 퍼포먼스 향상과 고품질 애플리케이션 개발을
꿈꾸시는C++ 개발자 여러분들을 위한 첫 번째, 두 번째 세미나, 그리고 완벽 팀웍을 바탕으로 소프트웨어 가시화 및
테스트 자동화를 통해 무결점 소프트웨어 개발을 꿈꾸시는 개발자 여러분들을 위한 세 번째 세미나로 구성되어
있습니다.

그 동안 경험해 보시지 못했던 편안하고 파워풀한 개발자로서의 삶을 만들어 드릴 Visual Studio 2010.
이번 겨울에는 이 세 번의 Visual Studio 2010 세미나 시리즈를 통해 여러분들의 개발자로서 가지고 계신 상상력과
잠재력을 실제 애플리케이션으로 보다 빠르고 파워풀하게 만들어 내실 수 있는 Skill-Up 의 기회로 삼으셨으면 합니다.
본 세미나는 여러분들의 보다 많은 참여를 위해 저녁시간에 진행되오니 저희와 함께 추운 겨울 밤을 뜨거운 학구열로
함께 보내주시면 감사하겠습니다.

Visual Studio 2010 세미나 시리즈에서 여러분들을 만나 뵙기를 바라겠습니다.
감사합니다.
세미나 일시 : 2010.12.02(목) 18:30~21:00 세미나 참석 대상 : C++ 개발자 여러분들
최흥배 선임연구원
- (현)마이에트 엔터테인먼트에서 Gunz2 서버 개발
- 2009, 2010 Microsoft Visual C++ MVP
- KGC 2008, 2009 강연
18:30 ~ 19:00 등록
19:00 ~ 19:50 Session 1 : Visual Studio 2010이 제공하는 새로운 가치
Visual Studio 2010에서 새롭게 선보이는 개발자 생산성 향상을 위해 보강된 사용 편의성 기능부터 고품질의 소프트웨어 개발에
필요한 품질관리 강화 기능에 이르는 다양한 기능들에 대해 안내해 드립니다.
19:50 ~ 20:00 휴식시간
20:00 ~ 20:50 Session 2 : 개발자, 개발팀의 미래를 위한 개발환경 업그레이드 방안
다양한 Visual Studio 사용자분들이 고민하시는 “왜 업그레이드를 해야지?”, “업그레이드를 어떻게 하지?”와 같은 주요
두가지 질문에 대한 명쾌한 해답을 드리는 세션으로, 업그레이드에 대한 공감대를 뿐만 아니라 업그레이드시 고려 및
유의해야 할 실무적인 Tip을 안내해 드립니다.
20:50 ~ 21:00 맺음말 경품 추첨
 
• 세미나 일시 : 2010.12.16(목) 18:30~21:00
• 세미나 참석 대상 : C++ 개발자 여러분들
• 세미나 강사 : 최흥배 선임연구원 (마이에트 엔터테인먼트)
• 세미나 일시 : 2011.01.13(목) 18:30~21:00
• 세미나 참석 대상 : 혁신을 꿈꾸는 모든 개발자 여러분들
• 세미나 강사 : 강성재 차장(한국마이크로소프트)
• 본 교육은 좌석이 한정되어 있으며 선착순으로 마감됩니다.
• 교육 후 추첨을 통하여 푸짐한 경품을 드립니다.
• 본 교육에서는 저녁식사를 제공해 드립니다.
• 주차권은 제공되지 않습니다.
행사 등록사무국 02) 598-9898 rgst@neodigm.com

저작자 표시 비영리 동일 조건 변경 허락
이 장소를 Daum지도에서 확인해보세요.
서울특별시 강남구 대치4동 | 포스코센터
도움말 Daum 지도
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)

이번 세미나는 C++을 사용하시는 전문 개발자 여러분과 게임 개발자 여러분들을 위한 세미나로, DirectX 11, Intel Parallel Studio, 병렬 프로그래밍 컨셉, PBB 기반 고수준 병렬 프로그래밍 4개 주제를 테마로 하는 기술 세미나입니다. 특히, 평소에 저희 Visual Studio 2010 한국 공식 팀 블로그 (http://www.vsts2010.net) 를 통하여 DirectX 11과 Visual C++ 관련 아티클을 자주 찾아주셨던 분들께 특별히 권해드리는 세미나입니다. :-)

장소: 양재동 엘타워 (ELTOWER) 그레이스 홀 (6F) / 일정: 2010년 11월 6일 수요일 (정정합니다. 2010년 11월 9일 화요일) 오후 1시 30분부터 / 참가 보증금: 2,000원

참가 신청 바로가기: http://www.microsoft.com/visualstudio/ko-kr/strategy-seminar.html

참가 신청 바로가기: http://www.microsoft.com/visualstudio/ko-kr/strategy-seminar.html

저작자 표시 비영리 동일 조건 변경 허락
이 장소를 Daum지도에서 확인해보세요.
서울특별시 서초구 양재1동 | 엘타워
도움말 Daum 지도
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)

최근 고객사의 요청으로 ActiveX 컨트롤 하나를 유지보수하고 있습니다. ActiveX 컨트롤에서 탈피하려는 추세가 있지만, 별 다른 대안이 없어서 ActiveX 컨트롤을 유지보수해야 하는 경우도 아직 우리나라에서는 상당히 많은것 같습니다.

 

Internet Explorer 7.0부터는 보호 모드라는 개념이 새로 소개되었습니다. 보호 모드란, 일종의 Sand-box 개념으로 기존과 같이 현재 로그온한 사용자의 권한을 그대로 물려받아 무분별하게 실행되는 것을 방어하는 안전 장치입니다. Windows XP와는 달리 Windows Vista부터는 일반 사용자를 단순히 관리자로 분류하지 않고, UAC를 통하여 작업에 대해 허가/거절 여부를 정할 수 있게 하였습니다.

 

우리가 권한 상승이라고 이야기하는 기능은 사실 권한 상승을 사용자에게 요청하는 것입니다. 이러한 권한 상승 요청을 구현하기 위하여 이제까지 참고할 수 있는 보편적인 리소스는 EXE 파일과 함께 매니페스트 파일을 배포하는 것이 대표적인 것이었습니다. 그리고 ActiveX 컨트롤의 경우 Elevation Moniker를 통하여 권한 할당을 받는 것이 대표적입니다.

 

ActiveX 컨트롤에서 어떻게 권한 상승을 구현할 수 있는지에 대하여 잘 정리한 권용휘 MVP님의 아티클을 참고하시면 어떻게 권한 상승이 이루어지고 관리될 수 있는지에 대한 컨셉을 확인하실 수 있습니다. (http://www.devpia.com/MAEUL/Contents/Detail.aspx?BoardID=51&MAEULNO=20&no=7669) 저는 권용휘 MVP님의 아티클 위에 몇 가지 내용을 더 첨언해보고자 합니다.

 

1. Visual C++ 6.0 (SP6)에서의 권한 상승 구현

 

놀랍게도, 그리고 안타깝게도, Visual C++ 6.0 개발 도구를 업그레이드하지 못하는 이슈는 도처에 널려있습니다. 기존에 개발되어있던 소프트웨어나 라이브러리가 특정 문자 세트에 완벽하게 맞추어져있지만, 업그레이드할 수 없을만한 이슈 (개발 업체의 부도 - 또는 - 계약 해지 / 담당자의 연락 두절과 같은)로 인하여, 올해부터는 더 이상 일체의 기술 지원을 받을 수 없는 (서비스 팩 다운로드도 MS 공식 홈페이지에서는 더이상 받으실 수 없습니다.) 그런 개발 플랫폼위에서 고군분투해야 하는 상황은 생각보다 자주 있습니다.

 

사실 권한 상승을 제대로 프로그래밍하려면 Windows Vista나 Windows 7 SDK가 필요합니다. 하지만 이들 SDK의 코드를 가져다 사용하려면 개발 도구를 Visual C++ 2005나 2008로 업그레이드할 필요가 있습니다. 하지만 개발 도구나 Windows SDK를 설치하지 않고 간단히 적용할 수 있다고 소개된 방법은 다행히 Visual C++ 6.0에서 온전하게 동작합니다.

 

typedef struct tagBIND_OPTS3 : tagBIND_OPTS2 {
 HWND hwnd;
} BIND_OPTS3, *LPBIND_OPTS3;

위의 코드를 자주 사용하는 헤더에 선언해두면 권한 상승을 위하여 호출하는 CoGetObject에 정확히 바인딩할 수 있습니다. 그리고, 아래의 두 함수를 이용하여, 권한 상승을 지원하는 운영 체제 (Windows Vista 이상의 운영 체제)를 식별하고 실제로 객체를 생성할 수 있습니다.

 

HRESULT CoCreateInstanceAsAdmin(HWND hwnd, REFCLSID rclsid, REFIID riid, OUT void ** ppv)
{
 BIND_OPTS3 bo;
 OLECHAR wszCLSID[50];
 OLECHAR wszMonikerName[300];
 const int CHARS_IN_GUID = 39;

 StringFromGUID2(rclsid, wszCLSID, CHARS_IN_GUID);
 swprintf(wszMonikerName, L"Elevation:Administrator!new:%s", wszCLSID);
 wprintf(L"%s\r\n", wszCLSID);
 wprintf(L"%s\r\n", wszMonikerName);

 memset(&bo, 0, sizeof(bo));

 bo.cbStruct = sizeof(bo);
 bo.hwnd = hwnd;
 bo.dwClassContext = CLSCTX_LOCAL_SERVER;

 return CoGetObject((LPCWSTR)wszMonikerName, &bo, riid, ppv);
}

 

BOOL IsUACRequiredOperatingSystem(void)
{
 BOOL bIsUACRequired = FALSE;
 OSVERSIONINFO sInfo;
 sInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

 if (GetVersionEx(&sInfo))
 {
  if( sInfo.dwPlatformId == VER_PLATFORM_WIN32_NT &&
   sInfo.dwMajorVersion >= 6 &&
   sInfo.dwMinorVersion >= 0) { // Windows VISTA or Higher
   bIsUACRequired = TRUE;
  }
 }

 return bIsUACRequired;
}

 

Visual C++ 6.0 기반의 프로젝트들은 거의 대부분 ANSI나 Multibyte Character Set 기반의 문자 세트를 기준으로 프로그램이 구성되어있습니다. 확장성을 위하여 Transition이 가능한 데이터 타입 (LPTSTR, LPCTSTR, TCHAR 등)은 거의 고려하지 않았을 확률이 높습니다. 하지만 우리가 호출해야 할 API들은 유니코드 문자열을 필요로 하기 때문에 직접 유니코드를 사용하도록 기존 코드를 수정하였습니다. 그리고 더불어서, StringCchPrintf 함수를 대신하여 sprintf 계열의 함수를 직접 이용하였습니다.

 

2. 실제 ActiveX 컨트롤에 권한 상승을 적용하는 또 다른 방법

 

권용휘 MVP님의 아티클에서 설명하는 방법은, 특정 메서드나 프로퍼티의 호출 과정에서 필요로하는 권한 상승을, 권한 상승을 외부에서 요구하고 결과를 받기 위한 프록시 멤버와, 실제 처리하는 멤버로 이원화하여 구현하는 방식입니다. 대부분의 경우 이 방법으로 해결할 수 있다고 생각합니다. 하지만 간혹, ActiveX 컨트롤의 동작이나 상태, 속성을 정의하는 작업 전체가 시스템에 종속적으로 구성된 경우도 있을 수 있습니다. 이 경우에는, 격리모드에서 실행되는 ActiveX 컨트롤과는 별도로 새로운 인스턴스를 노출시켜야 할 필요가 있습니다.

 

이에 대한 솔루션은 인터넷 검색중에 발견한 어떤 블로그의 아티클에서 찾을 수 있었습니다. (http://hbesthee.tistory.com/620) 이 아티클은 델파이 기반의 솔루션을 제시하고 있었으며 저는 이것을 Visual C++ 기반의 코드로 옮겼습니다.

STDMETHODIMP CtrusEBANK::trusElevate(VARIANT *ret)
{
 AFX_MANAGE_STATE(AfxGetStaticModuleState())

 if (IsUACRequiredOperatingSystem())
 {
  ItrusEBANK *pElevatedObj = NULL;
  HRESULT hr = CoCreateInstanceAsAdmin(NULL,
   CLSID_trusEBANK, IID_ItrusEBANK,
   (void **)&pElevatedObj);

  if (SUCCEEDED(hr))
  {
   ret->vt = VT_DISPATCH;
   ret->pdispVal = pElevatedObj;
   ret->pdispVal->AddRef();
   return S_OK;
  }
  else
  {
   ret->vt = VT_NULL;
   return hr;
  }
 }
 else
 {
  ret->vt = VT_BOOL;
  ret->boolVal = VARIANT_FALSE;
 }

 return S_OK;
}

 

STDMETHODIMP CtrusEBANK::trusNeedElevate(VARIANT *ret)
{
 AFX_MANAGE_STATE(AfxGetStaticModuleState())

 ret->vt = VT_BOOL;

 if (::IsUACRequiredOperatingSystem())
  ret->boolVal = VARIANT_TRUE;
 else
  ret->boolVal = VARIANT_FALSE;

 return S_OK;
}

 

위의 코드에서는 trusNeedElevate를 이용하여 권한 상승이 지원되는 운영 체제인지 파악하는 부분과, trusElevate를 이용하여 권한 상승이 적용될 개체를 반환하는 방법 두 가지를 보여주고 있습니다. 그리고 아래의 코드는 자바스크립트에서의 실제 사용 예시입니다.

 

var obj = new ActiveXObject("EBANK.trusEBANK");

if (obj.trusNeedElevate())
{
 var result = obj.trusElevate();
 if (result) {
  obj = result;
 }
}

// 여기서부터 obj 객체 사용

 

이와 같은 방법을 통하여, 권한 상승이 온전하게 ActiveX 컨트롤에 적용될 수 있도록 하여, 원활한 동작 환경을 구현할 수 있을 것입니다.

 

3. Internet Explorer Host Window를 Elevation하는 방법 [업데이트]

 

그리고 최근에 저는 여러가지 방법을 적용해보던 중에 가장 현실적인 타협안 하나를 발견하였습니다. 바로, Internet Explorer Host Window 자체를 Runtime 도중에 Elevation 처리하는 방법으로, 기존의 응용프로그램 기반 매니페스트와 유사하게 작동합니다. 그리고 이 방법은, Host Window를 Elevation하는 데에 사용할 수 있는것 뿐만 아니라, 대리 실행해야 하는 응용프로그램의 Manifest 보유 여부에 관계없이 Elevation에도 사용될 수 있습니다.

 

참고로, 이 방법은 http://www.softblog.com/2008-02/vista-tools/ 에서 소개한 VistaTools.cxx 파일의 코드를 일부 발췌한 것임을 밝혀둡니다.

 

typedef struct _TOKEN_ELEVATION {
    DWORD TokenIsElevated;
} TOKEN_ELEVATION, *PTOKEN_ELEVATION;

 

위의 구조체는 Elevation 상태를 점검하기 위하여 필요한 구조체로 이미 Elevation 처리가 되어있는 호스트 위에서 실행되는 경우 Elevation을 다시 실행하지 않도록 하기 위하여 필요합니다. 이 구조체는 Visual C++ 6.0 기반에서 프로그램을 업데이트할 때 수동으로 지정해야 합니다.

 

#ifndef CSIDL_PROGRAM_FILES
  #define CSIDL_PROGRAM_FILES 0x0026
#endif // CSIDL_PROGRAM_FILES

 

위의 Special Folder Constant는 %PROGRAMFILES% 폴더의 경로를 가져오기 위하여 필요합니다. 마찬가지로 Visual C++ 6.0 기반에서 프로그램을 업데이트할 때 수동으로 지정해야 합니다.

 

#include <shellapi.h>
#include <shlobj.h>

 

마지막으로 위의 두 헤더 파일을 참조하도록 선언하면 일단 준비는 끝납니다.

 

BOOL IsVistaOrHigher(void)
{
 OSVERSIONINFO versionInfo;
 versionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);

 if (GetVersionEx(&versionInfo) &&
  versionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT &&
  versionInfo.dwMajorVersion >= 6)
  return TRUE;
 else
  return FALSE;
}

 

HRESULT IsElevated(BOOL *pElevated)
{
 HRESULT hResult = E_FAIL;
 HANDLE hToken = NULL;

 if (!IsVistaOrHigher())
  return hResult;

 if (!OpenProcessToken(
  GetCurrentProcess(),
  TOKEN_QUERY,
  &hToken))
  return hResult;

 TOKEN_ELEVATION te = { 0 };
 DWORD dwReturnLength = 0;
 const int TokenElevation = 20;

 if (GetTokenInformation(
  hToken,
  (TOKEN_INFORMATION_CLASS)TokenElevation,
  &te,
  sizeof(te),
  &dwReturnLength))
 {
  hResult = te.TokenIsElevated ? S_OK : S_FALSE;

  if (pElevated)
   *pElevated = (te.TokenIsElevated != 0);
 }

 CloseHandle(hToken);
 return hResult;
}

 

BOOL ShellExecWithVerb(HWND hWnd, LPCTSTR lpVerb, LPCTSTR lpPath, LPCTSTR lpParameters, LPCTSTR lpDirectory)
{
 SHELLEXECUTEINFO executeInfo;
 memset(&executeInfo, 0, sizeof(executeInfo));

 executeInfo.cbSize = sizeof(SHELLEXECUTEINFO);
 executeInfo.fMask = 0;
 executeInfo.hwnd = hWnd;
 executeInfo.lpVerb = lpVerb;
 executeInfo.lpFile = lpPath;
 executeInfo.lpParameters = lpParameters;
 executeInfo.lpDirectory = lpDirectory;
 executeInfo.nShow = SW_NORMAL;

 return ShellExecuteEx(&executeInfo);
}

 

BOOL ShellExecWithElevation(HWND hWnd, LPCTSTR lpPath, LPCTSTR lpParameters, LPCTSTR lpDirectory)
{
 return ShellExecWithVerb(hWnd, _T("runas"), lpPath, lpParameters, lpDirectory);
}

 

BOOL OpenUrlWithElevation(HWND hWnd, LPCTSTR lpUrl)
{
 _TCHAR lpBuffer[MAX_PATH + 1];

 if (!SHGetSpecialFolderPath(hWnd, lpBuffer, CSIDL_PROGRAM_FILES, 0))
  return FALSE;

 _tcscat(lpBuffer, _T("\\Internet Explorer\\iexplore.exe"));
 return ShellExecWithElevation(hWnd, lpBuffer, lpUrl, _T(""));
}

 

위의 코드의 내용들 중에서 강조표시된 것이 실제 Elevation을 위하여 필요한 코드입니다. 그리고 실제 사용법은 아래와 같습니다.

 

STDMETHODIMP CMyAXControl::RunElevatedWeb(VARIANT *url)
{
 AFX_MANAGE_STATE(AfxGetStaticModuleState())
 USES_CONVERSION;

 CComBSTR targetUrl;
 url->vt = VT_BSTR;
 targetUrl = CComBSTR(url->bstrVal);

 _TCHAR buffer[MAX_PATH + 1];
 _tcscpy(buffer, (_TCHAR*)OLE2T(targetUrl.m_str));
 BOOL bResult = OpenUrlWithElevation(NULL, buffer);

 if (bResult)
  return S_OK;
 else
  return E_FAIL;
}

 

그리고 위의 메서드가 실제로 실행되어야 할 때와, 그렇지 않을 때를 구분하기 위하여 아래와 같이 상태 확인을 위한 메서드를 배치하는 것도 도움이 됩니다. :-)

 

STDMETHODIMP CMyAXControl::NeedElevate(VARIANT *ret)
{
 AFX_MANAGE_STATE(AfxGetStaticModuleState())

 ret->vt = VT_I4;
 ret->intVal = 0;

 if (IsVistaOrHigher())
 {
  BOOL bResult = FALSE;

  if (SUCCEEDED(IsElevated(&bResult)))
  {
   if (bResult == TRUE)
    ret->intVal = 4; // 이미 Elevation이 완료됨
   else
    ret->intVal = 3; // Elevation이 필요함
  }
  else
   ret->intVal = 2; // 상태 정보를 조회할 수 없음
 }
 else
  ret->intVal = 1; // UAC가 지원되지 않는 운영체제로 판단함

 return S_OK;
}

 

위의 메서드를 통하여, 반환값이 1 - 또는 - 4로 반환되는 경우에 한정하여 실제로 필요한 코드를 실행하고, 그렇지 않은 경우 Elevation을 수행하도록 유도하는 코드를 웹에서 작성할 수 있을 것입니다.

 

덧) 지적하거나 수정이 필요한 부분이 있으시면 댓글로 남겨주시면 바로 반영하도록 하겠습니다. 감사합니다. :-)

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)

닷넷 프레임워크 기술들 중 가장 적게 알려지고 그 비중이 많이 축소되어 소개되는 부문이 바로 Visual C++ CLR일 것입니다. 생각보다 C++ CLR이 유용할 수 있음에도 불구하고 여론에 떠밀려서 거의 사장되다시피하고 있지요. 이번 블로그 포스트에서는 C++ CLR을 통해서 실용적인 코딩 하나를 해볼까합니다.

C++과 더불어 관련 라이브러리들 (STL, ATL, WTL, Boost, MFC 등)의 경우 일반적인 응용프로그램을 작성하거나, 성능 튜닝이 필요한 응용프로그램들을 작성하거나, 수학 라이브러리 등의 힘을 빌어 불필요한 오버헤드가 없는 고속 연산을 처리하는 등의 목적에 알맞게 디자인되어있습니다. 그러나 엔터프라이즈 프로그래밍의 경우에서처럼 구조화된 처리를 수행해야 하는 경우 상당히 불편한 점이 많습니다. 간단한 예로 당장 XML 문서 하나 분석하는 것도 썩 편리하지는 않지요.

Visual Studio 패밀리에서 공식적으로 제공되는 기능은 아니지만 C++ CLR도 닷넷 프레임워크 위에서 기동되는 어셈블리를 작성할 수 있습니다. 그리고 더 중요한 것은 기존의 x86 코드나 x64 코드를 MSIL 어셈블리 사이에 끼워넣을 수 있다는 점입니다. 이러한 사실을 바탕으로 하여 하이브리드 코드를 만들 수 있습니다.

그럼 하나씩 살펴보도록 하겠습니다.

1. C++ CLR 기반의 클래스 라이브러리 프로젝트 하나를 만듭니다. 프로젝트 이름으로 여기서는 "CLRxASPX"를 사용하기로 하겠습니다.

2. 여느 닷넷 프로젝트처럼 예상 가능한 종류의 파일들이 만들어집니다. AssemblyInfo.cpp 파일은 어셈블리 어트리뷰트를 정의하는 부분으로 다음과 같이 코드가 구성되어있습니다.

AssemblyInfo.cpp 보기

C#에서와 마찬가지인 어트리뷰트 사용법입니다. 다만 C++ 컴파일러의 특성상 空문장임을 뒷쪽에 명시해둔 것이 다른 부분이라고 할 수 있겠습니다. 그리고 네임스페이스의 참조 방법이 C#과 동일하게 되어있습니다. C#에서 흔히 사용하는 using은 C++의 using namespace와 같습니다. 그리고 C++에서 흔히 사용하는 using std::cout; 같은 참조는 C#에서는 using Console = System.Console; 과 같이 표현이 가능합니다.

특이한 점이 하나 더 있다면 stdafx.h의 존재입니다. MFC나 ATL 프로젝트에서 Precompiled Header라는 명목으로 많이 사용되던 것이 C++ CLR에도 그대로 적용된 걸 볼 수 있습니다. 거리낌없이 그 개념 그대로 stdafx.h에 Windows API 헤더를 추가하거나 프로젝트 전반에 걸쳐 사용하고 싶은 다른 라이브러리의 헤더를 추가하면 됩니다.

그럼 stdafx.h는 어떻게 구성되어있는지 한 번 살펴보도록 하겠습니다.

stdafx.h 보기

C++ CLR의 유용함이 느껴지는 부분입니다. 굵게 표시한 부분이 예제를 위하여 추가된 코드로, Win32 API 헤더를 추가한 것입니다. WIN32_LEAN_AND_MEAN은 컴파일러나 링커의 처리를 최소화하기 위한 것으로 통계적으로 사용 확률이 그닥 높지 않은 코드, 라이브러리를 사전에 제거해주는 옵션입니다. 예제에서는 API 참조를 위해서만 stdafx.h를 이용한 것이므로 stdafx.cpp에는 다른 서술이 되어있지 않아서 생략합니다.

이제 실제 코드를 살펴보기로 합니다.

CLRxASPX.h 보기

using namespace로 모든 네임스페이스의 요소들을 참조할 수도 있지만  필요한 항목들만 위와 같이 참조할 수도 있습니다. 그리고 아래의 클래스 선언을 보면 일반 C++ 클래스 선언과는 구별되는 내용들이 몇 가지 있습니다. 우선, class 키워드 앞에 ref 키워드가 사용된 걸 볼 수 있는데 이것이 이 클래스가 Managed Class 임을 나타내는 것입니다. 그 다음에는 상속 대상을 ASP.NET의 페이지 클래스로 정한 것이 보입니다.

Protected 멤버 메서드로 Page_Load 메서드가 선언된 것이 보입니다. 이 때 알아둘 것이 있는데 ASP.NET은 내부적으로 Reflection을 통해서 실제로 Page 클래스의 Load 이벤트에 추가한 이벤트 처리기가 아니라도 자동으로 이름을 통하여 호출 대상을 결정하기 때문에 위의 메서드는 항상 실행됩니다.

그리고 C++ CLR에서 닷넷 프레임워크의 형식들을 이용할 때에는 닷넷 프레임워크 상의 관리되는 참조 객체임을 표현하기 위하여 과거의 Managed Extensions for C++ 시절때 사용하였던 __gc * 형식 대신 ^ 기호를 사용합니다. 포인터의 * 와는 구분되는 것으로 주소값을 얻기 위한 형변환 시도는 허용되지 않습니다. 이렇게 사용되는 형변환은 주소값에 대한 형변환이 아니라 형식 자체에 대한 형변환 시도가 됩니다. 별도로 해당 형식이 암묵적/명시적 형변환 연산자를 가지고 있지 않을 경우 상속 관계와는 무관하므로 컴파일이 실패합니다. 참고로, Page_Load 메서드가 이벤트 핸들러로서의 조건을 충족하려면 반환 형식은 void, 즉 반환 대상이 없으며, 두 개의 매개 변수를 받아들여야 합니다. 하나는 이벤트를 발행한 출처의 참조, 또 하나는 이벤트 인자 개체의 참조입니다. 자세한 내용은 System::EventHandler 대리자를 참고하시면 됩니다.

CLRxASPX.cpp 보기

구현하기 나름이지만, 원래의 C++ 코드 스타일을 유지하기 위하여 선언부와 구현부를 나눈다면 위와 같은 형태로 cpp 파일에 메서드 본문을 작성할 수 있습니다. 이 때, 메서드의 시그니처에 전체 네임스페이스가 지정되어야 함을 유의합니다.

windows.h 헤더 파일 내에 포함되어있는 항목들 중 시스템 정보를 추출하는 API를 이용해보기로 합니다. 이 부분은 C#이나 기타 닷넷 환경에서는 원래 Platform Invoke를 통하여 우회적으로 마샬링해야 하는 부분이지만 C++ CLR의 특성에 따라 Native Code를 평소처럼 실행할 수 있다는 것을 보여줍니다. SYSTEM_INFO 구조체의 주소값을 전달하여 시스템 정보를 구조체에 채우도록 만들고 이를 ASP.NET 서비스에서 보여준다는 것이 예제 프로그램의 컨셉입니다.

Label 컨트롤을 생성하기 위하여 gcnew 연산자를 사용하였습니다. 역시 C++에서 원래 사용하던 new 연산자와 구분되는 것입니다. gcnew 연산자로 관리되는 객체를 만들고, Text 프로퍼티에 문자열을 지정하였습니다. 관리 객체에 지정되는 문자열은 자동으로 System::String의 인스턴스로 분류되어 처리되도록 만들어져 있기 때문에 유니코드 문장임을 나타내는 접두사 L 없이도 쓰일 수 있습니다. 또한, 아랫쪽 코드를 보면 System::String 형식에만 정의되어있는 문자열간 합치기 기능이 C++에서도 동작하는 것을 볼 수 있습니다.

위의 예제 코드를 작성한 다음, 결과를 확인하기 위하여 아래와 같이 IIS 7.0에서 사용할 수 있는 web.config 파일을 작성하여 웹 어플리케이션 디렉터리를 구축하고, 해당 웹 어플리케이션 디렉터리 바로 아래의 Bin 폴더에 이 프로젝트로 빌드한 DLL을 저장하였습니다.

web.config 보기

위의 내용에서 확인해야 할 부분이 두 가지가 있습니다. IIS 7의 system.webServer 섹션은 닷넷 프레임워크가 처리하는 것이 아니라 IIS 7이 스스로 처리하는 부분입니다. 우선 핸들러에 type이라고 되어있는 부분은 닷넷 프레임워크에서 불러들일 IHttpHandler 인터페이스와 호환되는 형식의 약식 명칭을 지정하는 부분입니다. Page 클래스를 상속함으로서 이 부분이 자동으로 구현되었기 때문에 우리가 방금 작성한 C++ CLR 클래스의 약식 명칭을 여기에 기술합니다. 네임스페이스와 클래스 이름, 그리고 Bin 폴더 내에 들어있는 클래스 라이브러리의 어셈블리 이름을 여기에 기출합니다. 그리고 preCondition에서는 이 핸들러가 통합 모드에서 실행되어야 하고 닷넷 프레임워크 2.0 런타임을 사용하여 구동되어야 함을 표기하고 있습니다.

이제 실행 결과를 살펴보도록 하겠습니다.

C++ CLR이 Native Code와 잘 연동되어 ASP.NET 서비스를 수행하고 있는 것을 볼 수 있습니다. 여기서는 공식적으로 프로젝트 템플릿이 제공된 것이 아니라서 이와 같이 간소한 형태로만 프로그램을 작성하였지만 C++ CLR로도 이와 같이 프로그램 작성을 할 수 있다는 것을 확인할 수 있었습니다. Platform Invoke로 해결하기 어려운 문제는 이와 같이 프로그램의 형태에 제한을 갖지 않고 직접 C++의 힘을 빌릴 수도 있다는 것을 기억하면 쉽게 문제를 풀어나갈 수 있을 것입니다.

참고로 C++/CLI의 ECMA 표준안 문서는 http://www.ecma-international.org/publications/standards/Ecma-372.htm 에서 확인가능합니다.

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)

강성훈님의 개인 도메인 영역에서 흥미로운 문서를 하나 발견하였습니다. 상당한 노력이 뒷받침된 것으로 보이는 중요한 문서였는데 바로 Microsoft C++ 컴파일러의 이름 변경 체계 문서입니다.

http://mearie.org/documents/mscmangle

문서에 대한 내용은 더 구체적이고 더 정확한 방향으로 업데이트 되어 나갈 것입니다. 이러한 규칙을 일정 수준 이상 분석하여 매칭시킬 수 있다면 xPlatform이 닷넷 프레임워크가 기본적으로 제공하는 C 언어나 COM, ActiveX 같은 일상적인 언어 환경을 넘어서서 Visual C++이 사용하는 특유의 이름 변경 기술에 즉시 바인딩할 수 있는 기반 기술을 C++ 컴파일러가 아닌 곳에서 구현할 수 있을지도 모르겠다는 생각이 듭니다.

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)

드디어 Visual Studio 2008과 .NET Framework 3.5의 첫 서비스팩이 출시되었습니다. 여러 개발자들의 피드백에 힘입어 추가되고 개선된 부분들이 많아서 Windows Vista SP1이 나왔을 때 만큼이나 반갑습니다. 대강의 내용을 추려보면 이렇습니다.

Visual Studio 2008 Service Pack 1에서 개선되거나 달라진 점

  • WPF 디자이너의 성능 향상
  • SQL Server 2008 지원 추가 (SQL Server 2008의 모든 기능을 이용하려면 반드시 SP1을 설치해야 합니다.)
  • ADO.NET 엔티티 디자이너
  • Visual Basic과 Visual C++을 위한 컴포넌트와 도구들
  • Visual C++의 경우 이전에 커뮤니티를 통하여 소개되었고 국내에서도 몇 차례의 세미나를 통해서 언급된 Visual C++ Feature Pack이 통합되어 TR1 기술은 물론 MFC를 위한 Ribbon UI가 지원됩니다.
  • JavaScript 지원 강화, AJAX와 데이터 도구의 기능 향상, 웹 사이트 배포 기능 향상

.NET Framework 3.5 Service Pack 1에서 개선되거나 달라진 점

  • Windows Presentation Foundation 기반의 응용프로그램을 다시 컴파일하거나 변경하지 않아도 이전보다 최고 45% 이상 더 낳은 성능을 나타냅니다.
  • Windows Communication Foundation의 제어 방법이 더 정교해지고 더 다양해졌습니다.
  • .NET Framework 3.5의 설치가 이전보다 더 쉽고 자연스럽습니다. (Streamlined)
  • ADO.NET 엔티티 프레임워크, 데이터 서비스 및 SQL Server 2008과의 연동을 목표로 데이터 플랫폼 기술들이 더 좋아졌습니다.
  • 클라이언트 전용 런타임을 제공하며 런타임의 크기를 20MB 이내로 줄일 수 있습니다. (이전 .NET Framework 2.0 수준)

이 외에도 Team Foundation Server에 대한 기능이 많이 향상되었으며 좀 더 자세한 내용은 아래 기사를 참고하시기 바랍니다. 조만간 다른 언어 버전 및 Express Edition의 모든 언어 버전에 대한 SP1도 새로 공급될 예정이며, SQL Server 2008 Express Edition 및 SQL Server 2008에 대응되는 Management Studio/Express도 개발될 듯 합니다. 참고로, Visual Studio Shell 엔진도 SP1 코드 기반에 대응되는 버전이 새로 제공되므로 Shell 기반으로 독립 어플리케이션을 만드는 경우에도 새로운 SP1의 기능을 빠뜨리지 말고 가져가시면 도움이 될 것입니다. :-)

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)
Visual C++ Debugger는 Breakpoint를 어떻게 감지해내는 것일까? 원리는 아주 간단하다. 어셈블리어를 공부해보신 분들이라면 인터럽트라고 하는 기능을 들어본 적이 있을 것이다. Breakpoint를 지정한 부분에 인터럽트 코드를 삽입하여 컴파일하는 것이다. 그렇다면 우리가 직접 Breakpoint를 코딩으로 구현하는 것도 가능하지 않을까? 가능하다!

__asm
{
   int 3
}

위와 같은 코드를 실행해보면 Visual C++ Debugger가 정확히 해당 위치에서 멈추는 것을 볼 수 있다. 그러면 위와 같은 코드 대신 좀 더 직관적인 코드를 만드는법은 없을까? 매크로 구문을 이용하면 된다. 단, 이와 같은 구문은 디버그 모드에 한정하여 쓰는 것이 좋겠다.

#define BREAKPOINT __asm \
{ \
   int 3 \
} \

inline void SetBreakPoint(void)
{
   #ifdef DEBUG
       BREAKPOINT
   #endif
}

위와 같이 코드를 작성하고 따로 헤더 파일로 분리해 놓은 다음 SetBreakPoint 함수만 원하는 곳에 걸어두기만 하면 된다. SetBreakPoint 안에서 걸리는게 아니냐고 묻는다면 걱정하지 않아도 된다. 왜냐면 inline 함수이기때문이다. 익히 알다시피 inline 함수는 Call Stack에 영향을 주지 않는 함수이다.

그리고 SetBreakPoint 함수를 릴리즈 모드에서 사용할 경우 아무것도 실행하지 않게된다. 그러므로 #ifdef ~ #endif 전처리기 구문을 굳이 써놓지 않더라도 무방하다.

출처: http://www.debuglab.com/knowledge/int3.html
크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)

1. IDispatch 인터페이스에 대한 COM Smart Pointer 만드는 법

[CODE]
_com_ptr_t<_com_IIID<IDispatch, &__uuidof(IDispatch)> > oDispatch1(/* 이곳에 Interface Pointer를 인수로 지정한다. */);
[/CODE]

2. 이것을 매크로 구문으로 만든다면...

[CODE]
#ifndef COM_PTR
#define COM_PTR(x) _com_ptr_t<_com_IIID<x, &__uuidof(x)> >
#endif // COM_PTR

COM_PTR(IDispatch) oDispatch1(/* 이곳에 Interface Pointer를 인수로 지정한다. */);
[/CODE]

* 필요한 헤더 파일: comip.h

크리에이티브 커먼즈 라이선스
Creative Commons License
Posted by Windows Azure MVP 남정현 (rkttu.com)