Microsoft MVP성태의 닷넷 이야기
.NET Framework: 322. regsvcs.exe 로 어셈블리 등록 시 시스템 변경 사항 [링크 복사], [링크+제목 복사]
조회: 21166
글쓴 사람
홈페이지
첨부 파일

regsvcs.exe 로 어셈블리 등록 시 시스템 변경 사항


regasm.exe로 어셈블리 등록 시 시스템 변경 사항 (1) - .NET 2.0 + x86/x64/AnyCPU
; https://www.sysnet.pe.kr/2/0/1286
 
regasm.exe로 어셈블리 등록 시 시스템 변경 사항 (2) - .NET 4.0 + .NET 2.0
; https://www.sysnet.pe.kr/2/0/1287

regasm.exe로 어셈블리 등록 시 시스템 변경 사항 (3) - Type Library
; https://www.sysnet.pe.kr/2/0/1288

regsvcs.exe 로 어셈블리 등록 시 시스템 변경 사항
; https://www.sysnet.pe.kr/2/0/1289




regasm.exe 가 COM 개체를 등록할 때 사용했다면, regsvcs.exe 는 COM+ 개체를 등록할 때 사용합니다.

이를 실습하기 위해 지난 번 만든 COM 개체를 COM+ 로 승격시켜 보겠습니다.

COM+ 에 등록될 응용 프로그램은 서버 유형과 라이브러리 유형이 있는데, 먼저 편의상 라이브러리 유형 먼저 살펴보도록 하겠습니다. 뭐 그다지 어렵지 않은데요. System.EnterpriseServices 어셈블리를 참조 추가하고 COM+ 관련 특성들을 정의한 후 ServicedComponent를 상속받아주면 됩니다.

[assembly: ApplicationActivation(ActivationOption.Library)]
[assembly: ApplicationAccessControl(false)]
[assembly: ApplicationName("ClassLibraryAsAdmin")]

namespace ClassLibraryAsAdmin
{
    [System.Runtime.InteropServices.Guid("96A2754F-0F5A-446B-A974-3EC47C04B27F")]
    public class AdminCode : ServicedComponent, IAdminCode
    {
        public void SetRegistryValue()
        {
            ...[생략]...
        }

        public void TestMethod()
        {
            ...[생략]...
        }

        public void TestMethod2()
        {
            ...[생략]...
        }
    }
}

그 다음, gacutil + regsvcs 로 등록해주면 되는데요. COM+ 역시 COM 과 같은 레지스트리 설정이 들어가게 되므로 x86/x64에 대한 구분이 필요하고 regasm.exe 처럼 등록해주는 regsvcs.exe 프로세스 자체가 x86이냐 x64이냐가 중요하게 됩니다.

또한, COM+도 닷넷 이전의 기술이기 때문에 .NET 2.0/4.0 에 대한 배려가 없으므로, regasm.exe와 같은 제약을 받게 됩니다. 따라서, 해당 모듈에 대해서 .NET 2.0/4.0 모두 설치하는 것은 의미가 없으며 호환성을 위해 .NET 2.0 하나만 설치하는 것이 좋습니다.

자... 한번 볼까요?

우선, GAC 에 등록하고,

D:\temp\net20\AnyCPU>"..\..\net20\gacutil.exe" /i ComBaseClass.dll
Microsoft (R) .NET Global Assembly Cache Utility.  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Assembly successfully added to the cache

D:\temp\net20\AnyCPU>"..\..\net20\gacutil.exe" /i ClassLibraryAsAdmin.dll
Microsoft (R) .NET Global Assembly Cache Utility.  Version 3.5.30729.1
Copyright (c) Microsoft Corporation.  All rights reserved.

Assembly successfully added to the cache

x86 .NET 2.0 regsvcs.exe 로 설치합니다.

D:\temp\net20\AnyCPU>C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\regsvcs.exe ClassLibraryAsAdmin.dll
Microsoft (R) .NET Framework Services Installation Utility Version 2.0.50727.3053
Copyright (c) Microsoft Corporation.  All rights reserved.

Auto exporting 'ComBaseClass, Version=1.0.0.0, Culture=neutral, PublicKeyToken=c91e971f6240da9f' as 'C:\WINDOWS\assembly\GAC_MSIL\ComBaseClass\1.0.0.0__c91e971f6240da9f\ComBaseClass.tlb'.
Installed Assembly:
        Assembly: D:\temp\net20\AnyCPU\ClassLibraryAsAdmin.dll
        Application: ClassLibraryAsAdmin
        TypeLib: D:\temp\net20\AnyCPU\ClassLibraryAsAdmin.tlb

