C, C++2007. 4. 7. 22:01

시스템에서 작동하는 CLR 버전을 인식하고 열거해주는 C++ 클래스


저자 Nishant Sivakumar.

개날역자 j2doll

원문 : http://www.codeproject.com/dotnet/DetectDotNet.asp 

invalid-file

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] dwBufferSizeszRootPath 버퍼 길이를 명시한다.

반환값 : 처리 성공 여부에 따라 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 버전들을 열거하는 원리가 있다.

  1. (레지스트리로부터) 닷넷 프레임워크 루트 설치 경로를 얻는다. 전형적으로, 이것은 C:\WINDOWS\Microsoft.NET\Framework 같을 것이다. (c:\WINDOWS 윈도 설치 경로 경우)
  2. 루트 설치 경로의 모든 경로들을 찾아 낸다. 대부분 (항상 그렇지는 않다) 이러한 경로들은 각각의 닷넷 프레임워크들의 기반 경로들일 것이다.
  3. GetRequestedRuntimeInfo 사용하여 이전 절차에서 인식한 CLR 버전들의 존재 유무를 질의한다. 놀랍게도, GetRequestedRuntimeInfo 인자 pwszVersion 사실 닷넷 루트 경로 안에 특정한 CLR 버전들이 존재할 경우의 경로 이름이다. 거기에 만약 디폴트 'renamed' 경로 이름은 (보통 "v2.0.50215" 처럼) GetRequestedRuntimeInfo 에서 여전히 성공할 것이다.
  4. 한번 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 716 - 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/

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 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
Posted by Jay Two