Reg-Free COM이란 무엇인가 - 등록 불필요로 COM을 쓰는 구조와, 맞는 장면·맞지 않는 장면

· · COM, Reg-Free COM, Registration-Free COM, Windows 개발, 레거시 기술

COM / ActiveX / OCX의 안건에서는 배포와 갱신 때마다 같은 진흙이 나옵니다.

  • regsvr32가 필요
  • 관리자 권한이 필요해지기 쉬움
  • 다른 앱이 넣은 다른 버전과 부딪침
  • 언인스톨 후에 다른 제품까지 말려듦
  • 개발기에서는 동작하는데 클린 환경에서 동작하지 않음

이 진흙탕을 꽤 줄일 수 있는 것이 Reg-Free COM입니다. 다만 이름에 비해 「COM의 번거로움이 전부 사라지는 마법」은 아닙니다. 사라지는 것은 주로 글로벌 등록에 끌려가는 번거로움입니다. bitness, 의존 DLL, 형 라이브러리, 스레드 모델의 어려움까지는 사라지지 않습니다.

이 글에서는 Reg-Free COM을 Windows 데스크톱 앱에서 COM DLL / OCX을 앱 로컬에 가두어 쓰는 문맥을 중심으로 정리합니다.

1. 먼저 결론(한마디로)

먼저 거칠지만 도움이 되는 표현으로 하면 이렇습니다.

  • Reg-Free COM은 COM의 등록 정보를 레지스트리가 아니라 매니페스트로 가지는 방식입니다
  • 실행 시에는 CoCreateInstanceCLSIDFromProgID의 해결에서 액티베이션 컨텍스트가 먼저 보입니다
  • 그래서 COM DLL / OCX를 앱마다 private로 가질 수 있게 됩니다
  • 주된 장점은 XCOPY 배포가 쉬운 것, 버전 충돌을 피하기 쉬운 것, 언인스톨이 망가지기 어려운 것입니다
  • 다만 32bit / 64bit 문제는 사라지지 않습니다. 여기는 근성으로 넘을 수 없습니다
  • 또한 의존 DLL, 형 라이브러리, 설계 시 참조, 비표준적인 등록 의존은 별도로 생각할 필요가 있습니다
  • 실무에서는 앱 전용의 COM 부품을 옆에 두고 싶을 때 꽤 잘 맞습니다

요컨대 Reg-Free COM은 COM의 액티베이션을 앱 단위로 되돌리는 구조입니다.

2. 이 글에서 말하는 Reg-Free COM

Reg-Free COM은 Registration-Free COM의 약자입니다. 일본어에서는 「등록 불필요 COM」이라고 쓰이기도 합니다.

여기서 말하는 「등록 불필요」는 COM을 쓰기 위해 HKCR / CLSID / InprocServer32 같은 글로벌 레지스트리 등록에 전면 의존하지 않는다는 의미입니다. COM 그 자체가 사라진다는 것도 GUID가 불필요해진다는 것도 아닙니다.

이 글에서는 주로 다음 같은 것을 대상으로 합니다.

  • 네이티브 COM DLL
  • ATL 기반 COM 서버
  • ActiveX / OCX
  • .NET Framework 기반 COM 상호 운용
  • .NET 5+ / .NET 8의 COM host를 쓴 공개

반대로 이 글에서 강조하고 싶은 것은 다음 2점입니다.

  1. Reg-Free COM은 「액티베이션」의 이야기이다
  2. 형 정보의 배포나 설계 시의 참조 설정은 별도의 논점으로 남을 수 있다

여기를 섞으면 이야기가 꽤 탁해집니다.

3. 먼저 한 장으로 정리

우선 전체상을 한 장으로 보는 편이 빠릅니다.

flowchart LR
    APP["MyApp.exe"] --> AM["Application Manifest"]
    AM --> DEP["dependentAssembly"]
    DEP --> CM["Component / Assembly Manifest"]
    CM --> META["file / comClass / typelib"]
    META --> DLL["VendorControl.dll / .ocx"]

    APP --> ACTX["Activation Context"]
    ACTX --> COM["CLSIDFromProgID / CoCreateInstance"]
    COM --> DLL

