← 기술 블로그 목록으로 돌아가기
#947 • System Services & Windows Management Instrumentation (WMI) Governance

전산실 좀비 프로세스와 백그라운드 서비스 교착(Deadlock) 0초 해결: ZeliService의 WMI Win32_Service 비동기 수집과 sc.exe UAC 승격 거버넌스 아키텍처

"컴퓨터실 프린터 스풀러(Print Spooler)가 멈춰 인쇄가 먹통이 되거나, 윈도우 업데이트 서비스가 백그라운드 CPU를 100% 점유해 수업이 마비된 적이 있으십니까?" services.msc의 답답한 검색과 상용 시스템 도구의 유료 라이선스 단속을 무력화하는 Zero-Install 온디바이스 서비스 제어 스위트, ManagementObjectSearcher 기반의 WMI Win32_Service WQL 비동기 쿼리 엔진, 4대 서비스 상태(Running/Stopped/Paused/Pending) 이모지 시각화, 이름·표시명·설명 통합 실시간 필터링, sc.exe 래핑과 Verb="runas"를 통한 필요 최소한의 UAC 승격 및 Win32Exception 샌드박싱, 그리고 원자적 서비스 재시작(Restart) 파이프라인 백서.

"초·중·고 정보 수업 중 학생들이 수행평가 보고서를 인쇄하려 하면, 특정 문서의 폰트 에러로 인해 교내 프린터 스풀러(`Spooler`) 서비스가 'Stop Pending' 상태로 영구 교착(Deadlock)에 빠집니다. 교사는 윈도우 기본 `services.msc`를 실행하지만, 수백 개의 서비스 목록 중에서 문제를 일으킨 서비스를 찾느라 스크롤을 끝없이 내려야 하고, 서비스 정보를 기술지원센터에 전달하려 해도 텍스트 복사조차 되지 않습니다. Process Hacker나 Process Explorer 같은 도구를 설치하자니 보안 정책에 위배되고 유료 라이선스 단속에 걸립니다. ZeliService는 단 1바이트의 외부 바이너리 없이, 0초 만에 WMI로 300여 개 서비스의 실시간 상태를 집계하고 단 한 번의 클릭으로 안전하게 재시작합니다."

1. 학교 컴퓨터실과 군부대 PC의 서비스 마비 잔혹사: "Print Spooler 충돌과 Windows Update 무한 루프"

Windows 운영체제의 백그라운드 서비스(Windows Services)는 시스템의 생명선이지만, 동시에 장애 발생 시 가장 진단하기 어려운 영역입니다:

2. 윈도우 기본 서비스 관리자(services.msc)의 한계: "느린 검색, 필터링 부재, 서비스 정보 복사 불가"

Windows의 기본 서비스 관리 콘솔(services.msc)은 20년 전의 MMC(Microsoft Management Console) 아키텍처에 갇혀 있습니다:

  1. 실시간 검색(Search) 기능 부재: 서비스를 찾으려면 첫 글자를 타이핑하거나 스크롤바를 일일이 내려야 하며, 서비스 설명(Description)이나 실행 경로(PathName)를 기반으로 한 검색은 불가능합니다.
  2. 상태별 필터링 불가: "현재 실행 중인 서비스만 보기" 혹은 "자동 시작인데 중지된 비정상 서비스만 보기"와 같은 기초적인 필터조차 지원하지 않습니다.
  3. 상세 정보 원클릭 복사 불가: 서비스 이름, 실제 바이너리 경로, PID를 IT 지원팀이나 상급 기관에 전송하려면 속성 창을 열어 마우스로 드래그해야 하는 극심한 비효율이 발생합니다.
