tags:

views:

488

answers:

3

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()
    {
        ...
    }
}
A: 

A Windows Service has no UI, so it has no window.

John Saunders
But I'm querying the thread ID of an instance of notepad.exe, not the service itself.
Marve
+7  A: 

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.

Hans Passant
Thanks for the detailed answer. On further review, its probably a better design decision to run my code in an application instead of a service.
Marve
A: 

You could also enable the option "Allow service to interact with desktop" and see if that works. Otherwise I would have to agree with onbugz comment above.

Black Frog
Thanks for the suggestion. I did try that before posting and it didn't seem to affect my scenario.
Marve