일반적인 COM에서는 CoCreateInstance할 때 레지스트리를 따라가 어느 DLL을 로드할지를 정합니다. Reg-Free COM에서는 그 앞에 지금 유효한 액티베이션 컨텍스트를 보고, 거기에 쓰인 매니페스트 정보로부터 해결합니다.

그래서 같은 머신 위에서도 앱 A와 앱 B가 다른 버전의 같은 계통 COM 부품을 안고 동작하기 쉬워집니다. COM의 공유 문화를 조금 앱 로컬 쪽으로 되돌리는 느낌입니다.

4. 왜 일반적인 COM 배포는 무거워지기 쉬운가

일반적인 COM 배포가 무거운 것은 COM 자체가 나쁘다기보다 글로벌 등록의 전제가 있기 때문입니다.

대략 말하면 COM 클래스를 쓰려면 다음 같은 정보가 필요합니다.

정보 역할
CLSID 클래스를 일의적으로 식별하는 GUID
ProgID 사람이 다루기 쉬운 이름
InprocServer32 어느 DLL을 로드할까
ThreadingModel Apartment / Both 등의 전제
TypeLib 형 정보

이것들이 레지스트리에 들어가면 머신 전체에서는 편리합니다. 복수 앱에서 공유하기 쉽기 때문입니다.

다만 실무에서는 이 공유가 역효과를 냅니다.

  • 어느 제품의 셋업이 다른 제품의 COM 등록을 덮어쓴다
  • 언인스톨러가 「자기 것만 지울 셈」으로 공유 COM을 망가뜨린다
  • 개발기에 우연히 들어 있는 등록이 운영기에는 없다
  • 32bit와 64bit의 등록이 맞물리지 않아 현상만 기묘하게 어긋난다

COM 본체보다 배포 모델 쪽이 사람을 곤란하게 하는 경우가 꽤 많습니다. Reg-Free COM은 이 배포 모델의 괴로움을 줄이기 위한 구조입니다.

5. Reg-Free COM의 구조

5.1 애플리케이션 매니페스트로 의존 관계를 쓴다

우선 앱 쪽은 자신이 어느 side-by-side assembly에 의존하고 있는지를 애플리케이션 매니페스트에 씁니다.

이 매니페스트는,

  • MyApp.exe.manifest처럼 EXE의 옆에 둔다
  • EXE에 리소스로서 매립한다

중 어느 쪽으로도 다룰 수 있습니다. 실무에서는 배포나 교체를 보기 쉽게 하고 싶다면 외부 파일, 망가지기 어려움과 배포의 단순함을 우선한다면 매립이라는 구분이 많습니다.

덧붙여 외부 파일 버전과 매립 버전의 양쪽이 있는 경우는 파일 시스템상의 매니페스트가 우선됩니다.

5.2 컴포넌트 매니페스트로 COM 정보를 쓴다

다음으로 COM 쪽은 원래 레지스트리에 들어 있던 정보를 컴포넌트 매니페스트에 가집니다.

여기에는 예를 들어 다음 같은 정보가 들어갑니다.

  • comClass
  • clsid
  • progid
  • threadingModel
  • typelib
  • 필요하다면 proxy / stub이나 window class 등

즉 레지스트리 대신 XML로 COM의 외형을 기술하는 이미지입니다.

이 매니페스트는,

  • DLL과 별도 파일로 둔다
  • DLL에 리소스로서 매립한다

중 어느 쪽으로도 구성할 수 있습니다.

실무에서는 private assembly로서 DLL에 매립하는 쪽이 사고 나기 어려운 경우가 많습니다. 별도 파일 운영은 이해하기 쉬운 반면, 파일명과 assemblyIdentity의 대응, 배치 장소, 복사 누락에서 발을 걸기 쉽기 때문입니다.

5.3 실행 시에는 액티베이션 컨텍스트가 먼저 보인다

Reg-Free COM의 핵심은 여기입니다.

앱이 CLSIDFromProgIDCoCreateInstance를 호출하면 COM 런타임은 액티브한 액티베이션 컨텍스트를 봅니다. 거기에 필요한 ProgID → CLSID, CLSID → DLL의 정보가 있으면 레지스트리를 쓰지 않고 해결할 수 있습니다.

