5

I'm trying to get the size of the Windows' desktop (the whole thing, not just a single screen) from inside a service that I've written.

In WinForms -- the standard C# method of:

SystemInformation.VirtualScreen.Width   
SystemInformation.VirtualScreen.Height

seems to work (if you import the Winforms DLL, which I want to avoid) -- but it returns the wrong value. The desktop size is 2048x768 (2 screens), but the service reports 1024x768 (presumably it only picks up on one of the screens.)

Checking the option for the service to interact with the desktop has no effect.

Any thoughts?

Edit:

The solutions posted at C#: Get complete desktop size? don't work inside of a service. They all report the wrong value.

Interestingly, it seems like the value that is reported varies and is of no relation to the actual desktop size (some machines report 800x600 even though a single display on that machine is a much higher resolution.)

So -- any more ideas? Dropping into the registry and/or to the command line is OK. The only restriction is that I can't launch a winforms app to figure it out.

Community
  • 1
  • 1
debracey
  • 6,517
  • 1
  • 30
  • 56
  • 1
    Did you take into account the fact that the service might be running on a headless system? Or there are simply no users logged in? – Dmitry Feb 29 '12 at 22:29
  • I hope this http://stackoverflow.com/questions/1317235/c-get-complete-desktop-size will help you. – Andrei Schneider Feb 29 '12 at 22:29
  • I should clarify. Headless systems are not a concern in this setup. Secondly, I am attaching to the login event -- so I know when to attempt to figure out the desktop size (only after someone logs in.) – debracey Feb 29 '12 at 22:39
  • You can also have multiple users logged in to system simultaneously, some of them locally some of them through RDC. They might have different resolutions and different number of monitors. – Dmitry Feb 29 '12 at 22:45

4 Answers4

3

You cannot do this inside the service. Services run in session 0, where no GDI functions work. Once the process is created, you cannot change sessions, and cannot use UI in different session.

One of the possible solutions for you is to launch a new process in user session. You can start looking at this SO question. The other pre-requisites for this method is that your service has to be running as Local System, so that you can enable SE_TCB_NAME privilege (by calling AdjustTokenPrivilegies). Since you are saying you already hooked up to the user logon notification, you should be able to extract session ID of the session that you are interested in.

Once you have a process lauched in user session, you have to pass the result from the new process back to your service process. For that any kind of IPC mechanism could be used.

Community
  • 1
  • 1
seva titov
  • 11,720
  • 2
  • 35
  • 54
  • Thanks. I found an interesting post about querying WMI to figure out the desktop size (something like `"SELECT * FROM Win32_DesktopMonitor"` then `queryObj["ScreenWidth"]`). Do you know if that will work inside a service? Unfortunately Im not at work right now so I can't try it out --- I will try that and your suggestion tomorrow and report back. – debracey Mar 01 '12 at 03:21
  • Looks like Win32_DesktopMonitor gives you parameters of physical hardware, i.e. actually attached to video card with cord. It does not report screen sizes for the RDP sessions. It depends on your requirements -- do you need physical hardware specs or whatever screen is actually used by the user. – seva titov Mar 01 '12 at 03:36
  • That's a good point, I guess my original question wasn't clear. I care more about the maximum resolution the hardware can display. What the user actually sees might also be something useful to report -- but I think they care more about "what is the highest res I could show" – debracey Mar 01 '12 at 03:39
2

You could add the size of all screens to determine the total desktop size, as described by P Daddy in this post.

Rectangle rect = new Rectangle(int.MaxValue, 
                               int.MaxValue,
                               int.MinValue,
                               int.MinValue); 

foreach (Screen screen in Screen.AllScreens) 
{
    rect = Rectangle.Union(rect, screen.Bounds); 
}

Console.WriteLine("(w, h) = ({0}, {1})", rect.Width, rect.Height); 
Community
  • 1
  • 1
Gustavo Mori
  • 8,319
  • 3
  • 38
  • 52
  • Yeah ... I saw that one, doesn't work in a windows service. The result is the same -- 1024x768 when it should be 2048x768. – debracey Feb 29 '12 at 22:54
1

While I haven't encountered this exact problem myself I suspect that working with multiple monitors may require you to delve down to the Win32API level (a lot of edge cases do).

Have a look at the EnumDisplayMonitors function from user32, and specifically an example in C#.

Alternatively you can use Screen.AllScreens (as part of the Winforms DLL you don't want to import) as a quick check to see if what you want to do will be possible as a Windows Service. I am almost certain that this WinForms method relies on EnumDisplayMonitors internally.

cfeduke
  • 23,100
  • 10
  • 61
  • 65
0

The following code works in a service which I found in sharpAvi: https://sharpavi.codeplex.com/

        System.Windows.Media.Matrix toDevice;
        using (var source = new HwndSource(new HwndSourceParameters()))
        {
            toDevice = source.CompositionTarget.TransformToDevice;
        }
        screenWidth = (int)Math.Round(SystemParameters.PrimaryScreenWidth * toDevice.M11);
        screenHeight = (int)Math.Round(SystemParameters.PrimaryScreenHeight * toDevice.M22);
ickydime
  • 1,051
  • 10
  • 22