【问题标题】:Windows service screen capture not workingWindows 服务屏幕截图不起作用
【发布时间】:2023-03-19 02:14:01
【问题描述】:

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

我在桌面类的代码是这样的:

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
}

但是图像是黑色的。

【问题讨论】:

  • 你认为也许是因为BeginInteraction中的第一行代码是EndInteraction?
  • 肯·怀特:不,我删除了第一行,但结果还是黑色..:(
  • 第一行是有一段时间我在BeginInteraction之后忘记了EndInteraction,所以当开始调用BeginInteraction时它首先关闭最后一个交互..

标签: c# windows winapi windows-services


【解决方案1】:

您的进程在与控制台上的交互式用户不同的会话中运行。您正在会话中捕获 WinSta0 的桌面,并且由于没有人在那里显示任何内容,因此它变成了黑色。有关更多信息,请参阅http://msdn.microsoft.com/en-us/library/windows/desktop/ms687096%28v=vs.85%29.aspx 的 MS 文档。

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

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

【讨论】:

  • 那是个坏消息..:(你说得对,我试图显示一个对话框,但出现了这条消息:“服务正在尝试显示一条消息”,所以我无法通过以下方式捕获桌面Windows 7 中的 Win-service 直到我切换到用户桌面会话,对吗?
  • 我如何在用户会话中运行 win-service(不是 win-form)?有可能吗?
猜你喜欢
  • 2010-11-03
  • 2018-05-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-10
  • 2014-03-15
相关资源
最近更新 更多