출력 결과를 보니, TLB 파일이 자동으로 생성되는데 재미있는 것은 regsvcs.exe 의 인자로 직접 전달된 어셈블리에 대한 Type Library는 GAC 에 생성되지 않는 반면, 그것이 참조한 DLL 의 Type Library는 GAC 에 직접 생성하기 때문에 최종적인 파일 배치는 다음과 같습니다.

C:\WINDOWS\assembly\GAC_MSIL\ComBaseClass\1.0.0.0__c91e971f6240da9f\ComBaseClass.dll
C:\WINDOWS\assembly\GAC_MSIL\ComBaseClass\1.0.0.0__c91e971f6240da9f\ComBaseClass.tlb

C:\WINDOWS\assembly\GAC_MSIL\ClassLibraryAsAdmin\1.0.0.0__465ff3ec4fc809d7\ClassLibraryAsAdmin.dll
D:\temp\net20\AnyCPU\ClassLibraryAsAdmin.tlb

레지스트리의 변화는 다음과 같습니다.

=== ClassLibraryAsAdmin 에 대한 ProgId/CLSID ===
ClassLibraryAsAdmin.AdminCode
Wow6432Node\ClassLibraryAsAdmin.AdminCode
Wow6432Node\CLSID\{96A2754F-0F5A-446B-A974-3EC47C04B27F}

=== ClassLibraryAsAdmin 에 대한 TypeLibrary ===
TypeLib\{728F14FE-B021-43EF-8AAC-D37A4AF5ED6A}
Wow6432Node\TypeLib\{728F14FE-B021-43EF-8AAC-D37A4AF5ED6A}

=== ComBaseClass 에 대한 TypeLibrary ===
TypeLib\{1CCE4407-4EA5-4A60-BAF8-809AB169CE4D}
Wow6432Node\TypeLib\{1CCE4407-4EA5-4A60-BAF8-809AB169CE4D}

=== ClassLibraryAsAdmin 에 대한 Interface === 
Interface\{D7CE4B41-D2A5-3CCD-BF38-5B8197825E09}
Wow6432Node\Interface\{D7CE4B41-D2A5-3CCD-BF38-5B8197825E09}

=== ComBaseClass 에 대한 Interface === 
Interface\{23172F2F-A3D3-4180-97AE-7805F74A5A46}
Wow6432Node\Interface\{23172F2F-A3D3-4180-97AE-7805F74A5A46}

regasm.exe /tlb 와 비교해서 동일하게 TypeLibrary 와 Interface 관련 레지스트리들이 등록되었지만 x86 regsvcs.exe 로 인해 64비트에 대한 "CLSID\{96A2754F-0F5A-446B-A974-3EC47C04B27F}" 값만 누락되어 있습니다.

regsvcs.exe 실행으로 인한 시스템의 변화는 이 뿐만 아니라 COM+ Catalog 에도 적용됩니다. 이에 대해서는 "Component Services" 관리자로 확인할 수 있습니다.

regsvcs_lib_1.png

이렇게 등록된 COM+ 개체는 .NET 2.0/4.0 의 x86 EXE 파일에서 접근하는 것이 가능합니다. x64 EXE 의 경우에는 CLSID 가 등록되지 않았으므로 당연히 Activator.CreateInstance 에 CLSID/ProgId를 넘겨주는 방식으로는 예외가 발생합니다.

다음은 x64 .NET 2.0 EXE 의 경우에 예외가 발생한 내용이고,

System.Runtime.InteropServices.COMException (0x80040154): Retrieving the COM class factory for component with CLSID {96A2754F-0F5A-446B-A974-3EC47C04B27F} failed due to the following error: 80040154.
   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.ActivateInstance(Type type) in d:\...[생략]...\Program.cs:line 105
   at ConsoleApplication1.Program.Main(String[] args) in d:\...[생략]...\Program.cs:line 67

다음은 x64 .NET 4.0 EXE 실행시에 발생한 것입니다. (REGDB_E_CLASSNOTREG 라는 문자열이 하나 더 들어가 있는 정도의 차이.)

