탐색기에서 사진이 많은 폴더를 열었을 때 "COM Surrogate 가 작동을 중지했습니다"라는 창을 본 적이 있다면 이 이름이 낯설지 않을 것이다. 작업 관리자의 프로세스 탭에서도 COM Surrogate 가 여러 줄 보이고, 세부 정보 탭으로 가면 그 정체는 dllhost.exe 다. 이름만으로는 무슨 일을 하는지 전혀 알 수 없는 프로세스다.

10개가 떠 있었다

확인한 시점에 dllhost.exe 는 10개였다. 9개는 C:\Windows\System32\DllHost.exe, 1개는 C:\Windows\SysWOW64\DllHost.exe 였다. SysWOW64 쪽은 32비트 구성 요소를 위한 32비트판이다. System32 의 파일은 약 91KB 의 작은 실행 파일이고, 파일 설명은 "COM Surrogate", 버전은 10.0.26100.9549, 서명은 Microsoft Windows 로 유효했다.

10개 모두 부모는 같은 svchost.exe 였다. tasklist /svc 로 그 svchost 안의 서비스를 보면 DcomLaunch(DCOM Server Process Launcher), BrokerInfrastructure, PlugPlay, Power 등이 함께 들어 있다. 이 연재 앞쪽의 Runtime Broker를 띄운 것과 같은 svchost 다. 사용자가 직접 실행한 것이 아니라 COM 이 필요할 때 띄워 준 것이라는 뜻이다.

메모리는 하나당 작업 집합 9.6~16.4MB 였고, 10개의 개인 메모리를 다 더해도 약 29MB 였다.

DLL 을 대신 실행해 주는 대리인

Windows 의 많은 기능은 COM 이라는 방식으로 만든 DLL 에 들어 있다. 썸네일을 만드는 코드, 오른쪽 클릭 메뉴에 항목을 더하는 코드, 파일 형식을 해석하는 코드 같은 것이다. DLL 은 혼자 실행되지 못하고 어떤 프로세스 안에 올라가야 한다. 보통은 그 기능을 쓰는 프로그램, 예를 들어 탐색기 안에 바로 올라간다.

문제는 그 DLL 이 제3자가 만든 것일 때다. 압축 프로그램의 메뉴 확장이나 동영상 코덱의 썸네일 코드가 오류로 죽으면, 그걸 품고 있던 탐색기까지 같이 죽는다. 그래서 Windows 는 이런 DLL 을 탐색기 밖의 별도 프로세스에 올려 실행하는 방법을 쓴다. 그 빈 껍데기 프로세스가 dllhost.exe 이고, 대리인이라는 뜻의 Surrogate 가 이름에 붙은 이유다. 대리인 안에서 DLL 이 죽으면 COM Surrogate 만 끝나고 탐색기는 살아남는다. 앞에서 말한 "작동을 중지했습니다" 창이 바로 그 장면이다.

/Processid 의 ID 로 정체 찾기

dllhost 가 무엇을 대신 실행하고 있는지는 명령줄에 남는다.

Get-CimInstance Win32_Process -Filter "Name='dllhost.exe'" |
    Select-Object ProcessId, CommandLine

명령줄은 모두 DllHost.exe /Processid:{DFB65C4C-B34F-435D-AFE9-A86218684AA8} 처럼 중괄호 안에 ID 하나가 붙은 모양이었다. 이 ID 는 COM 의 AppID 또는 클래스 ID 이고, 레지스트리에서 찾을 수 있다. 오래된 방식으로 등록된 것은 HKEY_CLASSES_ROOT\AppID 와 HKEY_CLASSES_ROOT\CLSID 에, Store 패키지 방식으로 등록된 것은 HKLM\SOFTWARE\Classes\PackagedCom\ClassIndex 에 있다.

$id = '{DFB65C4C-B34F-435D-AFE9-A86218684AA8}'
Get-ChildItem Registry::HKEY_CLASSES_ROOT\CLSID |
    Where-Object { $_.GetValue('AppID') -eq $id } |
    ForEach-Object { $_.GetValue('') }
Get-ChildItem "HKLM:\SOFTWARE\Classes\PackagedCom\ClassIndex\$id" -ErrorAction SilentlyContinue

첫 줄은 그 AppID 를 쓰는 클래스의 이름을, 둘째 줄은 패키지 이름을 보여 준다. CLSID 전체를 훑기 때문에 몇 초 걸린다. 내 PC 의 10개를 이렇게 찾아본 결과다.

