Pregunta

I created a Windows service and I need to capture desktop screen, but the result image is black. I know that a windows service needs to assign to winsat0/default desktop. I use user32.dll functions to interact with the user desktop but it doesn't work!

My code in Desktop class is this:

internal bool BeginInteraction()
{
    EndInteraction();
    m_hCurWinsta = User32DLL.GetProcessWindowStation();
    if (m_hCurWinsta == IntPtr.Zero)
        return false;

    m_hCurDesktop = User32DLL.GetDesktopWindow();
    if (m_hCurDesktop == IntPtr.Zero)
        return false;

    m_hWinsta = User32DLL.OpenWindowStation("Winsta0", false,
        WindowStationAccessRight.WINSTA_ACCESSCLIPBOARD |
        WindowStationAccessRight.WINSTA_ACCESSGLOBALATOMS |
        WindowStationAccessRight.WINSTA_CREATEDESKTOP |
        WindowStationAccessRight.WINSTA_ENUMDESKTOPS |
        WindowStationAccessRight.WINSTA_ENUMERATE |
        WindowStationAccessRight.WINSTA_EXITWINDOWS |
        WindowStationAccessRight.WINSTA_READATTRIBUTES |
        WindowStationAccessRight.WINSTA_READSCREEN |
        WindowStationAccessRight.WINSTA_WRITEATTRIBUTES
        );
    if (m_hWinsta == IntPtr.Zero)
        return false;

    User32DLL.SetProcessWindowStation(m_hWinsta);

    m_hDesk = User32DLL.OpenDesktop("default", OpenDesktopFlag.DF_NONE, false,
        DesktopAccessRight.DESKTOP_CREATEMENU |
        DesktopAccessRight.DESKTOP_CREATEWINDOW |
        DesktopAccessRight.DESKTOP_ENUMERATE |
        DesktopAccessRight.DESKTOP_HOOKCONTROL |
        DesktopAccessRight.DESKTOP_JOURNALPLAYBACK |
        DesktopAccessRight.DESKTOP_JOURNALRECORD |
        DesktopAccessRight.DESKTOP_READOBJECTS |
        DesktopAccessRight.DESKTOP_SWITCHDESKTOP |
        DesktopAccessRight.DESKTOP_WRITEOBJECTS
        );
    if (m_hDesk == IntPtr.Zero)
        return false;

    User32DLL.SetThreadDesktop(m_hDesk);

    return true;
}

The function to get capture is this:

public static bool Trig1() // ScreenShot
{
    Desktop userDesk = new Desktop();
    if (!userDesk.BeginInteraction())
        return false;

    string path = @"C:\";
    if (!Directory.Exists(path))
        Directory.CreateDirectory(path);

    string fileName = string.Format("SCR-{0:yyyy-MM-dd_hh-mm-ss-tt}.png", DateTime.Now);
    string filePath = path + fileName;
    bmpScreenshot = CaptureScreen.GetDesktopImage();
    bmpScreenshot.Save(filePath, ImageFormat.Png);
    userDesk.EndInteraction();
    return true;
}

The capture class is this:

public class CaptureScreen
{
    #region Public Class Functions
    public static Bitmap GetDesktopImage()
    {
        //Variable to keep the handle of the btimap.
        IntPtr m_HBitmap = new IntPtr();

        //Variable to keep the refrence to the desktop bitmap.
        System.Drawing.Bitmap bmp = null;

        //In size variable we shall keep the size of the screen.
        SIZE size;

        //Here we get the handle to the desktop device context.
        IntPtr hDC = PlatformInvokeUSER32.GetDC(PlatformInvokeUSER32.GetDesktopWindow());

        //Here we make a compatible device context in memory for screen device context.
        IntPtr hMemDC = PlatformInvokeGDI32.CreateCompatibleDC(hDC);

        //We pass SM_CXSCREEN constant to GetSystemMetrics to get the X coordinates of screen.
        size.cx = PlatformInvokeUSER32.GetSystemMetrics(PlatformInvokeUSER32.SM_CXSCREEN);

        //We pass SM_CYSCREEN constant to GetSystemMetrics to get the Y coordinates of screen.
        size.cy = PlatformInvokeUSER32.GetSystemMetrics(PlatformInvokeUSER32.SM_CYSCREEN);

        //We create a compatible bitmap of screen size and using screen device context.
        m_HBitmap = PlatformInvokeGDI32.CreateCompatibleBitmap(hDC, size.cx, size.cy);

        //As m_HBitmap is IntPtr we can not check it against null. For this purspose IntPtr.Zero is used.
        if (m_HBitmap != IntPtr.Zero)
        {
            //Here we select the compatible bitmap in memeory device context and keeps the refrence to Old bitmap.
            IntPtr hOld = (IntPtr)PlatformInvokeGDI32.SelectObject(hMemDC, m_HBitmap);

            //We copy the Bitmap to the memory device context.
            PlatformInvokeGDI32.BitBlt(hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, PlatformInvokeGDI32.SRCCOPY);

            //We select the old bitmap back to the memory device context.
            PlatformInvokeGDI32.SelectObject(hMemDC, hOld);

            //We delete the memory device context.
            PlatformInvokeGDI32.DeleteDC(hMemDC);

            //We release the screen device context.
            PlatformInvokeUSER32.ReleaseDC(PlatformInvokeUSER32.GetDesktopWindow(), hDC);

            //Image is created by Image bitmap handle and assigned to Bitmap variable.
            bmp = System.Drawing.Image.FromHbitmap(m_HBitmap);

            //Delete the compatible bitmap object. 
            PlatformInvokeGDI32.DeleteObject(m_HBitmap);

            return bmp;
        }

        //If m_HBitmap is null retunrn null.
        return null;
    }
    #endregion
}

But the image is black.

¿Fue útil?

Solución

Your process is running in a different session than the interactive user at the console. You're capturing the desktop of WinSta0 in your session and since nobody has displayed anything there, it's coming out black. See the MS docs at http://msdn.microsoft.com/en-us/library/windows/desktop/ms687096%28v=vs.85%29.aspx for more.

As of NT6 (Vista / Server 2008 and newer), services are no longer allowed to interact with user sessions. They can still create windows / message boxes, but they will always be on the reserved session 0 (if there is an active interactive user on the console they will get the "A service is trying to display a message" popup and have the option of temporarily switching to and viewing session 0).

If you want to capture a user's desktop, you must create a process running in their session.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top