System.Runtime.InteropServices.COMException (0x80040154): Retrieving the COM class factory for component with CLSID {96A2754F-0F5A-446B-A974-3EC47C04B27F} failed due to the following error: 80040154 Class not registered (Exception from HRESULT: 0x80040154 (REGDB_E_CLASSNOTREG)).
   at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
   at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache)
   at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipVisibilityChecks, Boolean skipCheckThis, Boolean fillCache)
   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
   at ConsoleApplication1.Program.ActivateInstance(Type type) in d:\...[생략]...\Program.cs:line 105
   at ConsoleApplication1.Program.Main(String[] args) in d:\...[생략]...\Program.cs:line 67

이 문제를 해결하기 위해 x64 regsvcs.exe 로도 해당 COM 개체를 등록시켜줘야 할 것 같은데요. 하지만, 실행시켜 보면 다음과 같은 예외를 만나게 됩니다.

D:\temp\net20\AnyCPU>C:\WINDOWS\Microsoft.NET\Framework64\v2.0.50727\regsvcs.exe ClassLibraryAsAdmin.dll
Microsoft (R) .NET Framework Services Installation Utility Version 2.0.50727.3053
Copyright (c) Microsoft Corporation. All rights reserved.

The following installation error occurred:
1: One or more of the components being installed are already registered as 32 bit components in the target application. You must install the 64 bit versions of the components being installed in a different COM+ application, or delete the existing 32 bit versions of the components being installed from the target COM+ application prior to attempting install of the 64 bit versions. COM+ applications cannot contain bit neutral components.


이미 COM+ Catalog 에 등록된 정보이기 때문에 중복해서 등록할 수 없다고 나옵니다. 그렇다면, COM+ 로 등록한 경우에는 "AnyCPU"로 빌드된 .NET DLL 인 경우 Platform 구분에 따라 사용이 불가능하게 되는 걸까요?

여기서 잠깐 생각해 봐야 할 사항이 있습니다. 정말 x64를 위해서 regsvcs가 필요한지에 대해서부터 따져봐야 합니다. 아래의 글에서도 수작업으로 레지스트리 등록을 해준적이 있었는데요.

regasm.exe 로 어셈블리 등록 시 시스템 변경 사항 (1) - .NET 2.0 + x86/x64/AnyCPU
; https://www.sysnet.pe.kr/2/0/1286

그렇습니다. 실제로 필요한 것은 x64 EXE 에서 발견할 수 있는 CLSID 값이기 때문에 그 부분만 등록되면 됩니다. 그렇다면 그냥 x64 regasm.exe 만 해주면 되겠군요. ^^

그렇게만 해 주면 x86/x64 .NET 2.0/4.0 EXE 에서 정상적으로 COM+ 개체를 활성화할 수 있습니다.




COM+ 를 서버(EXE) 유형으로 등록한 경우에는 어떨까요? 뭔가 다른 것이 있을까요?

일단, 위의 소스 코드에서 Server 유형 - ApplicationActivation(ActivationOption.Server) - 으로만 바꿔서 동일한 등록 절차를 거쳤는데 모든 EXE 프로세스에서 다음과 같은 오류가 발생했습니다. (Windows Server 2003 x64에서 테스트되었습니다.)

System.Runtime.InteropServices.COMException (0x8000401A): The server process could not be started because the configured identity is incorrect.  Check the username and password. (Exception from HRESULT: 0x8000401A)
   at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
   at System.EnterpriseServices.Thunk.Proxy.CoCreateObject(Type serverType, Boolean bQuerySCInfo, Boolean& bIsAnotherProcess, String& uri)
   at System.EnterpriseServices.ServicedComponentProxyAttribute.CreateInstance(Type serverType)
   at System.Runtime.Remoting.Activation.ActivationServices.IsCurrentContextOK(Type serverType, Object[] props, Boolean bNewObj)
   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.ActivateInstance(Type type) in d:\...[생략]...\Program.cs:line 108
   at ConsoleApplication1.Program.Main(String[] args) in d:\...[생략]...\Program.cs:line 83

뜻밖의 오류였는데요. 이 오류에 대한 원인을 찾느라 한동안 헤맸는데, 바로 'Console' 로그인 사용자가 없는 상태에서 RDP로만 접속한 환경인 경우 이런 오류가 발생했습니다. (NT 서비스로 구동되는 프로세스에서는 이 오류가 당연히 발생할 수 있지만,) COM+ identity의 기본 설정인 "Interactive User"에는 RDP 로그인 계정은 포함되지 않는 것입니다.

사실, 현실적으로는 COM+ 속성창에서 "Identity" / "Account" 값을 "Interactive User" 이외의 값으로 설정하기 마련인데요.

