Windows服务屏幕捕获无法正常工作

时间:2014-05-09 15:45:49

标签: c# windows winapi windows-services

我创建了一个Windows服务,我需要捕获桌面屏幕,但结果图像是黑色的。我知道Windows服务需要分配给winsat0 / default桌面。我使用user32.dll函数与用户桌面进行交互,但它不起作用!

我在Desktop类中的代码是这样的:

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;
}

获取捕获的功能是:

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;
}

捕获类是这样的:

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
}

但是图像是黑色的。

1 个答案:

答案 0 :(得分:4)

您的进程在与控制台上的交互式用户不同的会话中运行。你正在你的会话中捕获WinSta0的桌面,因为没有人在那里显示任何东西,它会变黑。有关详情,请参阅http://msdn.microsoft.com/en-us/library/windows/desktop/ms687096%28v=vs.85%29.aspx上的MS文档。

从NT6(Vista / Server 2008及更新版本)开始,不再允许服务与用户会话进行交互。他们仍然可以创建窗口/消息框,但它们将始终位于保留的会话0上(如果控制台上有活动的交互式用户,他们将获得“服务正在尝试显示消息”弹出窗口并且可以选择暂时切换到并查看会话0)。

如果要捕获用户的桌面,则必须创建在其会话中运行的进程。