[WinAPI] SetHandleInformation과 bInheritHandles로 자식 프로세스에 핸들을 안전하게 전달하는 방법
🔐 부모 프로세스의 리소스를 자식에게 안전하게 넘기는 핸들 상속의 모든 것
Windows API를 다루다 보면 프로세스 간에 리소스를 공유해야 하는 상황이 자주 발생합니다.
특히 자식 프로세스를 생성하면서 파일 핸들이나 파이프와 같은 리소스를 안전하게 넘기기 위해서는 핸들 상속을 적절히 설정해야 하죠.
하지만 단순히 상속 가능 플래그만 설정했다고 해서 모든 게 자동으로 잘 되지는 않습니다.
오히려 의도치 않게 리소스가 누수되거나, 자식 프로세스에서 접근이 안 되는 문제가 생기기 쉽습니다.
이번 글에서는 SetHandleInformation 함수와 STARTUPINFO 구조체의 bInheritHandles 옵션을 활용해,
핸들을 정확하고 안전하게 자식 프로세스로 전달하는 방법을 다뤄보겠습니다.
단순 예제 코드에서부터 실무적인 활용 팁까지 함께 정리해드릴 테니,
WinAPI로 프로세스 제어를 구현하는 분들이라면 꼭 끝까지 읽어보시길 추천드립니다.
📋 목차
🔍 핸들 상속이란 무엇인가요?
Windows 운영체제에서는 파일, 파이프, 소켓, 이벤트 등 다양한 시스템 리소스에 접근하기 위해 핸들(Handle)이라는 개체를 사용합니다.
이 핸들은 커널 객체를 참조하는 포인터 같은 역할을 하며, 주로 프로세스 단위로 소유 및 접근 권한이 설정됩니다.
그런데 어떤 경우에는 부모 프로세스가 소유한 핸들을 자식 프로세스에게도 공유해야 할 필요가 생기는데요.
예를 들어, 부모가 만든 파이프를 통해 자식 프로세스와 데이터를 주고받거나,
특정 로그 파일에 자식 프로세스도 접근해서 동시에 내용을 기록해야 하는 경우입니다.
이때 필요한 개념이 바로 핸들 상속입니다.
-
📎
자식 프로세스에서 부모가 만든 파일 핸들을 그대로 사용하고 싶은 경우 -
🔌
이름 없는 파이프를 생성해 부모-자식 간 IPC를 구현할 때 -
📂
로그 파일을 여러 프로세스가 함께 기록할 때
하지만 단순히 자식 프로세스를 생성한다고 해서 부모의 핸들이 자동으로 상속되는 것은 아닙니다.
Windows에서는 명시적으로 상속 가능 여부를 설정해야 하고,
그 과정에서 보안 문제나 리소스 누수를 유발할 수 있으므로 반드시 신중하게 처리해야 합니다.
💎 핵심 포인트:
핸들 상속은 프로세스 간 통신과 리소스 공유에 매우 유용하지만, 설정을 잘못하면 예상치 못한 오류나 보안 취약점이 발생할 수 있습니다.
⚙️ CreateProcess와 bInheritHandles 옵션
Windows에서 자식 프로세스를 생성할 때 가장 많이 사용하는 함수는 CreateProcess입니다.
이 함수는 부모 프로세스가 자식 프로세스를 생성하는 전 과정을 세세하게 제어할 수 있는 기능을 제공하는데요,
그중에서도 핸들 상속 여부를 결정하는 핵심 인자가 바로 bInheritHandles입니다.
bInheritHandles는 BOOL 타입의 매개변수로, 이 값이 TRUE로 설정되면 부모 프로세스에서 상속 가능하게 설정된 핸들이 자식에게 전달됩니다.
단, 이 값만 설정한다고 해서 모든 핸들이 상속되는 것은 아닙니다.
핸들 자체에 상속 가능(Handle Inheritable) 속성이 설정되어 있어야 하며,
그렇지 않으면 bInheritHandles가 TRUE여도 자식은 핸들에 접근할 수 없습니다.
STARTUPINFO si = { 0 };
PROCESS_INFORMATION pi = { 0 };
si.cb = sizeof(si);
// 자식 프로세스를 생성할 때 핸들 상속 여부 설정
BOOL result = CreateProcess(
L"C:\\MyApp\\Child.exe",
NULL,
NULL,
NULL,
TRUE, // 🔴 bInheritHandles = TRUE
0,
NULL,
NULL,
&si,
&pi
);
위 예제처럼 bInheritHandles를 TRUE로 설정했다면,
부모 프로세스는 반드시 상속할 핸들에 대해 미리 상속 가능 플래그를 지정해주어야 합니다.
이때 사용되는 함수가 바로 SetHandleInformation입니다.
이는 다음 STEP에서 자세히 설명드릴게요.
⚠️ 주의: bInheritHandles를 TRUE로 설정하면 의도하지 않은 핸들까지 자식에게 넘어갈 수 있어, 반드시 필요한 핸들만 상속 가능 상태로 설정해야 합니다.
🔧 SetHandleInformation 함수의 역할
자식 프로세스가 부모의 핸들에 접근하려면 단순히 bInheritHandles 옵션을 TRUE로 설정하는 것만으로는 부족합니다.
핸들 자체에 상속 가능 속성이 명시적으로 설정되어 있어야 하며, 이를 지정하는 함수가 바로 SetHandleInformation입니다.
이 함수는 특정 핸들의 속성을 변경할 수 있으며, 주로 사용하는 플래그는 HANDLE_FLAG_INHERIT입니다.
이 플래그를 설정하면 해당 핸들은 자식 프로세스로 상속 가능해지고, 반대로 플래그를 제거하면 상속되지 않습니다.
// 핸들을 자식 프로세스로 상속 가능하게 설정
SetHandleInformation(hFile, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
첫 번째 인자는 핸들 값이며, 두 번째 인자는 수정할 속성의 종류, 세 번째 인자는 설정할 속성 값을 의미합니다.
위 코드처럼 두 번째, 세 번째 인자 모두 HANDLE_FLAG_INHERIT로 설정하면 상속 가능 상태가 활성화됩니다.
반대로 핸들 상속을 비활성화하고 싶다면, 세 번째 인자를 0으로 지정하면 됩니다.
이는 리소스 유출이나 불필요한 핸들 공유를 방지할 때 유용합니다.
💎 핵심 포인트:
SetHandleInformation은 핸들의 속성을 직접 제어할 수 있는 중요한 함수로, 상속 가능 여부 외에도 디버깅 시 리소스 누수를 추적하거나 보안 설정을 강화하는 데 매우 유용하게 쓰입니다.
-
✔️
bInheritHandles = TRUE설정만으로는 핸들 상속이 되지 않음 -
✔️
SetHandleInformation으로 명시적으로 HANDLE_FLAG_INHERIT 설정 필요 -
✔️
불필요한 핸들은 반드시 상속 비활성화할 것
📎 파일 핸들, 파이프 상속 실전 예제
이번에는 실제 코드 예제를 통해 부모 프로세스가 자식 프로세스에게 파일 핸들 또는 파이프 핸들을 안전하게 넘겨주는 과정을 정리해보겠습니다.
핵심은 다음 3단계로 요약됩니다.
-
🔐
핸들을 생성할 때 보안 속성 구조체에 bInheritHandle = TRUE 설정 -
🔧
SetHandleInformation으로 핸들에 HANDLE_FLAG_INHERIT 지정 -
🚀
CreateProcess에서bInheritHandles = TRUE설정
// 1. 보안 속성 구조체 준비
SECURITY_ATTRIBUTES sa = { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
// 2. 상속 가능한 파일 핸들 생성
HANDLE hFile = CreateFile(
L"log.txt",
GENERIC_WRITE,
FILE_SHARE_WRITE,
&sa, // bInheritHandle = TRUE
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL
);
// 3. 핸들을 명시적으로 상속 가능 상태로 설정
SetHandleInformation(hFile, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT);
// 4. 자식 프로세스 생성 시 bInheritHandles 설정
STARTUPINFO si = { sizeof(STARTUPINFO) };
PROCESS_INFORMATION pi = { 0 };
CreateProcess(
L"ChildApp.exe",
NULL,
NULL,
NULL,
TRUE, // 🔴 bInheritHandles = TRUE
0,
NULL,
NULL,
&si,
&pi
);
이 코드에서는 log.txt 파일 핸들을 자식 프로세스로 넘기기 위해 필수 설정을 모두 적용했습니다.
자식 프로세스에서는 이 핸들을 통해 동일한 파일에 접근하거나 데이터를 쓸 수 있습니다.
파이프 또한 동일한 방식으로 상속할 수 있으며, 부모가 읽기용, 자식이 쓰기용 핸들을 갖게 구성하면 IPC 구현도 가능합니다.
💡 TIP: 파일이나 파이프뿐 아니라 콘솔 입력/출력 핸들도 이 방식으로 상속 가능하므로, 자식 프로세스에 로그 출력용 stdout을 전달할 수도 있습니다.
💡 핸들 상속 시 흔히 발생하는 실수
이제까지 핸들 상속의 개념부터 실제 구현 방법까지 살펴봤다면, 마지막으로는 실무에서 자주 발생하는 실수를 짚고 넘어가는 것이 좋습니다.
아무리 정확하게 구현했더라도, 아래와 같은 실수가 하나라도 있으면 자식 프로세스에서 핸들이 정상 작동하지 않거나 예기치 못한 오류가 발생할 수 있습니다.
-
❌
SetHandleInformation을 호출하지 않고 bInheritHandles만 설정 -
❌
핸들 생성 시 보안 속성 구조체 없이 기본값 사용 -
❌
모든 핸들에 대해 무조건 상속 가능으로 설정 -
❌
자식 프로세스에서 핸들이 닫히지 않아 리소스 누수 발생
핸들을 상속할 때는 명시적으로 필요한 핸들만 상속 가능하게 설정하는 것이 안전한 습관입니다.
모든 핸들을 상속 가능하게 만들면, 의도치 않게 자식 프로세스가 리소스에 접근하거나 충돌이 발생할 수 있기 때문이죠.
또한 자식 프로세스에서 작업이 끝난 후에는 반드시 핸들을 CloseHandle을 통해 정리해야 합니다.
이를 생략하면 부모와 자식 모두 동일 핸들을 보유한 상태가 되어, 종료 시 리소스가 제대로 해제되지 않을 수 있습니다.
⚠️ 주의: 핸들 상속 처리는 보안적으로 민감할 수 있으므로, 민감한 자원(예: 네트워크 소켓, 보안 토큰 등)은 상속을 피하는 것이 좋습니다.
💎 핵심 포인트:
CreateProcess, SetHandleInformation, SECURITY_ATTRIBUTES 이 3가지를 정확히 이해하고 적절히 조합해야만 핸들 상속이 올바르게 작동합니다.
❓ 자주 묻는 질문 (FAQ)
CreateProcess에서 bInheritHandles를 TRUE로만 설정하면 핸들이 자동 상속되나요?
SetHandleInformation 대신 DuplicateHandle로 핸들 상속이 가능한가요?
파일 핸들 말고도 어떤 핸들을 상속할 수 있나요?
핸들을 상속받은 자식 프로세스에서 반드시 CloseHandle을 해야 하나요?
SetHandleInformation을 호출하지 않으면 무슨 문제가 생기나요?
보안 속성 구조체는 꼭 사용해야 하나요?
상속 가능한 핸들을 추적하거나 검사할 방법이 있나요?
모든 핸들을 자식에게 상속하면 안 되나요?
🧩 WinAPI로 핸들 상속 처리하는 안전한 방법 요약
Windows에서 자식 프로세스에게 파일 핸들이나 파이프를 전달하려면, 단순히 CreateProcess를 호출하는 것만으로는 충분하지 않습니다.
핸들에 명시적으로 HANDLE_FLAG_INHERIT 플래그를 설정하고, 보안 속성 구조체에서 bInheritHandle을 TRUE로 지정하며, CreateProcess 호출 시 bInheritHandles 인자도 TRUE로 설정해야 비로소 정상적인 상속이 이뤄집니다.
핸들 상속 처리를 잘못하면 자식 프로세스가 자원에 접근하지 못하거나, 반대로 너무 많은 리소스를 불필요하게 공유하게 되어 보안상 취약점이 될 수 있습니다.
따라서 필요한 핸들만 선별해서 안전하게 상속하고, 사용이 끝난 후에는 반드시 CloseHandle로 정리하는 습관이 중요합니다.
이 글에서 소개한 SetHandleInformation, SECURITY_ATTRIBUTES, bInheritHandles 조합은 WinAPI 기반 시스템 프로그래밍에서 매우 중요한 기초이며, 안정적인 프로세스 구조를 설계하는 데 필수적인 요소입니다.
🏷️ 관련 태그 :
winapi, 핸들상속, CreateProcess, SetHandleInformation, 파이프통신, 파일핸들전달, 프로세스제어, 시스템프로그래밍, WindowsAPI, 보안설정