m.K*_*aki 0 c# windows winapi windows-services
我创建了一个 Windows 服务,需要捕获桌面屏幕,但结果图像是黑色的。我知道Windows服务需要分配给winsat0/默认桌面。我使用 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;
}
Run Code Online (Sandbox Code Playgroud)
获取捕获的函数是这样的:
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;
}
Run Code Online (Sandbox Code Playgroud)
捕获类是这样的:
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
}
Run Code Online (Sandbox Code Playgroud)
但图像是黑色的。
您的进程与控制台上的交互式用户运行在不同的会话中。您正在会话中捕获 WinSta0 的桌面,由于没有人在那里显示任何内容,因此它显示为黑色。请参阅 MS 文档:http://msdn.microsoft.com/en-us/library/windows/desktop/ms687096%28v=vs.85%29.aspx有关详细信息,
从 NT6(Vista / Server 2008 及更高版本)开始,不再允许服务与用户会话交互。他们仍然可以创建窗口/消息框,但它们将始终位于保留会话 0 上(如果控制台上有活动的交互式用户,他们将收到“服务正在尝试显示消息”弹出窗口,并且可以选择暂时切换到并查看会话 0)。
如果要捕获用户的桌面,则必须创建一个在其会话中运行的进程。