반대로 필요한 정보가 매니페스트에 부족하면 통상의 등록 기반 해결로 떨어집니다. 이 동작 때문에 개발기에서는 우연히 동작한다는 덫이 생깁니다. Reg-Free로 할 수 있었다고 생각했는데 실제로는 로컬 등록에 도움받고 있는 것, 바로 그것입니다.

여기가 Reg-Free COM의 가장 짜증나는 함정입니다.

6. 무엇이 좋은가

Reg-Free COM의 이점은 실무에서는 꽤 분명합니다.

6.1 XCOPY 배포가 쉽다

앱 폴더에 필요한 파일을 모아 둘 수 있으므로 인스톨러나 등록 처리가 가벼워집니다. 물론 Program Files 아래에 쓴다면 권한은 별개 이야기지만, 적어도 COM 등록을 위한 관리자 작업은 줄이기 쉽습니다.

6.2 버전 충돌을 줄이기 쉽다

같은 머신 위에 복수 버전의 COM 부품이 있어도 앱마다 쓸 버전을 나누기 쉬워집니다. 다른 제품의 셋업으로 갑자기 동작이 바뀌었다는 사고를 꽤 피하기 쉬워집니다.

6.3 기존 코드를 크게 바꾸지 않아도 되는 경우가 많다

Reg-Free COM은 기존 코드의 호출 방식을 근본부터 바꾸기보다 해결하는 방식을 바꾸는 구조입니다. 그래서 잘 맞으면 CoCreateInstance 쪽 코드를 거의 건드리지 않고 도입할 수 있습니다.

6.4 삭제와 롤백이 편해진다

앱 단위로 갇혀 있으므로 갱신이나 롤백이 꽤 솔직해집니다. 극단적으로 말하면 폴더째 교체하는 발상이 취하기 쉬워집니다.

7. 맞는 장면·맞지 않는 장면

7.1 맞는 장면

다음 같은 경우에는 Reg-Free COM이 꽤 유력합니다.

상황 상성
앱 전용의 COM DLL / OCX를 동봉하고 싶다 매우 좋음
같은 PC에 복수 버전을 공존시키고 싶다 매우 좋음
벤더 부품의 등록 사고를 피하고 싶다 좋음
ActiveX / OCX를 기존 데스크톱 앱에서 private로 쓰고 싶다 좋음
배포를 가볍게 하면서 기존 호출은 크게 바꾸고 싶지 않다 좋음

전형적으로는 업무 데스크톱 앱, 장치 연계 도구, VB6 / MFC / WinForms의 기존 자산과의 상성이 좋습니다.

7.2 맞지 않거나 신중하게 봐야 할 장면

한편으로 다음 같은 경우는 신중하게 보는 편이 좋습니다.

상황 코멘트
COM을 머신 전체에서 공유하고 싶다 Reg-Free의 우마미가 옅다
bitness가 맞물려 있지 않다 Reg-Free로는 해결되지 않는다
비표준적인 등록 정보나 독자 셋업에 강하게 의존 매니페스트화하기 어렵다
의존 DLL이나 VC++ 런타임의 배포가 정리되지 않았다 결국 다른 곳에서 걸린다
설계 시 도구나 IDE의 참조 설정이 레지스트리 전제 별도의 운영 설계가 필요

특히 마지막 점은 중요합니다. Reg-Free COM은 실행 시의 액티베이션을 돕지만, 설계 시의 참조 설정 UI가 무엇을 전제로 하고 있는가까지 한 방에 바꿔 주지는 않습니다.

8. 흔한 오해

8.1 Reg-Free COM이면 bitness 문제는 사라진다

사라지지 않습니다. 32bit 프로세스에는 32bit의 in-proc COM DLL밖에 로드할 수 없고, 64bit 프로세스에는 64bit DLL밖에 들어가지 않습니다. 여기는 Reg-Free에서도 종래대로입니다.

8.2 Reg-Free COM이면 레지스트리를 일절 보지 않는다

이것도 다릅니다. 매니페스트에 필요 정보가 부족하면 통상의 등록 기반 해결로 떨어집니다. 그래서 개발기에서 성공 = Reg-Free 구성이 올바르다고는 할 수 없습니다.

8.3 Reg-Free COM이면 형 라이브러리 이야기도 자동으로 정리된다