regsvcs_lib_2.png

이렇게 해서, 실행을 하고 나면 COM+ 서버 유형의 경우에는 하나 더 고려해야 할 것이 있습니다. 라이브러리 유형인 경우에는 DLL 이 호출된 프로세스 측에서 활성화 되기 때문에 x86/x64 EXE에 따라서 자유롭게 동작을 하게 되는 반면 '서버 유형'으로 활성화 된 경우에는 .NET DLL 이 COM+의 dllhost.exe 위에 올려지기 때문에 x86/x64 가 분명하게 결정되어져야 합니다. 바로 그 결정되는 기준이 regsvcs.exe의 플랫폼 버전에 따릅니다.

x86 용 regsvcs.exe 로 등록한 경우, 64비트 운영체제에서는 32비트의 dllhost.exe 가 실행되고 그 위에서 호스팅이 되는 차이가 발생하는 것입니다.

서버 유형인 경우 재미있는 점이 또 하나 있는데요. 호출하는 측의 플랫폼 유형(x86/x64)이 상관없기 때문에 CLSID/ProgId 가 등록된 레지스트리 유무에 관계없이 활성화가 됩니다. 레지스트리에 없는데도 활성화되는 것을 보면, 아마도 윈도우 시스템 자체적으로 COM+ 카탈로그의 우선순위를 높게 해서 풀이를 하는 것 같습니다. 따라서, COM+ 서버 유형인 경우에는 굳이 레지스트리에 다음과 같은 항목들을 x86/x64에 따라 꼼꼼하게 맞춰줄 필요는 없습니다.

ClassLibraryAsAdmin.AdminCode
CLSID\{96A2754F-0F5A-446B-A974-3EC47C04B27F}
Wow6432Node\ClassLibraryAsAdmin.AdminCode
Wow6432Node\CLSID\{96A2754F-0F5A-446B-A974-3EC47C04B27F}

휴~~~, 여기까지 해서 그 동안의 gacutil / regasm / regsvcs 시리즈가 모두 끝이 납니다. ^^

첨부된 파일은 COM+ 라이브러리 유형서버 유형의 테스트에 사용된 간단한 프로젝트입니다.




[이 글에 대해서 여러분들과 의견을 공유하고 싶습니다. 틀리거나 미흡한 부분 또는 의문 사항이 있으시면 언제든 댓글 남겨주십시오.]

[연관 글]





[최초 등록일: ]
[최종 수정일: 8/6/2018 ]

Creative Commons License
이 저작물은 크리에이티브 커먼즈 코리아 저작자표시-비영리-변경금지 2.0 대한민국 라이센스에 따라 이용하실 수 있습니다.
by SeongTae Jeong, mailto:techsharer@outlook.com

비밀번호

댓글 쓴 사람
 



2013-04-17 06시34분
[최영수] AnyCPU로 개발한 프로그램이 x64서버에서 동작을 못해서 고생했는데 덕분에 잘 해결되었습니다.
[손님]
2013-04-17 06시51분
^^ 도움을 받았다고 하시니 쓴 보람이 생깁니다. (사실, 이런 류의 글은 누가 읽어볼지 저 스스로도 의심스러울 때가 있거든요. ^^)
정성태
2013-07-21 02시48분
[박일용] 도움되고 있습니다. ㅎㅎ
[손님]
2019-11-13 11시02분
[손님] 안녕하세요. 오래된 글에 질문을 드려도 될런지 모르곘네요.
regsvcs를 통해서 Com+로 등록을 하려고 하면 오류가 발생하고 있습니다.

다음 설치 오류가 발생했습니다.
1: 'd:\........\project.dll' 어셈블리를 로드하지 못했습니다.
2: 파일이나 어셈블리 '............project.dll' 또는 여기에 종속되어 있는 파일이나 어셈블리 중 하나를 로드할 수 없습니다. 프로그램을 잘못된 형식으로 로드하려고 했습니다.

라고 나오고 있습니다.
서버에 COM+로 등록을 하고 사용을 하려고 합니다.
위에 설명하신 것처럼 ActivationOption.Server 로 변경도 했습니다.
다만 제가 적용하려고 하는 프로젝트가 C#과 C++ 둘다 사용하고 있으며 CLI로 연결되어 있습니다.
즉, C#에서 만든 DLL이 C++에서 만든 DLL을 참조하고 있기도 합니다.

