시스템에서 작동하는 CLR 버전을 인식하고 열거해주는 C++ 클래스
개날역자 j2doll
원문 : http://www.codeproject.com/dotnet/DetectDotNet.asp
source files and release binaries [version 2.2] - 57.5 Kb
개요
CDetectDotNet 클래스는 컴퓨터에 닷넷 프레임워크가 설치되어 있는지를 인식한다. 그리고 현재 설치된 프레임워크 버전들을 보여 준다.
그리고 예측불가하지 않기 위해, 어떤 닷넷에도 의존성이 없는 unmanaged C++클래스이다. 닷넷 프레임웤에서 작동한다면 쓸모없는 것이 될 것이다. (당연하다; 현재 지원 닷넷 프레임워크 버전도 모르는데 어떤 버전의 managed 코드를 작동시킨다는 말인가?!?! 아에 닷넷 프레임워크가 없을 수도 있다.)
필자는 본 클래스가 타겟 머신에 어떤 버전의 닷넷이 설치되어 있는지를 인식하는 것이 필요한 설치 프로그램에서 유용하게 사용될 것이라고 믿는다.
주목해야 할 중요한 점은 닷넷 버전들과 (BCL 과 함께 매칭하는) CLR 버전들이다. 그리고 연계하는 SDK들이 존재하는지의 필요성은 필수적이지는 않다. ; 여기서 주목할 것은 '닷넷 SDK'들과 '닷넷 런타임'들의 차이점이다.
클래스 사용하기
DetectDotNet.h 와 DetectDotNet.cpp 를 사용하려는 프로젝트에 추가한다.
#include "DetectDotNet.h"
// . . .
CDetectDotNet detect;
vector<string> CLRVersions;
cout << "Is .NET present : "
<< (detect.IsDotNetPresent() ? "Yes" : "No") << endl;
TCHAR szPath[300];
cout << "Root Path : "
<< (detect.GetInstallRootPath(szPath, 299) ? szPath : "") << endl;
cout << "Number of CLRs detected : "
<< (int)detect.EnumerateCLRVersions(CLRVersions) << endl;
cout << "CLR versions available :-" << endl;
for(vector<string>::iterator it = CLRVersions.begin();
it < CLRVersions.end(); it++)
{
cout << *it << endl;
}
cout << "Press any key..." << endl;
getch();
예제 출력
Is .NET present : Yes
Root Path : C:\WINDOWS\Microsoft.NET\Framework\
Number of CLRs detected : 2
CLR versions available :-
2.0.50215
1.1.4322
Press any key...
Public 인터페이스
- CDetectDotNet();
CDetectDotNet 갹체를 생성한다.
- bool IsDotNetPresent();
반환값 : 시스템에 닷넷 프레임워크가 있는지의 여부를 반환한다. (반환값 : true / false)
- bool GetInstallRootPath(TCHAR* szRootPath, DWORD dwBufferSize);
닷넷 프레임워크의 설치 루트 경로를 반환한다.
- [out] szRootPath – 닷넷 루트 설치 경로가 반환된다.
- [in] dwBufferSize – szRootPath 의 버퍼 길이를 명시한다.
반환값 : 처리 성공 여부에 따라 true 또는 false 를 반환한다.
- size_t EnumerateCLRVersions(vector<string>& CLRVersions);
시스템에 작동하는 CLR 버전들을 열거한다.
- CLRVersions - 시스템에서 인시되는 CLR 버전들을 포함하고 있다.
반환값 : CLR 버전들의 개수를 반환한다.
구현
.NET 프레임워크 인식하기
필자가 발견한 닷넷 프레임워크의 존재 유무를 알아내는 매우 쉬운 기술은 LoadLibrary "mscoree.dll" 를 하고, 성공 여부를 보는 것이다.
또한 GetProcAddress 로 위치한 DLL 에서 어디에 OS 버전이 있는지의 시나리오들을 감찰하는 "GetCORVersion" 를 읽는 것도 또한 한다.
그리고 (클래스 생성자에서) 결과값을 캐쉬한다, 그리하여 부가적인 호출들이 LoadLibrary/GetProcAddress 호출을 반복하는 필요없다.
bool CDetectDotNet::IsDotNetPresent()
{
return m_bDotNetPresent;
}
bool CDetectDotNet::IsDotNetPresentInternal()
{
bool bRet = false;
//Attempt to LoadLibrary "mscoree.dll" (the CLR EE shim)
HMODULE hModule = LoadLibrary(_T("mscoree"));
if(hModule)
{
//Okay - that worked, but just to ensure that this is
//not a placeholder DLL shipped with some earlier OS versions,
//we attempt to GetProcAddress "GetCORVersion".
bRet = (GetProcAddress(hModule, "GetCORVersion") != NULL);
FreeLibrary(hModule);
}
return bRet;
}
CLR 버전들 열거하기
여기에 필자가 시스템의 사용 가능한 CLR 버전들을 열거하는 원리가 있다.
- (레지스트리로부터) 닷넷 프레임워크 루트 설치 경로를 얻는다. 전형적으로, 이것은 C:\WINDOWS\Microsoft.NET\Framework 와 같을 것이다. (c:\WINDOWS가 윈도 설치 경로 인 경우)
- 루트 설치 경로의 모든 경로들을 찾아 낸다. 대부분 (항상 그렇지는 않다) 이러한 경로들은 각각의 닷넷 프레임워크들의 기반 경로들일 것이다.
- GetRequestedRuntimeInfo 를 사용하여 이전 절차에서 인식한 CLR 버전들의 존재 유무를 질의한다. 놀랍게도, GetRequestedRuntimeInfo 의 인자 pwszVersion 는 사실 닷넷 루트 경로 안에 특정한 CLR 버전들이 존재할 경우의 경로 이름이다. 거기에 만약 디폴트 'renamed' 경로 이름은 (보통 "v2.0.50215" 처럼) GetRequestedRuntimeInfo 에서 여전히 성공할 것이다.
- 한번 GetRequestedRuntimeInfo 가 성공하면, 버전 문자열을 얻을 필요가 있다. ('renamed' 경로명 상황을 살펴 보면서) 이것을 하기 위해서 필자는 간단하게 그 경로 안에 있는 mscorlib.dll 를 살펴 보았다. 그리고 그 파일 버전을 추출해 내었다. 또한 프레임워크이 설치된 경로에 mscorlib.dl 를 가지고 있다는 것으로, 어떠한 성공적인 구현이 있었다는 것을 확신할 수 있을 것이다.
노트 - mscoree.h 에서GetRequestedRuntimeInfo를 찾을 수 있다. 필자가 이야기한 것은 VS.NET 2005 Beta 2 버전이다.
STDAPI GetRequestedRuntimeInfo(
LPCWSTR pExe, LPCWSTR pwszVersion, LPCWSTR pConfigurationFile,
DWORD startupFlags, DWORD runtimeInfoFlags,
LPWSTR pDirectory, DWORD dwDirectory, DWORD *dwDirectoryLength,
LPWSTR pVersion, DWORD cchBuffer, DWORD* dwlength);
여기에 닷넷 설치 루트 경로의 스크린샷이 있다.
'renamed' 경로 이름을 알아 내자. 사실 설치 경로는 "v2.0.50215" 이다.
pwszVersion 와 함께 GetRequestedRuntimeInfo 의 호출은 "renamed" 를 사실상 성공하도록 설정한다. (왜냐하면 GetRequestedRuntimeInfo 가 필자가 버전을 추출한 기술과 매우 유사한 기술이기 때문이다.) 우습게도 pVersion 에서 반환되는 버전 문자열은 경로의 이름이고, 실제 버전 문자열 이름이 아니다. 하지만 이것은 이 경로 안에 mscorlib.dll 가 있는지를 우리가 알아 내는데 큰 문제가 아니다. 그리고 이 버전 문자열은 포함된 경로의 CLR 버전을 준다.
mscorlib 어셈블리로부터.NET 버전 알아 내기
string CDetectDotNet::GetVersionFromFolderName(string szFolderName)
{
string strRet = "<Version could not be extracted from mscorlib>";
TCHAR szRootPath[g_cPathSize];
if(GetInstallRootPath(szRootPath, g_cPathSize))
{
USES_CONVERSION;
string szFilepath = T2A(szRootPath);
szFilepath += (szFolderName + "\\mscorlib.dll");
string s = GetDotNetVersion(A2CT(szFilepath.c_str()));
if(s.size() > 0)
strRet = s;
}
return strRet;
}
특정 닷넷 버전의 존재 유무 체크하기
[생략]
CLR 버전들의 목록 얻기
size_t CDetectDotNet::EnumerateCLRVersions(vector<string>& CLRVersions)
{
CLRVersions.clear();
USES_CONVERSION;
vector<string> PossibleCLRVersions;
EnumeratePossibleCLRVersionsInternal(PossibleCLRVersions);
for(vector<string>::iterator it = PossibleCLRVersions.begin();
it < PossibleCLRVersions.end(); it++)
{
if(CheckForSpecificCLRVersionInternal(A2CW((*it).c_str())))
{
CLRVersions.push_back(GetVersionFromFolderName(*it));
}
}
return CLRVersions.size();
}
결론
필자는 모든 버전의 윈도와 모든 버전의 닷넷에서 테스트해보지는 못 했다.
따라서 항상 제대로 작동한다고 보장하지는 못 한다.
하지만 귀하에게서 제대로 작동되지 않는다면 필자에게 포럼을 통하여 알려 주기를 바란다.
그리고 만약 귀하가 정확한 문제의 표현을 포스팅하였다면, 필자가 문제를 고치거나 우회하는 노력을 할 것이다. 하지만, 필자가 문제를 수정하는 방법을 알지 못한다면 필자에게 말해 주어야 한다. Enjoy!
History (너무 후다닥 만든거 아니야…-_-;)
-
2005년 7월 21년 - v2.2
- 2.1 버전에서 꾸물럭거렸던 생성자에서 버그 수정. (필자가 m_bDotNetPresent 를 초기화하는 IsDotNetPresent 를 잘못 호출하였다. )
-
2005년 7월 17일 - v2.1
- 내부적으로 IsDotNetPresent 와 GetInstallRootPath 를 cache 한다. (그래서 작동하는 인식 코드는 오직 한번만 실행되어 진다. - 생성자에서)
- 만약 경로들의 이름이 수정되었더라도 알아내게끔 클래스가 CLR 버전들을 인식하는 버전 열거 방식 수정. 측정 CLR 버전을 구분하는 mscorlib.dll 에서의 버전 문자열을 추출해냄으로 한다.
-
2005년 7월16일 - v2.0
- mscoree.h 와 mscoree.lib 의 의존성 제거
- stub 실행 가능한 외부 detector 를 invoke 하는 것을 제거
- 특정 CLR 버전을 확인하기 위하여 GetRequestedRuntimeInfo 를 사용
- LoadLibrary 가 성공적으로 이전 OS 버전를 얻는 DLL 위치를 얻는지의 여부를 mscoree.dll 가 구별하기 위해 IsDotNetPresent 를 갱신
-
2005년 7월 15일 - v1.0
- 클래스의 최초 버전 업로드
그 외 Q&A 리스트
일단… 전반적으로 이러한 방식은'꽁수(not the right way)'이며 정상적인 접근법이 아니라는 의견이 꽤 있다. 하지만, 역자가 보기에는 매우 직관적이며 또한 현실적인 접근법인 듯하다. (뭐랄까… 어떻게 안되겠니?! 라고나 할까나…) 코더에게 뭘 바래…직접 짜든가…된장 찌개 끓이던가…
번역문 : http://j2doll.tistory.com/64/
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
'C C++' 카테고리의 다른 글
CMainFrame IDR_MAINFRAME 확장자 연결 문자열 (0) | 2007.04.26 |
---|---|
GetProcAddress 과 Unicode (0) | 2007.04.14 |
SAFEARRAY 생성 / 소거 API 예제 (1) | 2007.04.03 |
CComSafeArray 클래스 (0) | 2007.04.03 |
visual studio 2005 service pack 1 (0) | 2007.03.25 |