Reg-Free COM이란 - 등록 없이 COM을 쓰는 구조
· 업데이트: · Go Komura · COM, Reg-Free COM, Registration-Free COM, Windows 개발, 레거시 기술
수정 이력(8건, 최종 수정 2026년 09월 03일)
이 글에 적용한 변경 사항의 기록입니다. 보관해 둔 수정 전 버전은 DOI가 부여된 고정 URL에서 읽을 수 있습니다.
- 관련 기사 링크를 한국어 permalink에 맞추는 등 CI가 지적한 표시용 수정을 반영했습니다. 본문의 기술적인 주장은 바꾸지 않았습니다.
- permalink·저자 표기·지식 맵 래퍼·깨진 내부 링크 등 CI가 지적한 표시용 수정을 반영했습니다. 본문의 기술적인 주장은 바꾸지 않았습니다.
- 일본어 원문의 완역으로 다시 번역했습니다. 기존 한국어판은 원문의 일부만 옮긴 축약본이라 절, 표, Mermaid 그림, 그림 설명, FAQ가 빠져 있었습니다. 일본어 원문에 맞춰 이들을 모두 복원했고, 기술적인 주장은 일본어판과 같습니다. 수정 전 버전 보기 (DOI: 10.5281/zenodo.21635177)
- 구조의 해결 순서나 구성·검증 절차를 그림으로도 따라갈 수 있도록 Mermaid 그림을 16점 추가했습니다(본문 500〜750자당 1그림 규약에 맞춘 것입니다). 본문 문장은 바꾸지 않았습니다.
- 글 맨 앞에 「이 글의 지식 맵」 절을 추가했습니다. 본문에서 다루는 개념과 그 관계를 요약·그림·상세 페이지 링크로 정리한 것입니다. 본문의 주장은 바꾸지 않았습니다.
- 외부 리뷰(1283건) 대응으로 본문을 갱신했습니다. 개별 변경 내용은 아래 이력을 참조하세요.
- 매니페스트 배치와 포함, 클린 환경에서의 확인 절차를 새로 넣었습니다. 아울러 기존 예가 「DLL에 포함하는 가정」이라고 쓰면서 assembly 이름이 DLL 이름과 다른 별도 파일 배치 방식이었던 불일치를 고치고, 포함할 때의 명명 규칙(assembly 이름=DLL 이름, 리소스 ID는 1)을 표로 보였습니다. `sxstrace` 절차와 용어 정의표도 추가하고, 비유로 적었던 5곳을 구체적인 서술로 고쳤습니다.
- 본문 중 관련 글 링크 문구가 링크 대상의 현재 제목과 어긋나 있던 것을 실제 제목에 맞췄습니다. 본문 내용은 바꾸지 않았습니다.
- 최초 공개
이 글을 인용하기(DOI: 10.5281/zenodo.21635176)
이 글은 Zenodo에 보관되어 있습니다. 항상 최신 버전으로 연결되는 DOI와 지금 보고 있는 버전에 고정된 DOI를 아래에 함께 제시합니다.
Go Komura (2026). 「Reg-Free COM이란 - 등록 없이 COM을 쓰는 구조」. 합동회사 코무라소프트. https://doi.org/10.5281/zenodo.21635176 https://comcomponent.com/ko/blog/what-is-reg-free-com/
- DOI(최신 버전)
- 10.5281/zenodo.21635176
- DOI(이 버전)
- 10.5281/zenodo.22217457
COM / ActiveX / OCX 안건에서는 배포와 갱신마다 같은 문제가 반복됩니다.
regsvr32가 필요함- 관리자 권한이 필요해지기 쉬움
- 다른 앱이 넣은 다른 버전과 충돌함
- 제거 후에 다른 제품까지 말려듦
- 개발 PC에서는 동작하는데 클린 환경에서는 동작하지 않음
이 수고를 꽤 줄일 수 있는 것이 Reg-Free COM입니다. 다만 이름에 비해 「COM의 번거로움이 전부 사라지는 마법」은 아닙니다. 사라지는 것은 주로 글로벌 등록에 끌려가는 번거로움입니다. bitness, 의존 DLL, 형식 라이브러리, 스레드 모델의 어려움까지는 사라지지 않습니다.
flowchart TB
accTitle: Reg-Free COM에서 사라지는 것과 남는 것
accDescr: Reg-Free COM에서 사라지는 것은 주로 글로벌 등록에 끌려가는 번거로움이며, bitness나 의존 DLL, 형식 라이브러리, 스레드 모델의 어려움은 사라지지 않음을 나타내는 그림.
rf1["Reg-Free COM"] --> rf2["글로벌 등록의 번거로움은 줄어듦"]
rf1 --> rf3["남는 어려움도 있다"]
rf3 -.-> rf4["bitness / 의존 DLL / 형식 라이브러리"]
그림 1: 마법이 아니라, 사라지는 번거로움과 남는 번거로움을 나눠서 기대한다.
이 글에서는 Reg-Free COM을 Windows 데스크톱 앱에서 COM DLL / OCX를 앱 로컬에 가둬 쓰는 맥락을 중심으로 정리합니다.
대상 독자와 전제
기존 COM DLL / OCX를 쓰는 Windows 데스크톱 앱을 배포하고 있고, regsvr32와 관리자 권한에서 벗어나고 싶은 개발자를 대상으로 씁니다. COM의 기초(CLSID, ProgID, CoCreateInstance, in-proc 서버)를 한 번은 만져 본 전제입니다. 아직 그 부분이 흐릿하다면, 먼저 「COM / ActiveX / OCX란 무엇인가 - 차이와 관계를 정리해 해설」을 읽고 오는 편이 빠릅니다.
절차 부분은 Windows SDK의 mt.exe와 sxstrace를 쓰므로, Visual Studio 또는 Windows SDK가 들어 있는 환경을 가정합니다.
1. 먼저 결론(한 마디로)
먼저 거칠지만 도움이 되는 표현으로 말하면 이렇습니다.
- Reg-Free COM은 COM의 등록 정보를 레지스트리가 아니라 매니페스트로 가지는 방식입니다
- 실행 시에는
CoCreateInstance나CLSIDFromProgID의 해결에서 액티베이션 컨텍스트가 먼저 보입니다 - 그래서 COM DLL / OCX를 앱마다 private로 둘 수 있게 됩니다
- 주된 이점은 XCOPY 배포가 쉬운 것, 버전 충돌을 피하기 쉬운 것, 제거가 잘 깨지지 않는 것입니다
- 다만 32bit / 64bit 문제는 사라지지 않습니다. 이 점은 매니페스트 작성법으로는 피할 수 없습니다
- 또한 의존 DLL, 형식 라이브러리, 설계 시 참조, 비표준 등록 의존은 따로 생각해야 합니다
- 실무에서는 앱 전용 COM 부품을 옆에 두고 싶을 때 꽤 잘 맞습니다
요컨대 Reg-Free COM은 COM의 액티베이션을 앱 단위로 되돌리는 구조입니다.
flowchart TB
accTitle: 등록 정보의 자리가 바뀐다
accDescr: Reg-Free COM은 COM의 등록 정보를 레지스트리가 아니라 매니페스트로 가지며, CoCreateInstance 등의 해결에서 액티베이션 컨텍스트가 먼저 보이므로 COM 부품을 앱마다 private로 둘 수 있음을 나타내는 그림.
mg1["등록 정보를 매니페스트로 가진다"] --> mg2["해결 시 액티베이션 컨텍스트가 먼저"]
mg2 --> mg3["COM 부품을 앱마다 private로 둘 수 있다"]
mg3 -.-> mg4["XCOPY 배포나 충돌 회피가 쉽다"]
그림 2: 레지스트리에서 매니페스트로, 해결의 입구가 바뀌는 것이 본질이다.
이 글의 지식 맵
Reg-Free COM은 COM의 등록 정보를 레지스트리가 아니라 매니페스트로 가지게 하고, 실행 시에는 액티베이션 컨텍스트를 먼저 보고 DLL을 해결하는 구조입니다. 애플리케이션 매니페스트가 의존 대상인 side-by-side assembly를 선언하고, 컴포넌트 매니페스트가 CLSID나 ProgID 등 원래 레지스트리에 있던 정보를 가진다는 분담으로, mt.exe로 매립하고 sxstrace로 해결 실패를 추적합니다. 다만 32bit/64bit의 일치 요건은 해소되지 않고 의존 DLL이나 타입 라이브러리의 처리는 별개의 논점으로 남으므로, VB6·MFC·WinForms 같은 기존 데스크톱 자산의 앱 로컬화에는 맞는 반면, 머신 전체에서의 COM 공유가 필요한 장면에는 맞지 않습니다. .NET 5+/.NET 8에서는 EnableComHosting으로 comhost를 만들고, EnableRegFreeCom을 활성화함으로써 Reg-Free COM용 side-by-side 매니페스트를 출력할 수 있습니다.
flowchart LR
accTitle: Reg-Free COM
accDescr: Reg-Free COM이 액티베이션 컨텍스트와 side-by-side assembly의 구조로 COM의 등록 정보를 앱 단위로 가지게 한다는 것, 애플리케이션 매니페스트와 컴포넌트 매니페스트의 역할 분담, mt.exe에 의한 구성과 sxstrace에 의한 장애 조사, .NET 5+의 comhost와의 관계를 보여주는 그림
reg_free_com["Reg-Free COM(등록 불필요 COM)"]
activation_context["활성화 컨텍스트"]
win32_application_manifest["애플리케이션 매니페스트(Win32 side-by-side)"]
component_manifest["컴포넌트 매니페스트(assembly manifest)"]
side_by_side_assembly["side-by-side assembly"]
bitness_match_requirement["비트수 일치 요건"]
regsvr32["regsvr32"]
type_library["타입 라이브러리(TLB)"]
mt_exe["mt.exe(매니페스트 도구)"]
sxstrace["sxstrace"]
assembly_searching_sequence["private assembly 탐색 순서"]
dotnet[".NET(Core 이후)"]
comhost["COM host(*.comhost.dll)"]
enable_com_hosting["EnableComHosting"]
enable_regfreecom["EnableRegFreeCom"]
vb6["Visual Basic 6.0(VB6)"]
clsid["CLSID(Class ID)"]
progid["ProgID(Programmatic Identifier)"]
com["COM(컴포넌트 오브젝트 모델)"]
activex["ActiveX"]
ocx["OCX"]
mfc["MFC(Microsoft Foundation Classes)"]
windows_forms["Windows Forms"]
machine_wide_com_sharing["COM의 시스템 전역 공유"]
reg_free_com -->|"이용한다"| activation_context
reg_free_com -->|"이용한다"| win32_application_manifest
reg_free_com -->|"이용한다"| component_manifest
win32_application_manifest -->|"전제로 한다"| side_by_side_assembly
component_manifest -->|"구현을 담당한다"| side_by_side_assembly
reg_free_com -->|"전제로 한다"| bitness_match_requirement
reg_free_com -.->|"의 후속"| regsvr32
component_manifest -.->|"이용한다"| type_library
reg_free_com -->|"에서 구성할 수 있다"| mt_exe
reg_free_com -->|"에서 확인할 수 있다"| sxstrace
component_manifest -->|"에서 구성할 수 있다"| mt_exe
reg_free_com -->|"전제로 한다"| assembly_searching_sequence
dotnet -.->|"이용한다"| comhost
comhost -->|"전제로 한다"| enable_com_hosting
comhost -->|"에서 구성할 수 있다"| enable_regfreecom
enable_regfreecom -->|"구현을 담당한다"| reg_free_com
vb6 -.->|"이용한다"| type_library
component_manifest -->|"이용한다"| clsid
component_manifest -.->|"이용한다"| progid
reg_free_com -->|"이용한다"| com
reg_free_com -->|"이용한다"| activex
reg_free_com -->|"이용한다"| ocx
reg_free_com -->|"권장되는 대응"| vb6
reg_free_com -->|"권장되는 대응"| mfc
reg_free_com -->|"권장되는 대응"| windows_forms
reg_free_com -->|"사용은 비권장"| machine_wide_com_sharing
그림의 실선은 항상 성립하는 관계, 점선은 조건이 붙는 관계입니다(성립 조건은 상세 페이지의 관계별 설명에 적혀 있습니다). 관계 전체 목록(총 26건, 근거와 확신도 포함)과 주요 개념의 정의는 지식 맵 상세 페이지에 정리되어 있습니다(일본어). 데이터: JSON-LD / Turtle
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를 쓴 공개
반대로 이 글에서 강조하고 싶은 것은 두 가지입니다.
- Reg-Free COM은 「액티베이션」의 이야기이다
- 형식 정보의 배포나 설계 시 참조 설정은, 별도의 논점으로 남는 경우가 있다
여기를 섞으면 이야기가 꽤 흐려집니다.
flowchart TB
accTitle: 섞으면 안 되는 두 논점
accDescr: Reg-Free COM은 액티베이션의 이야기이며, 형식 정보의 배포나 설계 시 참조 설정은 별도의 논점으로 남는 경우가 있으므로 이 둘을 섞으면 이야기가 흐려짐을 나타내는 그림.
pt1["Reg-Free COM"] --> pt2["액티베이션의 이야기"]
pt3["형식 정보의 배포·설계 시 참조"] --> pt4["별도의 논점으로 남는다"]
pt2 -.-> pt5["섞으면 이야기가 흐려진다"]
pt4 -.-> pt5
그림 3: 실행 시의 이야기와 설계 시의 이야기는, 처음부터 나눠서 다룬다.
3. 먼저 한 장으로 정리
그림에 들어가기 전에, 이 글에서 반복해서 나오는 4개 용어만 먼저 정의해 둡니다.
| 용어 | 의미 |
|---|---|
| 액티베이션 컨텍스트 (activation context) | 「지금 이 스레드는 어느 assembly의 어느 버전을 쓸지」를 유지하는, 실행 시의 데이터 구조입니다. CoCreateInstance는 레지스트리보다 여기를 먼저 봅니다 |
| side-by-side assembly | 같은 이름의 부품을 버전을 달리해 같은 머신에 공존시키기 위한 Windows 구조입니다. 매니페스트로 식별되며, assemblyIdentity가 이름표에 해당합니다 |
| 애플리케이션 매니페스트 | EXE 쪽에 붙이는 XML로, 「자신은 어느 side-by-side assembly에 의존하는지」를 적습니다 |
| 컴포넌트 매니페스트 (assembly manifest) | 부품 쪽에 붙이는 XML로, 「이 assembly는 어떤 파일을 포함하고, 어떤 COM 클래스를 공개하는지」를 적습니다. 원래 레지스트리에 들어가 있던 정보가 여기로 옵니다 |
액티베이션 컨텍스트는 스레드마다 가집니다. COM은 생성 측 스레드의 액티베이션 컨텍스트를 호스트 측 스레드로 넘긴 뒤에 LoadLibrary나 DllGetClassObject를 호출하므로, 호출 측에서 특별한 준비는 필요 없습니다.
그다음에 전체 그림을 한 장으로 보는 편이 빠릅니다.
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
그림 4: 앱의 매니페스트에서 컴포넌트의 매니페스트를 따라가, 레지스트리 없이 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을 깨뜨림
- 개발 PC에 우연히 들어 있는 등록이 프로덕션 머신에는 없음
- 32bit와 64bit 등록이 맞지 않아, 증상만 이상하게 어긋남
즉 COM 본체보다 배포 모델 쪽이 사람을 더 괴롭히는 경우가 꽤 많습니다. Reg-Free COM은 이 배포 모델의 고통을 줄이기 위한 구조입니다.
flowchart TB
accTitle: 글로벌 공유가 역효과를 내는 흐름
accDescr: COM의 등록 정보가 레지스트리에 들어가면 머신 전체에서 공유하기 쉬워지지만, 실무에서는 다른 제품에 의한 덮어쓰기나 제거 시의 말려듦, 개발 PC와 프로덕션 머신의 환경 차로 역효과를 내기 쉽고, COM 본체보다 배포 모델이 사람을 괴롭힘을 나타내는 그림.
gs1["등록 정보를 머신 전체에서 공유"] --> gs2["여러 앱에서 쓸 수 있어 편리"]
gs1 --> gs3["실무에서는 공유가 역효과를 냄"]
gs3 -.-> gs4["덮어쓰기 / 말려듦 / 환경 차"]
gs3 --> gs5["배포 모델이 사람을 괴롭힘"]
그림 5: 고통의 정체는 COM 자체가 아니라, 글로벌 등록이라는 전제이다.
5. Reg-Free COM의 구조
5.1 애플리케이션 매니페스트에 의존 관계를 적는다
먼저 앱 쪽은 자신이 어느 side-by-side assembly에 의존하는지를 애플리케이션 매니페스트에 적습니다.
이 매니페스트는
MyApp.exe.manifest처럼 EXE 옆에 두기- EXE에 리소스로 포함하기
어느 쪽으로도 다룰 수 있습니다. 실무에서는 배포와 교체를 보기 쉽게 하려면 외부 파일, 깨지기 어려움과 배포의 단순함을 우선하면 포함, 이렇게 나누는 경우가 많습니다.
참고로 외부 파일 쪽과 포함 쪽이 둘 다 있으면 파일 시스템의 매니페스트가 우선됩니다.
5.2 컴포넌트 매니페스트에 COM 정보를 적는다
다음으로 COM 쪽은 원래 레지스트리에 들어가 있던 정보를 컴포넌트 매니페스트에 가집니다.
여기에 들어가는 것은 예를 들어 이런 정보입니다.
comClassclsidprogidthreadingModeltypelib- 필요하면 proxy / stub이나 window class 등
즉 레지스트리 대신 XML로 COM의 겉모습을 기술하는 이미지입니다.
이 매니페스트는
- DLL과 별도 파일로 두기
- DLL에 리소스로 포함하기
어느 쪽으로도 구성할 수 있습니다.
실무에서는 private assembly로 DLL에 포함하는 편이 사고를 줄이는 경우가 많습니다. 별도 파일 운용은 알기 쉬운 반면, 파일 이름과 assemblyIdentity의 대응, 배치 위치, 복사 누락에 발목이 잡히기 쉽기 때문입니다.
flowchart TB
accTitle: 컴포넌트 매니페스트를 두는 방법
accDescr: 원래 레지스트리에 들어가 있던 comClass나 clsid, typelib 등의 정보를 컴포넌트 매니페스트가 XML로 가지며, DLL과 별도 파일로 둘지 DLL에 리소스로 포함할지를 고를 수 있고, 실무에서는 포함이 사고를 줄이기 쉬움을 나타내는 그림.
cm1["레지스트리에 있던 정보를 XML로 가진다"] --> cm2["별도 파일로 둔다"]
cm1 --> cm3["DLL에 포함한다"]
cm2 -.-> cm4["복사 누락 등으로 발목이 잡히기 쉽다"]
cm3 -.-> cm5["실무에서는 사고를 줄이는 경우가 많다"]
그림 6: 정보의 내용은 같아도, 두는 방식의 선택이 사고율을 좌우한다.
5.3 실행 시에는 액티베이션 컨텍스트가 먼저 보인다
Reg-Free COM의 핵심은 여기입니다.
앱이 CLSIDFromProgID나 CoCreateInstance를 호출하면, COM 런타임은 활성 액티베이션 컨텍스트를 봅니다.
거기에 필요한 ProgID → CLSID, CLSID → DLL 정보가 있으면 레지스트리 없이 해결할 수 있습니다.
반대로 필요한 정보가 매니페스트에 부족하면 통상의 등록 기반 해결로 떨어집니다. 이 동작 때문에 개발 PC에서는 우연히 동작한다는 함정이 생깁니다. Reg-Free로 된 줄 알았는데 실제로는 로컬 등록에 기대고 있던 경우입니다.
여기가 Reg-Free COM에서 가장 성가신 함정입니다.
flowchart TB
accTitle: 등록 기반 해결로 떨어지는 함정
accDescr: CoCreateInstance의 해결에서는 액티베이션 컨텍스트가 먼저 보이지만, 매니페스트에 필요한 정보가 부족하면 통상의 등록 기반 해결로 떨어지므로, 개발 PC에서는 로컬 등록에 기대어 우연히 동작한다는 함정이 생김을 나타내는 그림.
ac1["해결은 액티베이션 컨텍스트가 먼저"] --> ac2{"매니페스트에 정보가 충분한가"}
ac2 -->|"충분하다"| ac3["레지스트리 없이 해결"]
ac2 -->|"부족하다"| ac4["등록 기반 해결로 떨어진다"]
ac4 -.-> ac5["개발 PC에서는 우연히 동작하는 함정"]
그림 7: 조용히 떨어지는 폴백이, 이 구조에서 가장 큰 함정이다.
6. 무엇이 좋은가
Reg-Free COM의 이점은 실무에서 꽤 분명합니다.
6.1 XCOPY 배포가 쉽다
앱 폴더에 필요한 파일을 모아 둘 수 있어, 설치 프로그램이나 등록 처리가 가벼워집니다.
물론 Program Files 아래에 쓰면 권한은 다른 이야기지만, 적어도 COM 등록을 위한 관리자 작업은 줄이기 쉽습니다.
6.2 버전 충돌을 줄이기 쉽다
같은 머신에 COM 부품의 여러 버전이 있어도, 앱마다 쓰는 판을 나누기 쉬워집니다.
다른 제품의 설치로 갑자기 동작이 바뀌었다는 사고를 꽤 피하기 쉬워집니다.
6.3 기존 코드를 크게 바꾸지 않아도 되는 경우가 많다
Reg-Free COM은 기존 코드의 호출 방식을 근본부터 바꾸기보다 해결 방식을 바꾸는 구조입니다.
그래서 잘 맞으면 CoCreateInstance 쪽 코드를 거의 건드리지 않고 도입할 수 있습니다.
6.4 삭제와 롤백이 편해진다
앱 단위로 가둬 있으므로 갱신과 롤백이 꽤 단순해집니다. 극단적으로 말하면 폴더째 교체하는 발상을 취하기 쉬워집니다.
flowchart TB
accTitle: 앱 단위로 가두는 이점
accDescr: COM 부품을 앱 단위로 가두면 XCOPY 배포의 쉬움, 버전 충돌 회피의 쉬움, 기존 코드를 거의 바꾸지 않는 도입, 삭제와 롤백의 단순함이라는 이점이 생김을 나타내는 그림.
cl1["앱 단위로 가둔다"] --> cl2["XCOPY 배포가 쉽다"]
cl1 --> cl3["버전 충돌을 줄일 수 있다"]
cl2 -.-> cl5["폴더째 교체할 수 있다"]
cl3 -.-> cl4["호출 코드는 거의 건드리지 않는다"]
그림 8: 이점은 모두, 가뒀다는 하나의 성질에서 나온다.
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가 무엇을 전제로 하는지까지 한 번에 바꿔 주지는 않습니다.
flowchart TB
accTitle: 실행 시는 도움이 되지만 설계 시는 남는다
accDescr: Reg-Free COM은 실행 시 액티베이션을 돕지만, 설계 시 도구나 IDE의 참조 설정이 레지스트리 전제인 경우에는 그대로는 바뀌지 않아 별도의 운용 설계가 필요함을 나타내는 그림.
rt1["실행 시의 액티베이션"] --> rt2["Reg-Free가 도와 준다"]
dt1["설계 시의 참조 설정 UI"] --> dt2["레지스트리 전제인 경우가 있다"]
dt2 -.-> dt3["별도의 운용 설계가 필요"]
그림 9: 움직이는 구조가 갖춰져도, 개발하는 구조는 따로 갖춘다.
8. 흔한 오해
8.1 Reg-Free COM이면 bitness 문제는 사라진다
사라지지 않습니다. 32bit 프로세스에는 32bit in-proc COM DLL만 로드할 수 있고, 64bit 프로세스에는 64bit DLL만 들어갑니다. 이 점은 Reg-Free에서도 예전과 같습니다.
8.2 Reg-Free COM이면 레지스트리를 전혀 보지 않는다
이것도 틀립니다. 매니페스트에 필요 정보가 부족하면 통상의 등록 기반 해결로 떨어집니다. 그래서 개발 PC에서 성공 = Reg-Free 구성이 올바르다고 할 수는 없습니다.
8.3 Reg-Free COM이면 형식 라이브러리 이야기도 자동으로 정리된다
여기는 반만 맞습니다.
매니페스트에 typelib 정보도 쓸 수 있지만, VBA의 참조 설정, C++의 #import, .NET 쪽의 설계 시 참조 생성처럼 형식 정보의 취급은 따로 설계가 필요한 경우가 흔합니다.
Reg-Free COM은 우선 기동할 수 있게 하는 이야기입니다. 형식을 붙여 어떻게 개발할지는 그다음 논점입니다.
flowchart TB
accTitle: 기동 이야기와 형식 이야기의 순서
accDescr: 매니페스트에 typelib 정보도 쓸 수 있지만, VBA의 참조 설정이나 C++의 import, .NET의 설계 시 참조 생성 등 형식 정보의 취급은 따로 설계가 필요하며, Reg-Free COM은 우선 기동할 수 있게 하는 이야기이고 형식이 있는 개발은 그다음 논점임을 나타내는 그림.
st1["Reg-Free COM을 구성한다"] --> st2["우선 기동할 수 있게 한다"]
st2 --> st3["다음에 형식을 붙여 어떻게 개발할지"]
st3 -.-> st4["VBA 참조 설정 / import / interop 생성"]
그림 10: typelib를 쓸 수 있는 것과, 형식 정보 운용이 끝난 것은 별개이다.
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 스타일 애플리케이션 매니페스트에 더해, 매니지드 컴포넌트 쪽 매니페스트도 필요 |
| .NET 5+ / .NET 8에서 COM 공개 | EnableComHosting으로 COM host를 만들고, EnableRegFreeCom으로 Reg-Free용 manifest를 생성할 수 있다 |
9.1 .NET Framework 기반 COM
.NET Framework 기반 COM에서는 COM 앱 쪽의 Win32 스타일 application manifest와 매니지드 컴포넌트 쪽의 component manifest의 2단 구성이 됩니다.
즉 네이티브 COM 때보다 manifest가 한 장 늘어납니다. 10.4의 이름과 리소스 ID 제약은 이쪽 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가 출력됩니다.
형식 정보의 취급이 별도의 논점이 되는 것은 8.3과 같지만, .NET Core / .NET 5+에서는 여기가 더 바뀝니다. .NET Framework 때처럼 어셈블리에서 TLB가 자연스럽게 나오는 세계가 아니므로, 형식이 있는 이용이 필요하면 TLB의 생성·포함·등록을 따로 조립해야 합니다. 구체적인 절차는 「.NET 8 DLL을 VBA에서 타입이 지정된 상태로 쓰는 방법 - COM 공개와 dscom TLB」에 정리해 두었습니다.
flowchart TB
accTitle: .NET 5 이후의 Reg-Free COM
accDescr: .NET 5 이후에는 EnableComHosting으로 COM 공개의 입구가 되는 comhost.dll을 만들고, EnableRegFreeCom을 켜면 Reg-Free COM용 side-by-side 매니페스트가 출력되지만, TLB의 생성이나 등록은 따로 조립해야 함을 나타내는 그림.
dn1["EnableComHosting으로 빌드"] --> dn2["comhost.dll이 입구가 된다"]
dn2 --> dn3["EnableRegFreeCom을 켠다"]
dn3 --> dn4["Reg-Free용 매니페스트가 출력된다"]
dn4 -.-> dn5["TLB의 취급은 따로 조립한다"]
그림 11: .NET 8에서는 속성 두 개로 발판이 생기지만, 형식 정보는 별 작업이다.
10. 최소 구성 이미지
여기서는 MyApp.exe가 Vendor.CameraControl.dll을 Reg-Free COM으로 쓰는 최소 이미지를 보입니다.
10.1 파일 구성 이미지
MyApp.exe
MyApp.exe.manifest
Vendor.CameraControl.Asm.manifest
Vendor.CameraControl.dll
Vendor.Helper.dll
위 예에서는 컴포넌트 매니페스트를 별도 파일로 둔 가정입니다. DLL에 포함하는 형태로도 할 수 있지만, 그때는 assembly 이름과 리소스 ID에 정해진 제약이 붙습니다. 절차마다 10.4에서 다룹니다.
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에 맞춰 올바르게 써야 합니다.
flowchart TB
accTitle: 두 매니페스트의 일치 요건
accDescr: 앱 쪽 dependentAssembly와 컴포넌트 쪽 assemblyIdentity는 이름이나 버전이 일치해야 하며, 여기가 어긋나면 오류 겉모습만으로는 원인을 알 수 없는 기동 실패가 됨을 나타내는 그림.
mm1["앱 쪽 dependentAssembly"] --> mm3{"이름이나 버전이 일치하는가"}
mm2["컴포넌트 쪽 assemblyIdentity"] --> mm3
mm3 -->|"일치"| mm4["해결이 성립한다"]
mm3 -->|"어긋남"| mm5["원인이 안 보이는 기동 실패"]
그림 12: XML의 세부보다, 양쪽 이름표가 일치하는지가 생사를 가른다.
10.4 매니페스트를 어디에 둘지, 어떻게 포함할지
여기가 절차로서 가장 막히기 쉬운 곳입니다. 별도 파일로 둘지, 바이너리에 포함할지에 따라 붙여도 되는 이름이 바뀝니다.
side-by-side는 private assembly를 앱 폴더에 대해 이 순서로 찾습니다.
- WinSxS 폴더
<appdir>\<assemblyname>.DLL<appdir>\<assemblyname>.manifest<appdir>\<assemblyname>\<assemblyname>.DLL<appdir>\<assemblyname>\<assemblyname>.manifest
assembly 이름과 같은 이름의 DLL이 먼저 발견되면, 거기서 탐색이 멈춥니다. 여기서 다음 두 가지밖에 성립하지 않습니다.
| 두는 방식 | assembly 이름 | 파일 | 포함처 리소스 ID |
|---|---|---|---|
| 별도 파일 | DLL 이름과 다른 이름으로 한다. 예: Vendor.CameraControl.Asm |
Vendor.CameraControl.Asm.manifest를 DLL 옆에 둔다 |
포함하지 않음 |
| DLL에 포함 | DLL 이름과 같아도 된다. 예: Vendor.CameraControl |
Vendor.CameraControl.dll만 |
1 |
즉 10.2와 10.3의 예처럼 Vendor.CameraControl.Asm이라는 이름을 쓴다면, 그것은 별도 파일 방식입니다. 포함으로 바꾸려면 assemblyIdentity의 name을 Vendor.CameraControl로 바꾸고, dependentAssembly 쪽도 같은 이름으로 맞춥니다.
flowchart TB
accTitle: 탐색이 멈추는 구조
accDescr: side-by-side는 private assembly를 WinSxS부터 앱 폴더 순으로 찾고, assembly 이름과 같은 이름의 DLL이 먼저 발견되면 거기서 탐색이 멈추므로, 별도 파일 운용에서는 assembly 이름을 DLL 이름과 다르게 두어야 함을 나타내는 그림.
se1["private assembly 탐색을 시작"] --> se2["assembly 이름과 같은 DLL을 찾는다"]
se2 -->|"발견"| se3["거기서 탐색이 멈춘다"]
se2 -->|"없음"| se4["같은 이름의 manifest 파일을 찾는다"]
se3 -.-> se5["별도 파일 운용에서는 이름을 다르게 한다"]
그림 13: 이름 붙이기 제약은, 탐색이 중간에 멈추는 사양에서 온다.
하나 더, 틀리기 쉬운 제약이 있습니다. 컴포넌트 매니페스트는 EXE 리소스에 넣을 수 없습니다. EXE에 넣을 수 있는 것은 애플리케이션 매니페스트 쪽입니다.
포함에는 Windows SDK의 mt.exe(매니페스트 도구)를 씁니다. Visual Studio의 개발자 명령 프롬프트에서 실행합니다.
rem 1. 포함하기 전에, 먼저 구문 검사를 통과시킨다
mt.exe -manifest MyApp.exe.manifest -validate_manifest
mt.exe -manifest Vendor.CameraControl.manifest -validate_manifest
rem 2. 애플리케이션 매니페스트를 EXE에 포함한다
mt.exe -manifest MyApp.exe.manifest -outputresource:MyApp.exe;#1
rem 3. 컴포넌트 매니페스트를 DLL에 포함한다(리소스 ID는 1)
mt.exe -manifest Vendor.CameraControl.manifest -outputresource:Vendor.CameraControl.dll;#1
rem 4. 포함됐는지, 꺼내서 확인한다
mt.exe -inputresource:Vendor.CameraControl.dll;#1 -out:extracted.manifest
4번째 명령으로 꺼낸 extracted.manifest를 원래 XML과 견주면, 「포함한 줄 알았는데 들어가 있지 않았다」를 없앨 수 있습니다.
flowchart TB
accTitle: mt.exe로 포함하는 절차
accDescr: mt.exe에서는 먼저 validate_manifest로 구문 검사를 통과시키고, 애플리케이션 매니페스트를 EXE에, 컴포넌트 매니페스트를 리소스 ID 1로 DLL에 포함한 뒤, 마지막으로 꺼내 원래 XML과 견주어 확인하는 흐름을 나타내는 그림.
mt1["구문 검사를 통과시킨다"] --> mt2["앱 쪽을 EXE에 포함한다"]
mt2 --> mt3["부품 쪽을 DLL에 포함한다〔ID 1〕"]
mt3 --> mt4["꺼내서 원래 XML과 견준다"]
그림 14: 포함은, 확인까지 포함해 하나의 절차로 돌린다.
mt.exe에는 주의점이 두 가지 있습니다.
- 매니페스트가 참조하는 파일은 매니페스트와 같은 디렉터리에 둬야 합니다.
<file name="Vendor.CameraControl.dll">라고 썼다면 그 DLL을 매니페스트 옆에 두고 실행합니다. 빌드 출력과 매니페스트 관리 위치가 나뉘어 있으면 여기서 멈춥니다 -outputresource에서 리소스 ID를 생략하면CREATEPROCESS_MANIFEST_RESOURCE(= 1)가 쓰입니다. 의도치 않게 1이 되어도 곤란하지 않도록;#1은 명시해 두는 편이 읽기 쉽습니다
이미 포함된 것을 교체하기만 하면 -updateresource:<파일>;#1을 쓸 수 있습니다. 이것은 -inputresource와 -outputresource에 같은 인수를 넘기는 것과 같은 뜻입니다.
10.5 클린 환경까지 포함한 확인 절차
Reg-Free COM은 「개발 PC에서 동작했다」가 거의 의미가 없습니다. 로컬 레지스트리 등록에 기대고 있을 뿐이라는 가능성이 항상 남기 때문입니다. 다음 순서로 확인합니다.
- 빌드하고, 배포물 일체를 한 폴더에 모은다. EXE, 매니페스트, COM DLL, 의존 DLL, VC++ 런타임, proxy / stub DLL까지
- 검증 환경을 마련한다. 대상 COM이 한 번도 등록된 적 없는 환경이 이상적입니다. Windows 샌드박스를 쓰면 매번 깨끗한 상태에서 시험할 수 있습니다
- 그 환경에서, 대상 COM이 미등록임을 먼저 확인한다. 아래 명령으로 키를 찾을 수 없다는 오류가 나면 미등록입니다
- 폴더를 복사해, 그대로 기동한다. 설치 프로그램도
regsvr32도 실행하지 않습니다 - COM 객체 생성까지 통과하는지 확인한다. 기동만으로는 지연 생성 컴포넌트는 검증할 수 없습니다. 실제로
CoCreateInstance가 도는 화면이나 기능까지 움직입니다 - 실패하면 11.2의 절차로 로그를 딴다
절차 3에서 쓰는 명령은 이것입니다.
reg query "HKCR\CLSID\{01234567-89AB-CDEF-0123-456789ABCDEF}" /reg:64
reg query "HKCR\CLSID\{01234567-89AB-CDEF-0123-456789ABCDEF}" /reg:32
reg query "HKCR\Vendor.CameraControl.1"
32bit와 64bit 레지스트리 뷰는 별개이므로, 앱 bitness에 맞는 쪽을 반드시 봅니다. /reg:64와 /reg:32를 둘 다 보면 확실합니다.
개발 PC에서 시험하고 싶을 때는 일단 regsvr32 /u Vendor.CameraControl.dll로 등록을 뺀 뒤 확인합니다. 다만 다른 제품이 같은 COM을 쓰는 환경에서는 영향이 나오므로, 클린 환경을 마련하는 편이 안전합니다.
flowchart TB
accTitle: 클린 환경에서의 확인 흐름
accDescr: 배포물 일체를 한 폴더에 모으고, 대상 COM이 등록된 적 없는 검증 환경을 마련한 뒤, 미등록임을 먼저 확인하고 폴더를 복사해 그대로 기동하며, CoCreateInstance가 도는 기능까지 움직여 확인하는 흐름을 나타내는 그림.
cv1["배포물 일체를 모은다"] --> cv2["클린 검증 환경을 마련"]
cv2 --> cv3["미등록임을 먼저 확인"]
cv3 --> cv4["복사해 그대로 기동"]
cv4 --> cv5["COM 생성이 도는 기능까지 움직인다"]
cv5 -.->|"실패하면"| cv6["로그를 따서 조사한다"]
그림 15: 미등록 확인을 끼워 넣음으로써, 우연히 동작하는 경우를 검증에서 배제한다.
11. 빠지기 쉬운 곳
11.1 개발 PC에서는 동작하는데, 배포 대상에서 동작하지 않는다
먼저 의심할 것은 실제로는 레지스트리 등록에 기대고 있던 패턴입니다. Reg-Free COM 검증은 가능하면 클린 환경에서 하는 편이 안전합니다.
11.2 「side-by-side configuration is incorrect」로 기동하지 않는다
이 계열은 manifest 불일치, 의존 DLL 부족, VC++ 런타임 부족, 아키텍처 차이 등으로 일어납니다.
표면의 오류 문장만으로는 꽤 불친절하므로, 이벤트 로그와 sxstrace로 추적하는 것이 정석입니다.
이벤트 로그는 이벤트 뷰어 > Windows 로그 > Application을 열고, 소스가 SideBySide인 오류를 찾습니다. 어느 assembly 해결에서 실패했는가가 여기에 나옵니다.
sxstrace는 실패를 재현하면서 캡처합니다. 명령 프롬프트는 관리자로 열어 두면 확실합니다.
rem 1. 추적을 시작한다. 이 창은 연 채로 둔다
sxstrace trace -logfile:sxstrace.etl
rem 2. 다른 창에서 앱을 기동하고, 실패를 재현한다
rem 3. 추적을 멈춘다. 1의 창에서 Enter를 누르거나, 다른 창에서 다음을 실행한다
sxstrace stoptrace
rem 4. 원본 .etl을 사람이 읽을 수 있는 형식으로 변환한다
sxstrace parse -logfile:sxstrace.etl -outfile:sxstrace.txt
중지 프롬프트를 내고 싶지 않으면 절차 1에 -nostop을 붙입니다. 출력이 너무 길면 절차 4에 -filter:MyApp.exe를 더해 대상 앱 분만 좁힐 수 있습니다.
변환 후의 sxstrace.txt에는 어느 매니페스트를 찾아, 어디서 일치하지 않았는가가 순서대로 늘어섭니다. 10.4의 이름 붙이기를 틀린 경우도, 여기서 「찾아 간 파일 이름」을 보면 알 수 있습니다.
flowchart TB
accTitle: side-by-side 오류를 조사하는 방법
accDescr: side-by-side configuration is incorrect로 기동하지 않을 때는 이벤트 뷰어에서 SideBySide 소스의 오류를 확인하고, sxstrace로 추적을 시작해 실패를 재현한 뒤, 중지 후 parse로 사람이 읽을 수 있는 형식으로 변환해 불일치 지점을 쫓는 흐름을 나타내는 그림.
sx1["이벤트 로그에서 SideBySide를 확인"] --> sx2["sxstrace로 추적 시작"]
sx2 --> sx3["앱을 기동해 실패를 재현"]
sx3 --> sx4["중지하고 parse로 변환"]
sx4 --> sx5["탐색과 불일치 지점을 읽는다"]
그림 16: 표면의 오류 문장으로 고민하지 말고, 로그와 추적으로 해결의 길을 쫓는다.
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을 도입할 때의 기본 자세는 이렇습니다.
- 이것은 activation의 이야기이다고 선을 긋는다
- runtime과 design-time의 논점을 나눈다
- 클린 환경에서 확인한다
- bitness와 의존 DLL을 먼저 맞춘다
이 순서로 보면 사고를 꽤 줄일 수 있습니다.
flowchart TB
accTitle: 도입 시의 기본 자세
accDescr: Reg-Free COM을 도입할 때는 이것은 액티베이션의 이야기라고 선을 긋고, 실행 시와 설계 시의 논점을 나누며, 클린 환경에서 확인하고, bitness와 의존 DLL을 먼저 맞추는 순서로 보면 사고가 줄어듦을 나타내는 그림.
bs1["activation의 이야기라고 선을 긋는다"] --> bs2["runtime과 design-time을 나눈다"]
bs2 --> bs3["클린 환경에서 확인한다"]
bs3 --> bs4["bitness와 의존 DLL을 먼저 맞춘다"]
그림 17: 네 가지 자세를 순서대로 지키면, 도입 사고는 꽤 줄일 수 있다.
13. 관련 글
- COM / ActiveX / OCX란 무엇인가 - 차이와 관계를 정리해 해설
- ActiveX / OCX를 지금 어떻게 다룰 것인가 - 남길지・감쌀지・교체할지 판단표
- .NET 8 DLL을 VBA에서 타입이 지정된 상태로 쓰는 방법 - COM 공개와 dscom TLB
14. 참고 자료
- Microsoft Learn - 등록이 필요 없는 COM 개체 만들기
- Microsoft Learn - 애플리케이션 매니페스트
- Microsoft Learn - Assembly Manifests(포함 시 리소스 ID와 이름 제약)
- Microsoft Learn - Assembly Searching Sequence(private assembly의 탐색 순서)
- Microsoft Learn - Manifest File Schema
- Microsoft Learn - Mt.exe
- Microsoft Learn - 등록이 필요 없는 COM 상호 운용성 (.NET Framework)
- Microsoft Learn - COM에 .NET Core 구성 요소 공개
- Microsoft Learn - sxstrace
관련 기사
같은 태그를 공유하는 최신 기사입니다. 더 가까운 주제로 지식을 넓힐 수 있습니다.
COM / ActiveX / OCX란 무엇인가 - 차이와 관계를 정리해 해설
COM이란 무엇인지, ActiveX란 무엇인지, OCX란 무엇인지를 차이와 관계, OLE와의 연결, 어디서 쓰이는지, 지금 어떻게 봐야 하는지까지 실무 관점에서 정리합니다.
클립보드와 드래그 앤 드롭의 구조 ── 업무 앱에서 OLE 데이터 전송을 올바르게 다루기
Excel 표를 붙여 넣으면 서식이 무너지고, 원본을 닫으면 붙여 넣을 수 없다── 원인은 같은 내용을 여러 형식으로 두는 클립보드입니다. 표준 형식·지연 렌더링·OLE 드래그 앤 드롭부터 기록과 클라우드 동기 정책까지 설명합니다.
Windows 셸 통합의 현재 ── 컨텍스트 메뉴, 파일 연결, Windows 11의 변화
Windows 11에서 컨텍스트 메뉴가 「더 많은 옵션 표시」 뒤로 숨는 이유를, 확장자→ProgID→verb라는 파일 연결의 기본, 기존형 셸 확장의 주의점, IExplorerCommand와 MSIX/sparse package의 새 방식까지 이...
DLL・COM 인터페이스의 하위 호환성 ── 어떤 변경이 호출 측을 깨뜨리는지의 판단표
DLL이나 COM 컴포넌트의 어떤 변경이 호출 측을 깨뜨리는지. 바이너리 호환・소스 호환・동작 호환의 3계층을 정리하고, 변경 내용별 판단표, COM 인터페이스 불변의 철칙, semver 운용까지를 실무 가이드로 정리합니다.
Windows on Arm에서 업무 앱은 동작하는가 ── x64 에뮬레이션(Prism)과 네이티브 DLL·COM의 현실
「Windows on Arm에서 업무 앱은 동작하는가」에 개발자와 사내 IT를 위해 답합니다. x64 에뮬레이션(Prism)의 구조, 드라이버처럼 동작하지 않는 계층, .NET의 AnyCPU와 P/Invoke 문제, Arm 대응 체크리스트까지 정...
관련 토픽
이 기사와 가까운 토픽 페이지입니다. 기사를 출발점 삼아 관련 서비스와 다른 기사로 이어집니다.
Windows 기술 토픽
Windows 개발, 장애 조사, 기존 자산 활용에 관한 KomuraSoft LLC 기사를 모은 토픽 허브입니다.
ActiveX 이관
COM / ActiveX / OCX 자산을 유지할지, 감쌀지, 교체할지의 단계적 판단을 정리한 토픽 페이지입니다.
이 주제와 연결되는 서비스
이 기사는 다음 서비스 페이지로 이어집니다. 가까운 입구부터 확인해 주세요.
Windows 앱 개발
COM DLL / OCX 배포, 매니페스트 구성, bitness, 의존 DLL까지 포함해 Windows 데스크톱 앱 구현과 직결되는 주제입니다.
기술 상담 & 설계 리뷰
Reg-Free COM을 택할지, 등록 기반 운영을 남길지, 형식 라이브러리와 설계 시 참조를 어떻게 나눌지의 판단 정리에도 맞습니다.
자주 묻는 질문
이 기사 주제에 대해 상담 시 자주 나오는 질문을 모았습니다.
- Reg-Free COM이란 무엇인가요?
- COM의 등록 정보를 레지스트리가 아니라 매니페스트로 가지는 구조이며, Registration-Free COM의 약자입니다. 실행 시에는 CoCreateInstance나 CLSIDFromProgID의 해결에서 액티베이션 컨텍스트가 먼저 보이고, 거기에 적힌 매니페스트 정보로 DLL을 해결합니다. 이로써 COM DLL/OCX를 앱마다 private로 둘 수 있어, XCOPY 배포가 쉽고, 버전 충돌을 피하기 쉬우며, 제거가 잘 깨지지 않는다는 이점이 있습니다.
- Reg-Free COM이면 32bit/64bit 문제도 해결되나요?
- 해결되지 않습니다. 32bit 프로세스에는 32bit in-proc COM DLL만 로드할 수 있고, 64bit 프로세스에는 64bit DLL만 들어갑니다. 이 점은 Reg-Free에서도 예전과 같습니다. 의존 DLL과 VC++ 런타임 배포, 형식 라이브러리, 설계 시 참조 설정, 비표준 등록 정보에 대한 의존도 따로 생각해야 합니다. Reg-Free COM에서 사라지는 것은 주로 글로벌 등록에 끌려가는 번거로움뿐입니다.
- Reg-Free COM 구성이 개발 PC에서는 동작하는데 배포 대상에서 동작하지 않는 이유는 무엇인가요?
- 먼저 의심할 것은, 실제로는 레지스트리 등록에 기대고 있던 패턴입니다. 매니페스트에 필요한 정보가 부족하면 COM 런타임은 통상의 등록 기반 해결로 떨어지므로, 개발 PC에서는 로컬 등록 덕분에 우연히 동작하는 경우가 있습니다. 그래서 Reg-Free COM 검증은 클린 환경에서 하는 편이 안전합니다. 「side-by-side configuration is incorrect」로 기동하지 않을 때는 매니페스트 불일치나 의존 DLL 부족 등이 원인이며, 이벤트 로그와 sxstrace로 추적하는 것이 정석입니다.
- .NET 8로 만든 COM 컴포넌트도 Reg-Free COM으로 할 수 있나요?
- 할 수 있습니다. .NET 5+/.NET 8에서는 EnableComHosting으로 COM 공개의 입구가 되는 *.comhost.dll을 만들고, 여기에 EnableRegFreeCom=true를 넣으면 Reg-Free COM용 side-by-side manifest가 출력됩니다. 다만 Reg-Free COM과 TLB 전략은 별개의 논점입니다. .NET Core/.NET 5+에서는 .NET Framework 때처럼 어셈블리에서 TLB가 자연스럽게 나오지 않으므로, VBA 초기 바인딩처럼 형식이 있는 이용이 필요하면 TLB의 생성·포함·등록을 따로 정리하는 편이 안전합니다.