여기는 반만 옳습니다. 매니페스트에는 typelib 정보도 쓸 수 있지만, VBA의 참조 설정, C++의 #import, .NET 쪽의 설계 시 참조 생성 등 형 정보의 취급은 별도로 설계가 필요한 경우가 평범하게 있습니다.

Reg-Free COM은 우선 기동할 수 있게 하는 이야기입니다. 어떻게 형 있게 개발할까는 그 다음의 논점입니다.

8.4 Reg-Free COM이면 어떤 ActiveX / OCX라도 그대로 된다

여기도 위험합니다. 컴포넌트가 표준적인 COM 등록 정보에 타고 있다면 진행하기 쉽지만, 독자 레지스트리 설정, 추가 셋업, 라이선스 처리, 다른 모듈군에의 의존이 짙으면 Reg-Free화는 갑자기 까다로워집니다.

8.5 Reg-Free COM과 .NET Framework / .NET 8은 대체로 같다

비슷한 부분은 있지만 툴체인은 꽤 다릅니다. .NET Framework + RegAsm의 문맥과 .NET 5+ / .NET 8 + comhost의 문맥은 같은 COM이어도 발판이 별개입니다.

9. 네이티브 / .NET Framework / .NET 5+ / .NET 8의 차이

여기는 섞이기 쉬우므로 한 번 나누어 봅니다.

계통 대충 정리
네이티브 COM DLL / OCX 애플리케이션 매니페스트 + 컴포넌트 매니페스트로 생각하는 것이 기본
.NET Framework 기반의 COM 상호 운용 Win32 스타일의 애플리케이션 매니페스트에 더해 managed 컴포넌트 쪽의 매니페스트도 필요
.NET 5+ / .NET 8에서 COM 공개 EnableComHosting으로 COM host를 만들고, EnableRegFreeCom으로 Reg-Free용 manifest를 생성할 수 있다

9.1 .NET Framework 기반의 COM

.NET Framework 기반의 COM에서는 COM 앱 쪽의 Win32 스타일 application manifestmanaged 컴포넌트 쪽의 component manifest의 2단 구성이 됩니다.

이쯤은 네이티브 COM보다 조금 까다롭습니다. 「Reg-Free COM은 알았지만 managed component가 얽히면 갑자기 manifest가 한 장 더 늘어난다」는 지점에서 대화의 노면이 또 조금 진흙탕이 됩니다.

9.2 .NET 5+ / .NET 8에서의 COM 공개

.NET 5+ / .NET 8에서는 COM 공개의 입구가 *.comhost.dll가 됩니다. 나아가 EnableRegFreeCom=true를 넣으면 Reg-Free COM용의 side-by-side manifest가 출력됩니다.

다만 여기서도 중요한 것은 Reg-Free COM과 TLB 전략은 별개라는 점입니다. .NET Core / .NET 5+에서는 .NET Framework 때처럼 어셈블리에서 TLB가 자연스럽게 나오는 세계가 아닙니다. 형 있는 이용이 필요하다면 TLB의 생성·매립·등록의 이야기를 별도로 정리하는 편이 안전합니다.

10. 최소 구성 이미지

여기서는 MyApp.exeVendor.CameraControl.dll을 Reg-Free COM으로 쓰는 최소 이미지를 보입니다.

10.1 파일 구성 이미지

MyApp.exe
MyApp.exe.manifest
Vendor.CameraControl.dll
Vendor.Helper.dll

위 예에서는 컴포넌트 매니페스트는 DLL 쪽에 매립하고 있다는 상정입니다. 별도 파일로 해도 되지만 우선은 매립 쪽이 정리하기 쉽습니다.

10.2 애플리케이션 매니페스트의 이미지

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
    type="win32"
    name="KomuraSoft.MyApp"
    version="1.0.0.0"
    processorArchitecture="amd64" />

  <dependency>
    <dependentAssembly>
      <assemblyIdentity
        type="win32"
        name="Vendor.CameraControl.Asm"
        version="1.0.0.0"
        processorArchitecture="amd64" />
    </dependentAssembly>
  </dependency>
</assembly>

