gpt4 book ai didi

c# - 在服务器桌面 session 上捕获屏幕

转载 作者:IT王子 更新时间:2023-10-29 04:44:40 25 4
gpt4 key购买 nike

我开发了一个 GUI 测试框架,可以定期对我们公司的网站进行集成测试。当出现故障时,它会截取桌面的屏幕截图等。这在专用 Windows Server 2008 上的登录用户无人值守的情况下运行。

问题 是在我已断开远程桌面 session 连接的桌面上截取屏幕截图。我得到以下异常:

System.ComponentModel.Win32Exception (0x80004005): The handle is invalid     
at System.Drawing.Graphics.CopyFromScreen(Int32 sourceX, Int32 sourceY, Int32 destinationX, Int32 destinationY, Size blockRegionSize, CopyPixelOperation copyPixelOperation)
at System.Drawing.Graphics.CopyFromScreen(Point upperLeftSource, Point upperLeftDestination, Size blockRegionSize)
at IntegrationTester.TestCaseRunner.TakeScreenshot(String name) in C:\VS2010\IntegrationTester\IntegrationTester\Config\TestCaseRunner.cs:line 144
at IntegrationTester.TestCaseRunner.StartTest() in C:\VS2010\IntegrationTester\IntegrationTester\Config\TestCaseRunner.cs:line 96

TakeScreenshot() 方法如下所示:

public static void TakeScreenshot(string name)
{
var bounds = Screen.GetBounds(Point.Empty);
using (Bitmap bitmap = new Bitmap(bounds.Width, bounds.Height))
{
using (Graphics g = Graphics.FromImage(bitmap))
{
g.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size);
}
bitmap.Save("someFileName", ImageFormat.Jpeg);
}
}

我已确保屏幕保护程序设置为“无”且没有超时。我还实现了一段代码,它执行几个 pinvokes 以发送鼠标移动,希望它会生成桌面图形句柄..但没有。

IntPtr hWnd = GetForegroundWindow();
if (hWnd != IntPtr.Zero)
SendMessage(hWnd, 0x200, IntPtr.Zero, IntPtr.Zero);

如有任何建议,我们将不胜感激。

最佳答案

为了捕获屏幕,您需要在用户 session 中运行一个程序。那是因为没有用户就无法关联桌面。

要解决这个问题,您可以运行一个桌面应用程序来拍摄图像,这个应用程序可以在事件用户的 session 中调用,这可以通过服务完成。

下面的代码允许您以在本地用户桌面上运行的方式调用桌面应用程序。

如果需要以特定用户身份执行,请查看文章Allow service to interact with desktop? Ouch.中的代码.您还可以考虑使用函数 LogonUser .

代码:

public void Execute()
{
IntPtr sessionTokenHandle = IntPtr.Zero;
try
{
sessionTokenHandle = SessionFinder.GetLocalInteractiveSession();
if (sessionTokenHandle != IntPtr.Zero)
{
ProcessLauncher.StartProcessAsUser("Executable Path", "Command Line", "Working Directory", sessionTokenHandle);
}
}
catch
{
//What are we gonna do?
}
finally
{
if (sessionTokenHandle != IntPtr.Zero)
{
NativeMethods.CloseHandle(sessionTokenHandle);
}
}
}

internal static class SessionFinder
{
private const int INT_ConsoleSession = -1;

internal static IntPtr GetLocalInteractiveSession()
{
IntPtr tokenHandle = IntPtr.Zero;
int sessionID = NativeMethods.WTSGetActiveConsoleSessionId();
if (sessionID != INT_ConsoleSession)
{
if (!NativeMethods.WTSQueryUserToken(sessionID, out tokenHandle))
{
throw new System.ComponentModel.Win32Exception();
}
}
return tokenHandle;
}
}

