2

My company is using an unfortunate third party product that requires a user to be left logged on to an XP workstation running two instances of it's product.

I was trying to use this ...from within a windows service to get the titles of each of these open windows for monitoring purposes, but they always return null or empty. I can get the process names, however, just fine. The reason I'm interested in the titles and not the process names is that the process names are the same (two instances of the same application) but the titles are unique. I also get the same result for any open window, an open instance of notepad for example.

The service is being run as the user running the two instances of the third party application.

So I'm just curious, why would I be able to get the process names, but not the titles? Does it need to have SERVICE_INTERACTIVE_PROCESS specified to see the titles for some reason, but not the names? (of course I wouldn't use that flag: http://blogs.msdn.com/b/larryosterman/archive/2005/09/14/466175.aspx)

My code:

static void check_Clients()
{
    Process[] processList = Process.GetProcesses();

    TextWriter t = new StreamWriter(@"C:\temp\svctesting.txt", true);

    foreach (Process process in processList)
    {
        if (!String.IsNullOrEmpty(process.MainWindowTitle))
        {
            // I never get to this point, titles are always null or empty
            t.WriteLine("Process Title:  " + process.MainWindowTitle);
        }
        else
        {
            // this works fine
            t.WriteLine("Process Name:  " + process.ProcessName);
        }
    }
    t.Close();
    t = null;
}

EDIT

I tried changing my code to this per some of the offered answers:

using System.Runtime.InteropServices;

...

[DllImport("user32.dll")]
static extern int GetWindowText(int hWnd, StringBuilder text, int count);

...

static void check_Clients()
{
    Process[] processList = Process.GetProcesses();

    TextWriter t = new StreamWriter(@"C:\temp\svctesting.txt", true);

    foreach (Process process in processList)
    {
        StringBuilder Buff = new StringBuilder(256);

        if (GetWindowText(process.MainWindowHandle.ToInt32(), Buff, 256) > 0)
        {
            t.WriteLine("Window Text:  " + Buff.ToString());
        }
        else
        {
            t.WriteLine("Process Name:  " + process.ProcessName);
        }
    }
    t.Close();
    t = null;
}

But I still get the same result, all process names, no window text.

EDIT 2

I also tried:

static void check_Clients()
{

    TextWriter t = new StreamWriter(@"C:\temp\svctesting.txt", true);

    IntPtr hWnd = WndSearcher.SearchForWindow("IEFrame", "pinvoke.net: EnumWindows");

    t.WriteLine(hWnd.ToString());

    t.Close();
    t = null;
}

Using the WndSearcher class from the pinvoke page offered below, and tried another version of the same that only compared title. I get 0 despite having the pinvoke page up in IE. I'm pretty much lost.

Community
  • 1
  • 1
Mike
  • 399
  • 2
  • 9
  • 19

3 Answers3

6

Services run in session 0, the non-interactive session for services. Desktop applications run in different sessions. Session 1 for the first interactive user, session 2 for the second and so on. Inside the sessions are window stations, and inside window stations are desktops.

Window stations are secured and isolated. That means that a process in one window station cannot access the objects of another. This means that your service cannot use GetWindowText or indeed any function or message to interact with a window in a different window station.

If you need to transmit this information between the interactive desktops and your service, then you need:

  1. A process running in the interactive desktop, and
  2. An IPC channel to communicate between that desktop process and the service.

In short, you are going to need to build a bit more scaffolding to get this to work.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Ok, what you have described makes sense to me. Thanks for the information. – Mike Jun 18 '14 at 20:48
  • @DavidHeffernan Does *allow service to interact with desktop* option allows service to access windows in other WindowStation ? – tchelidze Jul 08 '16 at 10:39
  • @tchelidze It allows you interact with windows in session 0. Which isn't very useful on account of session 0 being non-interactive. All it is useful for is old services that have not been updated that are poking at windows. They get to poke at them, even though no user can see them or interact with them. – David Heffernan Jul 08 '16 at 10:45
  • @DavidHeffernan What about [Impersonation](https://www.nuget.org/packages/SimpleImpersonation) as solution ? – tchelidze Jul 08 '16 at 10:56
  • @tchelidze What is the problem? – David Heffernan Jul 08 '16 at 11:45
  • @DavidHeffernan get active window class for process. *(from service)* – tchelidze Jul 08 '16 at 13:39
  • @tchelidze That makes no sense. Processes don't have active window class. Services cannot reach into different sessions' desktops. Why are you fighting. Every post you read tells you to work from a process in the same desktop as your target. – David Heffernan Jul 08 '16 at 13:42
  • @DavidHeffernan Process owns Threads, one of which is GUI thread which has [hwndActive](https://msdn.microsoft.com/en-us/library/windows/desktop/ms632604(v=vs.85).aspx) , i'm iterested in that window class. Problem is that i can't run multiple instance of that process on same machine, otherwise i could [start new process from service](http://www.codeproject.com/Articles/35773/Subverting-Vista-UAC-in-Both-and-bit-Archite) – tchelidze Jul 08 '16 at 14:56
  • @tchelidze You know what you need to do. Run a process in the same session as the target. – David Heffernan Jul 08 '16 at 14:57
  • @DavidHeffernan problem : `target` is not one, instead all the processes with name `x` (potentially run multiple instance in multiple session). Possible solution : Run all `helper` processes in all session and IPC to them for `hwndActive`. – tchelidze Jul 08 '16 at 15:00
  • @tchelidze possibleonly solution – David Heffernan Jul 08 '16 at 15:07
1

I don't think you can do that with the .net framework...

but you can with WinAPI: http://msdn.microsoft.com/en-us/library/ms633497%28v=vs.85%29.aspx

and you can call it from C# like so: http://www.pinvoke.net/default.aspx/user32/enumwindows.html

An important note: your process need to have permissions for this...


you'll also probably need this: http://msdn.microsoft.com/en-us/library/ms633520(v=vs.85).aspx http://www.pinvoke.net/default.aspx/user32.getwindowtext

AK_
  • 7,981
  • 7
  • 46
  • 78
0
using System.Runtime.InteropServices;
using System.Text;

...

[DllImport("user32.dll")]
static extern int GetWindowText(int hWnd, StringBuilder text, int count); 

...

Process[] processList = Process.GetProcesses();

List<string> windowTitles = new List<string>();

foreach (Process proc in processList)
{
    StringBuilder Buff = new StringBuilder(256);

    if (GetWindowText(proc.MainWindowHandle.ToInt32(), Buff, 256) > 0 )
    {
        windowTitles.Add(Buff.ToString());
    }
}
FodderZone
  • 863
  • 12
  • 27