Why does GetWindowThreadProcessId return 0 when called from a service?

c#, pinvoke, winapi

Solution

A service runs in its own session, the infamous session 0 in Vista and Win7. That session isolates services from the user desktop, it runs in another session. Specifically to prevent a service that usually runs with a very privileged account (like LocalSystem) from interacting with the user. A security hole.

Accordingly, a service cannot see the window handles owned by another session.

Not sure why you are doing this but you typically need a helper program that presents a user interface and communicates with the service through an IPC mechanism like named pipes, sockets, .NET remoting or WCF. If you use a named pipe, prefix the pipe name with `"Global\"` so all sessions can see it.

Problem

When using the following class in a console application, and having at least one instance of Notepad running, `GetWindowThreadProcessId` correctly returns a non-zero thread id. However, if the same code is included in a Windows Service, `GetWindowThreadProcessId` always returns `0` and no exceptions are thrown. Changing the user the service launches under to be the same as the one running the console application didn't alter the result. What causes `GetWindowThreadProcessId` to return `0` even if it is provided with a valid hwnd? And why does it function differently in the console application and the service? Note: I am running Windows 7 32-bit and targeting .NET 3.5. ``` public class TestClass { [DllImport("user32.dll")] static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId); public void AttachToNotepad() { var processesToAttachTo = Process.GetProcessesByName("Notepad") foreach (var process in processesToAttachTo) { var threadID = GetWindowThreadProcessId(process.MainWindowHandle, IntPtr.Zero); .... } } } ``` Console Code: ``` class Program { static void Main(string[] args) { var testClass = new TestClass(); testClass.AttachToNotepad(); } } ``` Service Code: ``` public class TestService : ServiceBase { private TestClass testClass = new TestClass(); static void Main() { ServiceBase.Run(new TestService()); } protected override void OnStart(string[] args) { testClass.AttachToNotepad(); base.OnStart(args); } protected override void OnStop() { ... } } ```

Original source