레지스트리에서 찾은 정체 DLL 개수
Plan9FileSystem (WSL 파일을 탐색기에서 열 때) vp9fs.dll 1
WslDeviceHost (WSL 장치 연결) wsldevicehost.dll 1
WebPlatformStorageServer (AppID 이름만 확인) 1
Bandizip 오른쪽 클릭 메뉴 bzshell.x64.dll 1
OneDrive 오른쪽 클릭 메뉴 FileSyncShell64.dll 1
Windows 메모장 오른쪽 클릭 메뉴 NotepadExplorerCommand.dll 1
Notepad++ 오른쪽 클릭 메뉴 NppShell.dll 1
VS Code "Code로 열기" 메뉴 code_explorer_command_x64.dll 1
패키지 이름이 ID 로만 된 앱 확인 못 함 1
명령줄이 읽히지 않은 것 - 1

절반이 오른쪽 클릭 메뉴였다. Windows 11 의 새 오른쪽 클릭 메뉴에 항목을 넣는 앱 확장은 패키지 방식 COM 으로 등록되고, 탐색기 밖의 dllhost 에서 실행되는 것으로 보인다. 탐색기에서 파일을 한 번 오른쪽 클릭하고 나면 이런 dllhost 가 하나씩 늘었다가, 한동안 쓰지 않으면 사라진다. 그래서 dllhost 의 개수는 "최근에 어떤 메뉴와 기능을 썼느냐"에 따라 계속 달라진다.

썸네일과 COM Surrogate 오류

COM Surrogate 가 가장 자주 문제로 보이는 곳은 썸네일이다. 탐색기는 사진·동영상의 미리 보기를 만들 때 파일 형식별 썸네일 처리기를 부르고, 그중 상당수가 dllhost 안에서 돈다. 내 PC 의 레지스트리에는 Thumbnail Cache Out of Proc Server 라는 AppID 가 thumbcache.dll 과 묶여 등록되어 있었다. 확인한 시점에는 썸네일을 만드는 중이 아니어서 이 ID 로 뜬 dllhost 는 없었다. 만들어진 썸네일은 %LOCALAPPDATA%\Microsoft\Windows\Explorer 의 thumbcache_*.db 파일 15개, 합계 86.4MB 로 쌓여 있었다.

동영상 폴더를 열 때마다 COM Surrogate 가 죽거나 CPU 를 잡고 놓지 않는다면, 범인은 dllhost 가 아니라 그 안에 올라간 코덱이나 썸네일 처리기일 가능성이 높다. 따로 설치한 코덱 팩이나 영상 편집 프로그램이 넣어 둔 처리기가 특정 파일을 해석하다 막히는 경우가 많은 것으로 알려져 있다. 확인 순서는 이렇다.

이벤트 뷰어의 Windows 로그 → 응용 프로그램에서 이벤트 ID 1000(응용 프로그램 오류)을 찾으면, 죽은 프로세스가 dllhost.exe 일 때 "오류 모듈 이름"에 실제로 문제를 일으킨 DLL 이 나온다. PowerShell 로는 이렇게 본다.

Get-WinEvent -FilterHashtable @{LogName='Application'; Id=1000} -MaxEvents 500 |
    Where-Object Message -match 'dllhost' | Select-Object -First 5 TimeCreated, Message

내 PC 에서는 최근 기록 중 dllhost 오류가 한 건도 없었다. 오류 모듈이 어떤 코덱의 DLL 로 나온다면 그 코덱을 업데이트하거나 제거하는 것이 해결책이다. dllhost.exe 를 막거나 지우는 건 답이 아니다. 대리인을 없애면 그 DLL 은 탐색기 안에서 직접 돌거나 아예 동작하지 못한다.

경로가 System32 나 SysWOW64 이고 서명이 Microsoft Windows 로 유효하다면 dllhost 는 정상이다. 다른 폴더의 dllhost.exe 이거나 /Processid 없이 떠 있는데 CPU·네트워크를 계속 쓴다면 그때 의심하면 된다.

다음 편은 개수 대신 메모리 숫자로 눈에 걸리는 프로세스, Desktop Window Manager(dwm.exe)다. 모니터를 여러 대 쓰는 PC 에서 메모리와 GPU 를 얼마나 쓰는지 본다.