服务是“允许服务与桌面交互”。
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, SvcMgr, Dialogs;
type
TCopyDesk = class(TService)
procedure ServiceContinue(Sender: TService; var Continued: Boolean);
procedure ServiceExecute(Sender: TService);
procedure ServicePause(Sender: TService; var Paused: Boolean);
procedure ServiceShutdown(Sender: TService);
procedure ServiceStart(Sender: TService; var Started: Boolean);
procedure ServiceStop(Sender: TService; var Stopped: Boolean);
private
procedure CopyScreen(const Index: Integer);
public
function GetServiceController: TServiceController; override;
end;
var
CopyDesk: TCopyDesk;
implementation
{$R *.DFM}
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
CopyDesk.Controller(CtrlCode);
end;
procedure TCopyDesk.CopyScreen(const Index: Integer);
const
DefaultWindowStation = 'WinSta0';
DefaultDesktop = 'Default';
CAPTUREBLT = $40000000;
WINSTA_ALL_ACCESS = $0000037f;
var
Bmp: TBitmap;
hwinstaSave: HWINSTA;
hdeskSave: HDESK;
hwinstaUser: HWINSTA;
hdeskUser: HDESK;
dwThreadId: DWORD;
hdcScreen : HDC;
hdcCompatible : HDC;
hbmScreen : HBITMAP;
begin
hwinstaUser:= OpenWindowStation(DefaultWindowStation, FALSE, WINSTA_ALL_ACCESS);
hwinstaSave:= GetProcessWindowStation;
if hwinstaUser = 0 then
begin
OutputDebugString(PChar('OpenWindowStation failed' + SysErrorMessage (GetLastError)));
exit;
end;
if not SetProcessWindowStation(hwinstaUser) then
begin
OutputDebugString('SetProcessWindowStation failed');
exit;
end;
// hdeskUser:= OpenDesktop(DefaultDesktop, 0, FALSE, MAXIMUM_ALLOWED);
hdeskUser:= OpenInputDesktop(0, False, MAXIMUM_ALLOWED);
if hdeskUser = 0 then
begin
OutputDebugString('OpenDesktop failed');
SetProcessWindowStation (hwinstaSave);
CloseWindowStation (hwinstaUser);
exit;
end;
dwThreadId:= GetCurrentThreadID;
hdeskSave:= GetThreadDesktop(dwThreadId);
if not SetThreadDesktop(hdeskUser) then
begin
OutputDebugString(PChar('SetThreadDesktop' + SysErrorMessage(GetLastError)));
Exit;
end;
try
hdcScreen := GetDC(0);//GetDC(GetDesktopWindow);//CreateDC('DISPLAY', nil, nil, nil);
hdcCompatible := CreateCompatibleDC(hdcScreen);
hbmScreen := CreateCompatibleBitmap(hdcScreen,
GetDeviceCaps(hdcScreen, HORZRES),
GetDeviceCaps(hdcScreen, VERTRES));
SelectObject(hdcCompatible, hbmScreen);
bmp:= TBitmap.Create;
bmp.Handle:= hbmScreen;
BitBlt(hdcCompatible, 0,0, bmp.Width, bmp.Height, hdcScreen, 0,0, SRCCOPY OR CAPTUREBLT);
Bmp.SaveToFile('C:\Users\Public\ScreenShot\' + IntToStr(Index) + '.bmp');
finally
DeleteDC(hdcScreen);
DeleteDC(hdcCompatible);
Bmp.Free;
Bmp:= nil;
end;
SetThreadDesktop(hdeskSave);
SetProcessWindowStation(hwinstaSave);
if hwinstaUser <> 0 then
CloseWindowStation(hwinstaUser);
if hdeskUser <> 0 then
CloseDesktop(hdeskUser);
end;
function TCopyDesk.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TCopyDesk.ServiceContinue(Sender: TService;
var Continued: Boolean);
begin
while not Terminated do
begin
Sleep(10);
ServiceThread.ProcessRequests(False);
end;
end;
procedure TCopyDesk.ServiceExecute(Sender: TService);
var
Index: Integer;
begin
Index:= 0;
while not Terminated do
begin
CopyScreen(Index);
Inc(Index);
ServiceThread.ProcessRequests(False);
// Sleep(1000);
// if Index = 4 then
DoStop;
end;
end;
procedure TCopyDesk.ServicePause(Sender: TService; var Paused: Boolean);
begin
Paused:= True;
end;
procedure TCopyDesk.ServiceShutdown(Sender: TService);
begin
Status:= csStopped;
ReportStatus();
end;
procedure TCopyDesk.ServiceStart(Sender: TService; var Started: Boolean);
begin
Started:= True;
end;
procedure TCopyDesk.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
Stopped:= True;
end;
end.
答案 0 :(得分:15)
在Vista及更高版本中,服务将无法截取屏幕截图或以其他方式与桌面交互 - 不再支持“允许服务与桌面交互”。服务在无法与桌面交互的隔离会话中运行。有关详细信息,请阅读“session 0 isolation”。
有关原因的更多背景,this thread explains:
由于终端服务或远程桌面连接正在运行多个会话,因此服务与具有一个桌面的交互式窗口站之间没有一对一的关系。每个交互式会话可以有一个。服务应该与哪个人交谈?如果没有人查看你的服务运行的机器的任何桌面怎么办 - 没有人注意到消息框或任何UI的东西。
依靠这个“功能”不再适用。摆脱它,没有其他选择。
答案 1 :(得分:5)
作为Joe White回答的补充:
现在同时拥有服务和UI的大多数应用都分为多个流程:至少一个服务和至少一个(自动启动)UI流程。
这些进程通过IPC和Synchronization对象彼此通信,如(命名)管道,(内存映射)文件,邮件插槽,队列,事件,互斥锁,信号量等。请注意,这些对象中有一些重叠(一些被视为IPC,另一些更像是同步)。 Windows的一个良好开端是MSDN Inteprocess Communications page。
这是Input Director的工作原理。它由以下过程组成:
Number 1.作为服务流程运行并加载2.
Number 3.作为UI进程运行并加载4。
通过Process Explorer Process Monitor和SysInternals来观察这些互动方式的绝佳方式。
答案 2 :(得分:0)
1-简单方法,创建一个单独的DLL并将屏幕捕获代码放入其中,然后使用CreateRemoteThread在用户进程(DLL注入)中加载DLL(例如,explorer.exe)。以下是DLL注入的例子:
var
PID: Cardinal;
DLL_Name: string;
pDLL: Pointer;
hProcess, BW: Cardinal ;
hRemote_Thread: Cardinal;
begin
DLL_Name := 'C:\ScreenCap.dll';
PID := 3052; // (explorer.exe process ID)
hProcess := OpenProcess(PROCESS_ALL_ACCESS, false, PID);
pDLL := VirtualAllocEx(hProcess, 0, Length(DLL_Name), MEM_COMMIT, PAGE_EXECUTE_READWRITE);
WriteProcessMemory(hProcess, pDLL, PChar(DLL_Name), Length(DLL_Name), BW);
CreateRemoteThread(hProcess, nil, 0, GetProcAddress(GetModuleHandle('kernel32.dll'), 'LoadLibraryA'), pDLL, 0, hRemote_Thread);
CloseHandle(hProzess);
end;
这是针对unicode的非unicode Delphi版本(&lt; 2009) 应该将DLL_Name的长度乘以Char的大小 (VirtualAllocEx和)中的长度(DLL_Name)* SizeOf(Char)) WriteProcessMemory,你可以使用'LoadLibraryW'。
当你的服务启动它注入DLL并开始捕获时,我建议你在DLL中放入检查你的服务状态的代码以便相应地运行,你可以使用多个线程但要小心你不应该在DLLMain,因为它可能会导致死锁。
2-很难,你可以在没有创建单独的DLL的情况下为你注入整个代码,你可以查看这篇文章,它涵盖了所有内容,它是用C ++编写的,但它非常有用而且难以理解: