성태의 닷넷 이야기
홈 주인
모아 놓은 자료
프로그래밍
질문/답변
사용자 관리
사용자
메뉴
아티클
외부 아티클
유용한 코드
온라인 기능
MathJax 입력기
최근 덧글
[정성태] Working with Rust Libraries from C#...
[정성태] Detecting blocking calls using asyn...
[정성태] 아쉽게도, 커뮤니티는 아니고 개인 블로그입니다. ^^
[정성태] 질문이 잘 이해가 안 됩니다. 우선, 해당 소스코드에서 ILis...
[양승조
] var대신 dinamic으로 선언해서 해결은 했습니다. 맞는 해...
[양승조
] 또 막혔습니다. ㅠㅠ var list = props[i].Ge...
[양승조
] 아. 감사합니다. 어제는 안됐던것 같은데....정신을 차려야겠네...
[정성태] "props[i].GetValue(props[i])" 코드에서 ...
[정성태] 저렇게 조각 코드 말고, 실제로 재현이 되는 예제 프로젝트를 압...
[정성태] Modules 창(Ctrl+Shift+U)을 띄워서, 해당 Op...
글쓰기
제목
이름
암호
전자우편
HTML
홈페이지
유형
제니퍼 .NET
닷넷
COM 개체 관련
스크립트
VC++
VS.NET IDE
Windows
Team Foundation Server
디버깅 기술
오류 유형
개발 환경 구성
웹
기타
Linux
Java
DDK
Math
Phone
Graphics
사물인터넷
부모글 보이기/감추기
내용
<div style='display: inline'> <h1 style='font-family: Malgun Gothic, Consolas; font-size: 20pt; color: #006699; text-align: center; font-weight: bold'>regasm.exe로 어셈블리 등록 시 시스템 변경 사항 (1) - .NET 2.0 + x86/x64/AnyCPU</h1> <p> regasm.exe는 .NET 어셈블리를 COM 개체로 등록해 주는 역할을 합니다. 그래서, C# 클래스를 C/C++에서 COM 개체로써 활성화 시켜 접근할 수 있기 때문에 Interop 차원에서 유용하게 쓸 수 있는데요.<br /> <br /> regasm.exe를 사용할 때 알고 계셔야 하는 것이 바로 COM 개체의 위치가 레지스트리에 있다는 점입니다. 왜냐하면 64비트 운영체제에서는 COM 개체 등록 위치가 플랫폼에 따라 다음과 같이 바뀌기 때문입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > x64: HKEY_CLASSES_ROOT\CLSID x86: HKEY_CLASSES_ROOT\<span style='color: blue; font-weight: bold'>Wow6432Node</span>\CLSID </pre> <br /> 이번에도 환경 테스트를 위해 프로젝트를 구성해 볼 텐데요. 편의상 <a target='tab' href='http://www.sysnet.pe.kr/2/0/1285'>지난번 gacutil.exe</a>를 설명할 때 <a target='tab' href='http://www.sysnet.pe.kr/bbs/DownloadAttachment.aspx?fid=717&boardid=331301885'>첨부한 코드</a>를 변경해 보겠습니다. 그리 많은 수정은 아니고, 단지 Guid 특성 추가 및 assembly 레벨로 ComVisible 특성을 true로 바꾼 후 각각 x86/x64로 빌드해 주는 것이 끝입니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>[assembly: ComVisible(true)]</span> namespace ClassLibraryAsAdmin { <span style='color: blue; font-weight: bold'>[System.Runtime.InteropServices.Guid("41AC8568-9230-4E63-B7C5-CAAD997EE207")]</span> public class AdminCode { public void SetRegistryValue() { if (IntPtr.Size == 4) { Console.Write("x86 "); } else { Console.Write("x64 "); } Console.WriteLine(".NET Framework: " + Environment.Version.ToString()); } } } </pre> <br /> <a target='tab' href='http://www.sysnet.pe.kr/2/0/1285'>gacutil.exe</a>의 경우에는 등록 기준을 판가름하는 것이 해당 C# DLL의 ".NET Framework" 버전이었는데요. 하지만 regasm.exe의 경우에는 COM 개체가 x86/x64에 따라 등록 위치가 달라지는 반면 C# DLL 자체는 "AnyCPU"로도 빌드될 수 있으므로 왠지 구분 단계가 모호해집니다. 그래서 DLL 단계에서 구분이 안 되기 때문에, 해당 DLL을 등록해 주는 regasm.exe 자체의 플랫폼에 따라 등록 위치가 달라진다는 특징을 갖게 됩니다.<br /> <br /> 결국 다음과 같이 4가지 상황으로 나뉩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > x86 + .NET 2.0: %windir%\Microsoft.NET\Framework\v2.0.50727\regasm.exe x64 + .NET 2.0: %windir%\Microsoft.NET\Framework64\v2.0.50727\regasm.exe x86 + .NET 4.0: %windir%\Microsoft.NET\Framework\v4.0.30319\regasm.exe x64 + .NET 4.0: %windir%\Microsoft.NET\Framework64\v4.0.30319\regasm.exe </pre> <br /> 일단, x86 .NET 2.0 DLL을 .NET 2.0의 gacutil + x86 regasm으로 처리하는 경우를 볼까요?<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>gacutil.exe /i ClassLibraryAsAdmin.dll</span> Microsoft (R) .NET Global Assembly Cache Utility. <span style='color: blue; font-weight: bold'>Version 3.5.30729.1</span> Copyright (c) Microsoft Corporation. All rights reserved. <span style='color: blue; font-weight: bold'>Assembly successfully added to the cache</span> C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\<span style='color: blue; font-weight: bold'>regasm.exe ClassLibraryAsAdmin.dll</span> Microsoft (R) .NET Framework Assembly Registration Utility <span style='color: blue; font-weight: bold'>2.0.50727.3053</span> Copyright (C) Microsoft Corporation 1998-2004. All rights reserved. <span style='color: blue; font-weight: bold'>Types registered successfully</span> </pre> <br /> 이로 인해 변경된 레지스트리를 보면 다음과 같고,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > HKEY_CLASSES_ROOT\ClassLibraryAsAdmin.AdminCode HKEY_CLASSES_ROOT\<span style='color: blue; font-weight: bold'>Wow6432Node</span>\ClassLibraryAsAdmin.AdminCode HKEY_CLASSES_ROOT\<span style='color: blue; font-weight: bold'>Wow6432Node</span>\CLSID\{41AC8568-9230-4E63-B7C5-CAAD997EE207} </pre> <br /> 생성된 GAC 경로는 아래와 같습니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\WINDOWS\assembly\<span style='color: blue; font-weight: bold'>GAC_32</span>\ClassLibraryAsAdmin\1.0.0.0__465ff3ec4fc809d7\ClassLibraryAsAdmin.dll </pre> <br /> 보시는 것처럼, "ProgId"의 경우에는 (쓸데없이) x86/x64에 모두 등록된 반면, CLSID는 x86에만 등록되어 있습니다. 이것이 등록에 사용한 regasm.exe가 x86인 경우의 결과입니다.<br /> <br /> 실제로 등록된 C# COM 개체를 다음과 같이 활성화하는 테스트를 거치면,<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Guid guid = new Guid("{41AC8568-9230-4E63-B7C5-CAAD997EE207}"); Type type = <span style='color: blue; font-weight: bold'>Type.GetTypeFromCLSID(guid);</span> object comObject = <span style='color: blue; font-weight: bold'>Activator.CreateInstance(type);</span> AdminCode adminCode = comObject as AdminCode; adminCode.TestMethod(); </pre> <br /> x86 EXE 프로세스에서는 정상적으로 실행이 되지만, x64 EXE에서는 아래와 같은 오류를 발생합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > <span style='color: blue; font-weight: bold'>Unhandled Exception: System.Runtime.InteropServices.COMException (0x80040154): Retrieving the COM class factory for component with CLSID {41AC8568-9230-4E63-B7C5-CAAD997EE207} failed due to the following error: 80040154.</span> at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandle& ctor, Boolean& bNeedSecurityCheck) at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean fillCache) at System.RuntimeType.CreateInstanceImpl(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean fillCache) at System.Activator.CreateInstance(Type type, Boolean nonPublic) at ConsoleApplication1.Program.Main(String[] args) in d:\...[생략]...\ConsoleApplication1\Program.cs:line 19 </pre> <br /> 물론, 위의 코드를 CLSID가 아닌 ProgId로 테스트하는 것도 가능합니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Type type = <span style='color: blue; font-weight: bold'>Type.GetTypeFromProgID("ClassLibraryAsAdmin.AdminCode");</span> object comObject = Activator.CreateInstance(type); AdminCode adminCode = comObject as AdminCode; adminCode.TestMethod(); </pre> <br /> 결과는 CLSID로 활성화한 경우와 다르지 않습니다.<br /> <br /> 자, 이제 그럼 반대로 해보면, x64 C# DLL을 gacutil + x64 regasm.exe로 등록하면 레지스트리와 파일은 각각 다음과 같이 설정됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > HKEY_CLASSES_ROOT\ClassLibraryAsAdmin.AdminCode HKEY_CLASSES_ROOT\CLSID\{41AC8568-9230-4E63-B7C5-CAAD997EE207} HKEY_CLASSES_ROOT\Wow6432Node\ClassLibraryAsAdmin.AdminCode C:\WINDOWS\assembly\<span style='color: blue; font-weight: bold'>GAC_64</span>\ClassLibraryAsAdmin\1.0.0.0__465ff3ec4fc809d7\ClassLibraryAsAdmin.dll </pre> <br /> x86과 유사하게 등록이 이뤄진 것을 확인할 수 있는데요. 마찬가지로, ProgId는 x86/x64에 모두 등록되었지만 CLSID는 x64에만 등록되었고, x64 EXE에서는 정상적으로 C# COM DLL을 사용할 수 있는 반면 x86 EXE에서는 System.Runtime.InteropServices.COMException이 발생합니다.<br /> <br /> 여기까지는, 그런대로 우리가 예상했던 결과와 크게 다르지 않습니다.<br /> <br /> <hr style='width: 50%' /><br /> <br /> 그렇다면, C# DLL을 "AnyCPU"로 두고 x86/x64 EXE 모두에서 정상적으로 활성화하고 싶다면 어떻게 해야 할까요? 일단, 해당 DLL을 x86 regasm.exe로 등록하면 x86 DLL을 등록했을 때와 동일한 레지스트리 값들만 설정이 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > HKEY_CLASSES_ROOT\ClassLibraryAsAdmin.AdminCode HKEY_CLASSES_ROOT\Wow6432Node\ClassLibraryAsAdmin.AdminCode HKEY_CLASSES_ROOT\Wow6432Node\CLSID\{41AC8568-9230-4E63-B7C5-CAAD997EE207} </pre> <br /> 물론, gacutil은 정상적으로 플랫폼에 무관한게 GAC_MSIL로 등록이 됩니다.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > C:\WINDOWS\assembly\<span style='color: blue; font-weight: bold'>GAC_MSIL</span>\ClassLibraryAsAdmin\1.0.0.0__465ff3ec4fc809d7\ClassLibraryAsAdmin.dll </pre> <br /> 이런 레지스트리 값으로는 당연히 x86 EXE 프로세스에서만 정상적으로 C# COM 개체를 활성화할 수 있고, x64 EXE 프로세스에서는 System.Runtime.InteropServices.COMException 예외가 발생합니다. 문제는, x64용 CLSID가 등록되지 않았기 때문인데 이를 해결하기 위해 "HKEY_CLASSES_ROOT\CLSID\{41AC8568-9230-4E63-B7C5-CAAD997EE207}" 경로에 해당하는 레지스트리 값들을 수작업으로 맞춰주면 해결이 됩니다.<br /> <br /> 예를 들면, 다음과 같은 식의 레지스트리 값을 등록해 주는 것이지요.<br /> <br /> <pre style='margin: 10px 0px 10px 10px; padding: 10px 0px 10px 10px; background-color: #fbedbb; overflow: auto; font-family: Consolas, Verdana;' > Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\CLSID\{41AC8568-9230-4E63-B7C5-CAAD997EE207}] @="ClassLibraryAsAdmin.AdminCode" [HKEY_CLASSES_ROOT\CLSID\{41AC8568-9230-4E63-B7C5-CAAD997EE207}\Implemented Categories] [HKEY_CLASSES_ROOT\CLSID\{41AC8568-9230-4E63-B7C5-CAAD997EE207}\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}] [HKEY_CLASSES_ROOT\CLSID\{41AC8568-9230-4E63-B7C5-CAAD997EE207}\InprocServer32] @="mscoree.dll" "ThreadingModel"="Both" "Class"="ClassLibraryAsAdmin.AdminCode" "Assembly"="ClassLibraryAsAdmin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=465ff3ec4fc809d7" "RuntimeVersion"="v2.0.50727" [HKEY_CLASSES_ROOT\CLSID\{41AC8568-9230-4E63-B7C5-CAAD997EE207}\InprocServer32\1.0.0.0] "Class"="ClassLibraryAsAdmin.AdminCode" "Assembly"="ClassLibraryAsAdmin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=465ff3ec4fc809d7" "RuntimeVersion"="v2.0.50727" [HKEY_CLASSES_ROOT\CLSID\{41AC8568-9230-4E63-B7C5-CAAD997EE207}\ProgId] @="ClassLibraryAsAdmin.AdminCode" </pre> <br /> 당연히, 이건 제가 이론적인 부분을 설명하려고 억지로 구성해 본 것이고 현실적으로 보면 x64용 regasm.exe를 실행해 주는 것이 맞습니다.<br /> <br /> 정리해 볼까요? AnyCPU 타입으로 빌드된 C# COM 개체를 x86/x64 EXE 프로세스에서 모두 사용하고 싶다면 gacutil + x86 regasm + x64 regasm 등록 과정을 거치면 된다는 것!<br /> </p><br /> <br /><hr /><span style='color: Maroon'>[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]</span> </div>
첨부파일
스팸 방지용 인증 번호
7901
(왼쪽의 숫자를 입력해야 합니다.)