[C#] ProcessStartInfo.WindowStyle = Hidden vs. CreateNoWindow = true

김정환(John.Kim)
OldbeeDev
Published in
5 min readFeb 17, 2020
http://www.firststeps.ru/mfc/winapi/r.php?131

외부 프로그램을 실행시키는 가장 간단한 방법은 Process.Start(파일경로)를 호출하는 것입니다. 그리고 외부 프로그램을 숨김 상태로 실행시키는 방법은 앞선 문서에서 설명한 바와 같이 WindowStyle=Hidden 옵션을 사용하는 것입니다. 숨김 상태로 실행하는 또 다른 방법도 있는데 바로 CreateNoWindow 옵션을 사용하는 것입니다.

CreateNoWindow=true가 적용되려면 반드시 UseShellExecute=false여야 한다고 합니다. 이 둘의 차이점을 검색해 보면 여러 가지가 나옵니다만, 말로는 잘 이해가 안되서 소스 코드를 직접 확인해 보았습니다.

.NET Reference Source

소스 코드의 위치는 아래와 같습니다.

https://raw.githubusercontent.com/microsoft/referencesource/master/System/services/monitoring/system/diagnosticts/Process.cs

Process.cs를 살펴보면 PeakMemorySize, Standard Input/Output/Error, processorAffinity 등의 어려운(?) 용어들이 나오지만 제 관심사는 아닌 것 같아서 모두 스킵하고 곧장 Start() 메소드를 살펴봅니다.

UseShellExecute 값에 따라 서로 다른 메소드가 호출됩니다. 이름만 봐서는 한 놈은 ShellExecute를 사용하는 것이고 다른 한 놈은 CreateProcess를 사용하는 것 같습니다.

StartWithShellExecuteEx

StartWithShellExecuteEx() 메소드의 대략적인 흐름은 다음과 같습니다.

  1. 사전 조건 체크 (사용자 권한, Pipe연결, 환경 변수)
  2. Window Style 적용
  3. 실행 파일 문자열 및 아규먼트 문자열 처리
  4. ShellExecuteEx API가 호출됨
  5. 기타 뒷 처리

UseShellExecute=true일 때는 ShellExecuteEx API가 사용되며, ProcessWindowStyle.Hidden 값은 이 API와 연관된 파라미터임을 알 수 있습니다.

StartWithCreateProcess

StartWithCreateProcess() 메소드의 코드 구조는 다음과 같습니다.

  1. command line 문자열 생성
  2. STARTUPINFO 구조체 생성
  3. 이 부분에서 CreateNoWindow 옵션 처리
    if (startInfo.CreateNoWindow)
    creationFlags |= NativeMethods.CREATE_NO_WINDOW;
  4. CreateProcess 실행
    UserName이 입력되었으면 CreateProcessWithLogon API가 호출됨,
    그 외에는 CreateProcess API가 호출됨
  5. 기타 뒷 처리

UseShellExecute=false일 때는 CreateProcess API가 사용되며 CreateNoWindow 옵션은 이 API와 관련된 파라미터임을 알 수 있습니다.

성능 비교

Version1과 Version2의 성능 차이가 궁금해서 간단히 테스트를 해봤습니다. 프로세스를 100개씩 생성하는데 걸리는 시간을 측정했습니다.

Results

StartWithHiddenStyle running time = 2187 ms
StartWithCreateNoWindow running time = 696 ms

StartWithHiddenStyle running time = 4538 ms
StartWithCreateNoWindow running time = 676 ms

StartWithHiddenStyle running time = 4151 ms
StartWithCreateNoWindow running time = 680 ms

StartWithHiddenStyle running time = 3669 ms
StartWithCreateNoWindow running time = 708 ms

StartWithHiddenStyle running time = 3005 ms
StartWithCreateNoWindow running time = 760 ms

대략 StartWithCreateNoWindow가 3배~6배 정도 빠릅니다. CreateProcess API가 더 Low Level이므로 당연한 결과일 것입니다.

.NET Core에서는?

ShellExecuteEx는 윈도우 셸 API 인데 linux 기반에서 돌까? 하는 궁금증이 생겼는데 조금 찾아보니 과연 여러 이야기가 있는 것 같습니다. 이 부분도 소스를 확인해보면 좀 더 구체적으로 알 수 있을 것입니다.

https://github.com/dotnet/runtime/tree/master/src/libraries/System.Diagnostics.Process/src/System/Diagnostics

그러나 지금은 제가 시간이 없으므로 다음에 정리하도록 하겠습니다. ;)

끝!

--

--