flowchart TD subgraph WMI_SCAN["🔍 WMI Win32_Service WQL 비동기 쿼리 엔진"] QUERY["SELECT Name, DisplayName, State, StartMode, Description, ProcessId, PathName FROM Win32_Service"] QUERY --> PARSE["ServiceItem 모델 매핑 & 4대 상태 이모지 부여"] PARSE --> MEM["_allServices 인메모리 캐싱 (300+ 서비스 0초 로드)"] end subgraph FILTER_ENGINE["⚡ 실시간 스트리밍 필터 & 다중 필드 검색"] MEM --> F1["상태 필터 (전체 / Running / Stopped / Auto)"] F1 --> F2["텍스트 검색 (서비스명 / 표시명 / 설명 다중 대조)"] F2 --> UI_LIST["_displayServices WinUI 3 반응형 ListView 렌더링"] end subgraph SECURE_CTRL["🛡️ sc.exe 래핑 & UAC 권한 격리 제어 파이프라인"] UI_LIST -->|선택 후 시작/중지/재시작| DIALOG["ContentDialog 확인 모달 (실수 방지)"] DIALOG -->|확인| SC["sc.exe {action} \"{serviceName}\""] SC -->|Verb = runas| UAC["UAC 권한 승격 팝업"] UAC -->|승인| EXEC["WaitForExitAsync 원자적 실행 ➔ 1초 후 자동 리로드"] UAC -->|거부 (Win32Exception)| CATCH["권한 부족 안전 롤백 샌드박스 (크래시 0%)"] end classDef wmi fill:#1e1b4b,stroke:#818cf8,stroke-width:2px,color:#e0e7ff; classDef filter fill:#0f172a,stroke:#a855f7,stroke-width:2px,color:#f3e8ff; classDef ctrl fill:#022c22,stroke:#10b981,stroke-width:2px,color:#a7f3d0; class WMI_SCAN wmi; class FILTER_ENGINE filter; class SECURE_CTRL ctrl;

3. WMI Win32_Service WQL 비동기 쿼리 엔진: 수백 개 시스템 서비스의 0초 인벤토리 수집

System.ServiceProcess.ServiceController 클래스는 간단하지만, 각 서비스의 상세 설명(Description), 실행 파일 경로(PathName), 프로세스 ID(PID)를 가져오지 못합니다.

ZeliService는 WMI(Windows Management Instrumentation)의 Win32_Service 클래스를 WQL(WMI Query Language)로 비동기 질의하여, 단 한 번의 호출로 시스템 내 300여 개 서비스의 모든 메타데이터를 0초 만에 완벽하게 수집합니다:

C# ZeliService/Views/ZeliServiceWindow.xaml.cs — WQL 비동기 쿼리 엔진
private async Task LoadServicesAsync()
{
    SetStatus(string.Format("🔄 {0}", Loc.Get("Svc_Scanning")), "");

    var services = await Task.Run(() =>
    {
        var list = new List<ServiceItem>();
        try
        {
            using var searcher = new ManagementObjectSearcher(
                "SELECT Name, DisplayName, State, StartMode, Description, ProcessId, PathName FROM Win32_Service");
            foreach (ManagementObject obj in searcher.Get())
            {
                var state = obj["State"]?.ToString() ?? "";
                list.Add(new ServiceItem
                {
                    ServiceName = obj["Name"]?.ToString() ?? "",
                    DisplayName = obj["DisplayName"]?.ToString() ?? "",
                    Status = state,
                    StatusIcon = state switch
                    {
                        "Running" => "🟢",
                        "Stopped" => "🔴",
                        "Paused" => "🟡",
                        "Start Pending" => "🟠",
                        "Stop Pending" => "🟠",
                        _ => "⚪"
                    },
                    StartType = obj["StartMode"]?.ToString() switch
                    {
                        "Auto" => Loc.Get("Svc_StartTypeAuto"),
                        "Manual" => Loc.Get("Svc_StartTypeManual"),
                        "Disabled" => Loc.Get("Svc_StartTypeDisabled"),
                        var x => x ?? ""
                    },
                    Description = obj["Description"]?.ToString() ?? "",
                    Pid = Convert.ToInt32(obj["ProcessId"] ?? 0),
                    PathName = obj["PathName"]?.ToString() ?? ""
                });
            }
        }
        catch (Exception ex)
        {
            Debug.WriteLine($"[ZeliService] WMI error: {ex.Message}");
        }
        return list.OrderBy(s => s.DisplayName).ToList();
    });

    _allServices = services;
    ApplyFilter();
}

4. 상태별 이모지 시각화와 다중 필드(이름·표시명·설명) 실시간 스트리밍 필터

사용자는 텍스트로 된 영문 상태 대신, 한눈에 직관적으로 파악할 수 있는 시각적 힌트를 원합니다. ZeliService는 4대 상태에 직관적인 이모지(🟢/🔴/🟡/🟠)를 부여하고, 사용자가 검색창에 타이핑하는 즉시 대소문자 구분 없이 서비스명, 표시 이름, 설명문까지 전방위 대조합니다:

C# ZeliService/Views/ZeliServiceWindow.xaml.cs — 실시간 스트리밍 필터링
private void ApplyFilter()
{
    var filtered = _allServices.AsEnumerable();

    // 1. 상태 콤보박스 필터링 (전체 / Running / Stopped / Auto)
    int filterIdx = CboFilter.SelectedIndex;
    filtered = filterIdx switch
    {
        1 => filtered.Where(s => s.Status == "Running"),
        2 => filtered.Where(s => s.Status == "Stopped"),
        3 => filtered.Where(s => s.StartType == Loc.Get("Svc_StartTypeAuto")),
        _ => filtered
    };

    // 2. 텍스트 다중 필드 실시간 검색
    string search = TxtSearch.Text?.Trim() ?? "";
    if (!string.IsNullOrEmpty(search))
    {
        filtered = filtered.Where(s =>
            s.ServiceName.Contains(search, StringComparison.OrdinalIgnoreCase) ||
            s.DisplayName.Contains(search, StringComparison.OrdinalIgnoreCase) ||
            (s.Description?.Contains(search, StringComparison.OrdinalIgnoreCase) ?? false));
    }

    var result = filtered.ToList();
    _displayServices.Clear();
    foreach (var s in result) _displayServices.Add(s);
    TxtCount.Text = $"({result.Count}/{_allServices.Count})";
}

5. sc.exe 래핑과 Verb=runas를 통한 안전한 UAC 승격 및 Win32Exception 샌드박싱

윈도우 시스템 서비스를 제어하려면 반드시 관리자 권한이 필요합니다. 그러나 앱 전체를 처음부터 '관리자 권한으로 실행'하도록 강제하면, 권한이 없는 일반 학생 계정에서는 프로그램 실행 자체가 차단되는 심각한 문제가 발생합니다.

ZeliService는 최소 권한의 원칙(Principle of Least Privilege)을 적용합니다. 일반 읽기 및 검색은 일반 사용자 권한으로 0초 만에 구동하고, 사용자가 특정 서비스의 시작/중지를 요청할 때만 Windows의 신뢰받는 네이티브 도구인 sc.exe를 Verb = "runas"로 격리 실행합니다:

C# ZeliService/Views/ZeliServiceWindow.xaml.cs — sc.exe UAC 격리 실행 엔진
private async Task RunScCommand(string action, string serviceName)
{
    try
    {
        var psi = new ProcessStartInfo
        {
            FileName = "sc.exe",
            Arguments = $"{action} \"{serviceName}\"",
            UseShellExecute = true,
            Verb = "runas" // 필요한 순간에만 UAC 권한 승격 창 요청
        };
        var proc = Process.Start(psi);
        if (proc != null) await proc.WaitForExitAsync();
        
        await Task.Delay(1000);
        await LoadServicesAsync(); // 서비스 상태 자동 리로드
        SetStatus(string.Format("✅ {0}", string.Format(Loc.Get("Svc_ActionComplete"), serviceName, action)), "");
    }
    catch (System.ComponentModel.Win32Exception)
    {
        // 사용자가 UAC 승인 팝업에서 '아니오'를 누르거나 권한 거부 시 안전 격리
        SetStatus(string.Format("❌ {0}", Loc.Get("Svc_NeedAdmin")), "");
    }
    catch (Exception ex)
    {
        SetStatus(string.Format("❌ {0}: {1}", Loc.Get("Common_Error"), ex.Message), "");
    }
}

6. 가역적 서비스 재시작(Restart) 파이프라인: 원자적 정지-지연-기동-재스캔 시퀀스

먹통이 된 서비스를 정상화할 때 가장 안전한 방법은 '완전한 정지(Stop) ➔ 프로세스 자원 반환 대기 ➔ 깨끗한 재시작(Start)'입니다. 윈도우 기본 도구는 정지 중 타임아웃이 나면 서비스가 '중지 중(Stop Pending)' 상태로 굳어버리기 십상입니다.

ZeliService의 재시작 파이프라인은 2,000ms의 완충 시간을 두어 소켓과 I/O 핸들이 온전히 닫히도록 유도한 후 기동하며, 곧바로 WMI 재스캔을 수행하여 실제 가동 여부를 시각적으로 확정합니다:

C# ZeliService/Views/ZeliServiceWindow.xaml.cs — 4단계 원자적 재시작 시퀀스
private async void BtnRestart_Click(object sender, RoutedEventArgs e)
{
    var svc = GetSelectedService();
    if (svc == null) { SetStatus(string.Format("⚠️ {0}", Loc.Get("Svc_SelectService")), ""); return; }

    SetStatus(string.Format("🔁 {0}", string.Format(Loc.Get("Svc_Restarting"), svc.DisplayName)), "");
    
    // Step 1: 안전 정지 명령
    await RunScCommand("stop", svc.ServiceName);
    
    // Step 2: 자원 반환 및 프로세스 정리 대기 (2초 완충)
    await Task.Delay(2000);
    
    // Step 3: 깨끗한 재시동
    await RunScCommand("start", svc.ServiceName);
    
    // Step 4: 완료 후 LoadServicesAsync가 자동 호출되어 최신 상태 반영
}

7. 윈도우 서비스 관리 도구(services.msc vs PowerShell Get-Service vs Process Hacker vs ZeliService) 비교 분석

학교 전산실 및 국방 폐쇄망 환경의 관점에서 기존 솔루션들과 비교 분석한 결과는 다음과 같습니다:

비교 항목 Windows services.msc PowerShell (Get-Service) Process Hacker (상용/오픈소스) ZeliService (온디바이스)
설치 및 구동 방식 OS 내장 MMC CLI 터미널 명령어 설치 필요 (드라이버 적재) Zero-Install 무설치 네이티브 C#
초기 실행 권한 요구 일반 권한 (수정 시 차단) 터미널 권한 종속 관리자 권한 필수 (KProcessHacker) 일반 권한 0초 로드 + 필요시만 UAC
실시간 스트리밍 검색 미지원 (첫 글자 점프만 가능) Where-Object 스크립트 작성 텍스트 필터 지원 이름·표시명·설명 다중 실시간 검색
상태 시각화 텍스트 (시작됨/중지됨) 텍스트 색상 하이라이트 4대 상태 직관적 이모지(🟢/🔴/🟡/🟠)
서비스 정보 원클릭 복사 미지원 (수동 드래그) 파이프라인 포맷팅 필요 속성 창 복사 이름·경로·PID·설명 클립보드 원클릭
공공기관 라이선스 단속 무관 무관 보안 정책 위반 우려(드라이버 차단) 완전 무상 보급 / 100% 라이선스 프리

8. 결론: "상용 라이선스 단속과 블루스크린 공포 없는 공교육·국방 전산 인프라 서비스 거버넌스"

멈춰버린 서비스 하나가 수십 명의 수업을 중단시키고 업무망을 마비시킬 수 있습니다. 하지만 문제를 해결하기 위해 시스템 커널 드라이버를 건드리는 무거운 전문 도구를 설치하는 것은 시스템 안정성에 더 큰 위험을 초래합니다.

ZeliDesk의 ZeliService는 마이크로소프트의 표준 WMI 인프라와 신뢰받는 sc.exe만을 완벽하게 조합하여, 관리자 권한 없는 일반 계정에서도 안전하게 전체 서비스를 진단하고 꼭 필요한 순간에만 최소한의 UAC 승격으로 문제를 해결합니다. 이제 전국의 교육 현장과 공공 전산망은 가장 안전하고 스마트한 방식으로 백그라운드 서비스 라이프사이클을 통제할 수 있습니다.

← 이전 기술 백서 #948 디지털 교과서(AIDT) 원격 수업 지연과 학내망 병목 0초 진단: ZeliSpeed의 ICMP 지터(Jitter) 계측과 비동기 스트리밍 대역폭 텔레메트리 아키텍처 다음 기술 백서 → #946 학생 과제물 100개 0초 일괄 정리와 소실 방지: ZeliRename의 정규식(Regex) 배치 리네이머와 Undo 스택 가역 복원 아키텍처
⚙️ ZeliDesk ZeliService 온디바이스 서비스 관리자 체험하기
0초 만에 300개 윈도우 서비스 상태를 파악하고, 교착된 서비스를 안전하게 재기동하세요.
72종 유틸리티 전체 둘러보기 →