为了捕获屏幕,您需要在用户会话中运行程序。这是因为如果没有用户,就无法关联桌面。
为了解决这个问题,您可以运行桌面应用程序来拍摄图像,可以在活动用户的会话中调用该应用程序,这可以通过服务来完成。
下面的代码允许您调用桌面应用程序,使其在本地用户的桌面上运行。
如果需要以特定用户身份执行,请查看文章中的代码允许服务与桌面交互吗?哎哟。 http://asprosys.blogspot.com/2009/03/allow-service-to-interact-with-desktop.html。您还可以考虑使用该功能登录用户 http://msdn.microsoft.com/en-us/library/windows/desktop/aa378184%28v=vs.85%29.aspx.
代码:
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;
}
}
此代码是对文章中找到的代码的修改允许服务与桌面交互吗?哎哟。 http://asprosys.blogspot.com/2009/03/allow-service-to-interact-with-desktop.html(必读)
附录:
上面的代码允许在本地登录到计算机的用户的桌面上执行程序。此方法特定于当前本地用户,但可以为任何用户执行此操作。检查文章中的代码允许服务与桌面交互吗?哎哟。 http://asprosys.blogspot.com/2009/03/allow-service-to-interact-with-desktop.html举个例子。
这个方法的核心是函数创建进程为用户 http://msdn.microsoft.com/en-us/library/windows/desktop/ms682429%28v=vs.85%29.aspx,您可以在以下位置找到更多信息:MSDN http://msdn.microsoft.com/en-us/library/windows/desktop/ms682429%28v=vs.85%29.aspx.
Replace "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 http://msdn.microsoft.com/en-us/library/system.reflection.assembly.location.aspx作为可执行路径。然后你可以使用System.Environment.UserInteractive http://msdn.microsoft.com/en-us/library/system.environment.userinteractive.aspx检测可执行文件是否未作为服务运行,并将有关所需执行的任务的信息作为执行参数传递。在此答案的上下文中,即捕获屏幕(例如CopyFromScreen http://msdn.microsoft.com/en-us/library/system.drawing.graphics.copyfromscreen.aspx),也可能是别的东西。