10.3 컴포넌트 매니페스트의 이미지

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
  <assemblyIdentity
    type="win32"
    name="Vendor.CameraControl.Asm"
    version="1.0.0.0"
    processorArchitecture="amd64" />

  <file name="Vendor.CameraControl.dll">
    <comClass
      clsid="{01234567-89AB-CDEF-0123-456789ABCDEF}"
      progid="Vendor.CameraControl.1"
      threadingModel="Apartment"
      tlbid="{89ABCDEF-0123-4567-89AB-CDEF01234567}" />

    <typelib
      tlbid="{89ABCDEF-0123-4567-89AB-CDEF01234567}"
      version="1.0"
      helpdir="" />
  </file>
</assembly>

이 예에서 정말 중요한 것은 XML의 세부보다 앱 쪽의 dependentAssembly와 컴포넌트 쪽의 assemblyIdentity가 일치하는 것입니다. 여기가 어긋나면 꽤 조용히 고생합니다.

덧붙여 위의 GUID나 이름은 설명용 예입니다. 실제로는 컴포넌트가 공개하고 있는 CLSID / TLBID / ProgID / threading model에 맞춰 올바르게 써야 합니다.

11. 빠지기 쉬운 곳

11.1 개발기에서는 동작하는데 배포처에서는 동작하지 않는다

우선 의심해야 할 것은 실은 레지스트리 등록에 도움받고 있었던 패턴입니다. Reg-Free COM의 검증은 가능하다면 클린 환경에서 하는 편이 안전합니다.

11.2 「side-by-side configuration is incorrect」로 기동되지 않는다

이 계열은 manifest의 부정합, 의존 DLL의 부족, VC++ 런타임 부족, 아키텍처 차이 등으로 일어납니다. 표면의 에러문만으로는 꽤 불친절하므로 이벤트 로그sxstrace로 쫓는 것이 정석입니다.

11.3 component manifest와 application manifest의 대응이 어긋난다

  • name이 다르다
  • version이 다르다
  • processorArchitecture가 다르다
  • 복사한 셈이었던 manifest가 오래됐다

이쯤은 겉보기로는 아주 작은 차이지만 기동 시에는 꽤 크게 작용합니다.

11.4 의존 DLL을 두고 오는 것을 잊는다

Vendor.CameraControl.dll만 보고 만족하면 그 앞에서 읽히는 Vendor.Helper.dll, VC++ 런타임, proxy / stub DLL이 빠집니다. Reg-Free COM은 COM 등록의 문제를 줄이지만 네이티브 의존 해결의 문제까지 없애 주지는 않습니다.

11.5 type library나 참조 설정의 운영을 뒤로 미룬다

런타임 activation만 통과시켜도,

  • VBA에서 조기 바인딩하고 싶다
  • C++에서 #import하고 싶다
  • .NET 쪽에서 설계 시에 interop을 만들고 싶다

가 되면 형 정보를 나누어 주는 방식이 필요합니다. Reg-Free COM은 여기를 자동으로 전부 정리해 주지는 않으므로 runtime과 design-time을 나누어 생각하는 것이 중요합니다.

12. 정리

Reg-Free COM을 한마디로 말하면 COM의 등록 정보를 머신 전체에서 앱 단위로 치우치는 구조입니다.

그래서,

  • COM DLL / OCX를 앱 로컬에 가두기 쉽다
  • 버전 충돌을 줄이기 쉽다
  • 배포나 롤백을 단순화하기 쉽다

는 이점이 있습니다.

한편으로,

  • 32bit / 64bit
  • 의존 DLL
  • TLB / 참조 설정
  • 비표준적인 등록 의존
  • 클린 환경에서의 검증

는 여전히 중요합니다.

그래서 Reg-Free COM을 도입할 때의 기본 자세는 이렇습니다.

  1. 이것은 activation의 이야기다라고 단정한다
  2. runtime과 design-time의 논점을 나눈다
  3. 클린 환경에서 확인한다
  4. bitness와 의존 DLL을 먼저 갖춘다

이 순서로 보면 꽤 사고 나기 어려워집니다.

13. 관련 기사

14. 참고 자료

관련 기사

같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.

관련 토픽

이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.

ActiveX 이관

COM / ActiveX / OCX 자산을 유지할지, 감쌀지, 교체할지의 단계적 판단을 정리한 토픽 페이지입니다.

토픽 보기

이 주제와 연결되는 서비스

이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.

블로그 목록으로 돌아가기