internal static class ProcessLauncher
{
internal static void StartProcessAsUser(string executablePath, string commandline, string workingDirectory, IntPtr sessionTokenHandle)
{
var processInformation = new NativeMethods.PROCESS_INFORMATION();
try
{
var startupInformation = new NativeMethods.STARTUPINFO();
startupInformation.length = Marshal.SizeOf(startupInformation);
startupInformation.desktop = string.Empty;
bool result = NativeMethods.CreateProcessAsUser
(
sessionTokenHandle,
executablePath,
commandline,
IntPtr.Zero,
IntPtr.Zero,
false,
0,
IntPtr.Zero,
workingDirectory,
ref startupInformation,
ref processInformation
);
if (!result)
{
int error = Marshal.GetLastWin32Error();
string message = string.Format("CreateProcessAsUser Error: {0}", error);
throw new ApplicationException(message);
}
}
finally
{
if (processInformation.processHandle != IntPtr.Zero)
{
NativeMethods.CloseHandle(processInformation.processHandle);
}
if (processInformation.threadHandle != IntPtr.Zero)
{
NativeMethods.CloseHandle(processInformation.threadHandle);
}
if (sessionTokenHandle != IntPtr.Zero)
{
NativeMethods.CloseHandle(sessionTokenHandle);
}
}
}
}

internal static class NativeMethods
{
[DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
internal static extern bool CloseHandle(IntPtr handle);

[DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
internal static extern bool CreateProcessAsUser(IntPtr tokenHandle, string applicationName, string commandLine, IntPtr processAttributes, IntPtr threadAttributes, bool inheritHandle, int creationFlags, IntPtr envrionment, string currentDirectory, ref STARTUPINFO startupInfo, ref PROCESS_INFORMATION processInformation);

[DllImport("Kernel32.dll", EntryPoint = "WTSGetActiveConsoleSessionId")]
internal static extern int WTSGetActiveConsoleSessionId();

[DllImport("WtsApi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool WTSQueryUserToken(int SessionId, out IntPtr phToken);

[StructLayout(LayoutKind.Sequential)]
internal struct PROCESS_INFORMATION
{
public IntPtr processHandle;
public IntPtr threadHandle;
public int processID;
public int threadID;
}

[StructLayout(LayoutKind.Sequential)]
internal struct STARTUPINFO
{
public int length;
public string reserved;
public string desktop;
public string title;
public int x;
public int y;
public int width;
public int height;
public int consoleColumns;
public int consoleRows;
public int consoleFillAttribute;
public int flags;
public short showWindow;
public short reserverd2;
public IntPtr reserved3;
public IntPtr stdInputHandle;
public IntPtr stdOutputHandle;
public IntPtr stdErrorHandle;
}
}

此代码是对文章 Allow service to interact with desktop? Ouch. 中的代码的修改(必读)


附录:

上面的代码允许在本地登录机器的用户的桌面上执行程序。此方法特定于当前本地用户,但可以对任何用户执行此操作。检查文章中的代码 Allow service to interact with desktop? Ouch.举个例子。

这个方法的核心是函数CreateProcessAsUser ,您可以在 MSDN 找到更多信息.

"Executable Path" 替换为要运行的可执行文件的路径。将 "Command Line" 替换为作为执行参数传递的字符串,并将 "Working Directory" 替换为您想要的工作目录。例如你可以提取可执行路径的文件夹:

    internal static string GetFolder(string path)
{
var folder = System.IO.Directory.GetParent(path).FullName;
if (!folder.EndsWith(System.IO.Path.DirectorySeparatorChar.ToString()))
{
folder += System.IO.Path.DirectorySeparatorChar;
}
return folder;
}

如果您有服务,您可以在服务中使用此代码来调用桌面应用程序。该桌面应用程序也可能是服务可执行文件...为此您可以使用 Assembly.GetExecutingAssembly().Location作为可执行路径。然后你可以使用System.Environment.UserInteractive检测可执行文件是否未作为服务运行,并将有关需要执行的任务的信息作为执行参数传递。在这个捕获屏幕的答案的上下文中(例如使用 CopyFromScreen ),它可能是别的东西。

关于c# - 在服务器桌面 session 上捕获屏幕,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5200341/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com