어떻게 해야 할지 막막하여 지푸라기라도 잡는 심정으로 문의드려봅니다.
감사합니다.

[손님]
2019-11-13 11시09분
아래의 글에 질문을 중복하셨는데, 이후 덧글 진행도 거기서 해주세요.

http://www.sysnet.pe.kr/3/0/5258
정성태

[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...
NoWriterDateCnt.TitleFile(s)
12153정성태2/23/202047.NET Framework: 898. Trampoline을 이용한 후킹의 한계파일 다운로드1
12152정성태2/23/202041.NET Framework: 897. 실행 시에 메서드 가로채기 - CLR Injection: Runtime Method Replacer 개선 - 세 번째 이야기(Trampoline 후킹)파일 다운로드1
12151정성태2/23/202035.NET Framework: 896. C# - Win32 API를 Trampoline 기법을 이용해 C# 메서드로 가로채는 방법 - 두 번째 이야기 (원본 함수 호출)파일 다운로드1
12150정성태2/23/202055.NET Framework: 895. C# - Win32 API를 Trampoline 기법을 이용해 C# 메서드로 가로채는 방법파일 다운로드1
12149정성태2/20/202034.NET Framework: 894. eBEST C# XingAPI 래퍼 - 연속 조회 처리 방법
12148정성태2/23/2020102디버깅 기술: 163. x64 환경에서 구현하는 다양한 Trampoline 기법
12147정성태2/19/202091디버깅 기술: 162. x86/x64의 기계어 코드 최대 길이
12146정성태2/18/202068.NET Framework: 893. eBEST C# XingAPI 래퍼 - 로그인 처리파일 다운로드1
12145정성태2/18/202050.NET Framework: 892. eBEST C# XingAPI 래퍼 - Sqlite 지원 추가파일 다운로드1
12144정성태2/23/2020122.NET Framework: 891. 실행 시에 메서드 가로채기 - CLR Injection: Runtime Method Replacer 개선 - 두 번째 이야기파일 다운로드1
12143정성태2/13/202067.NET Framework: 890. 상황별 GetFunctionPointer 반환값 정리 - x64파일 다운로드1
12142정성태2/13/2020100.NET Framework: 889. C# 코드로 접근하는 MethodDesc, MethodTable파일 다운로드1
12141정성태2/10/2020155.NET Framework: 888. C# - ASP.NET Core 웹 응용 프로그램의 출력 가로채기 [1]파일 다운로드1
12140정성태2/10/2020102.NET Framework: 887. C# - ASP.NET 웹 응용 프로그램의 출력 가로채기파일 다운로드1
12139정성태2/9/2020148.NET Framework: 886. C# - Console 응용 프로그램에서 UI 스레드 구현 방법
12138정성태2/9/2020136.NET Framework: 885. C# - 닷넷 응용 프로그램에서 Sqlite 사용파일 다운로드1
12137정성태2/9/202081오류 유형: 592. [AhnLab] 경고 - 디버거 실행을 탐지했습니다.
12136정성태2/6/202053Windows: 166. Windows + S(또는 Q)로 뜨는 작업 표시줄의 검색 바가 동작하지 않는 경우
12135정성태2/6/202088개발 환경 구성: 468. Nuget 패키지의 로컬 보관 폴더를 옮기는 방법
12134정성태2/5/2020146.NET Framework: 884. eBEST XingAPI의 C# 래퍼 버전 - XingAPINet Nuget 패키지파일 다운로드1
12133정성태2/7/2020144디버깅 기술: 161. Windbg 환경에서 확인해 본 .NET 메서드 JIT 컴파일 전과 후 - 두 번째 이야기
12132정성태2/20/2020265.NET Framework: 883. C#으로 구현하는 Win32 API 후킹(예: Sleep 호출 가로채기)파일 다운로드1
12131정성태1/27/2020208개발 환경 구성: 467. LocaleEmulator를 이용해 유니코드를 지원하지 않는(한글이 깨지는) 프로그램을 실행하는 방법
12130정성태1/26/2020117VS.NET IDE: 142. Visual Studio에서 windbg의 "Open Executable..."처럼 EXE를 직접 열어 디버깅을 시작하는 방법
12129정성태1/26/2020343.NET Framework: 882. C# - 키움 Open API+ 사용 시 Registry 등록 없이 KHOpenAPI.ocx 사용하는 방법
[1]  2  3  4  5  6  7  8  9  10  11  12  13  14  15  ...