【发布时间】:2016-12-07 02:58:04
【问题描述】:
我看到了 Stack Overflow 问题How to switch a process between default desktop and Winlogon desktop?。
我已经制作了一个创建控制台项目应用程序的最小测试用例,但SetThreadDesktop() 不会将我的程序切换到目标桌面。
为什么会这样?
program Project1;
{$APPTYPE CONSOLE}
{$R *.res}
uses
Winapi.Windows,
System.SysUtils,
Vcl.Graphics,
function RandomPassword(PLen: Integer): string;
var
str: string;
begin
Randomize;
str := 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
Result := '';
repeat
Result := Result + str[Random(Length(str)) + 1];
until (Length(Result) = PLen)
end;
procedure Print;
var
DCDesk: HDC;
bmp: TBitmap;
hmod, hmod2 : HMODULE;
BitBltAPI: function(DestDC: HDC; X, Y, Width, Height: Integer; SrcDC: HDC; XSrc, YSrc: Integer; Rop: DWORD): BOOL; stdcall;
GetWindowDCAPI: function(hWnd: HWND): HDC; stdcall;
begin
hmod := GetModuleHandle('Gdi32.dll');
hmod2:= GetModuleHandle('User32.dll');
if (hmod <> 0) and (hmod2 <> 0) then begin
bmp := TBitmap.Create;
bmp.Height := Screen.Height;
bmp.Width := Screen.Width;
GetWindowDCAPI := GetProcAddress(hmod2, 'GetWindowDC');
if (@GetWindowDCAPI <> nil) then begin
DCDesk := GetWindowDCAPI(GetDesktopWindow);
end;
BitBltAPI := GetProcAddress(hmod, 'BitBlt');
if (@BitBltAPI <> nil) then begin
BitBltAPI(bmp.Canvas.Handle, 0, 0, Screen.Width, Screen.Height, DCDesk, 0, 0, SRCCOPY);
bmp.SaveToFile('ScreenShot_------_' + RandomPassword(8) + '.bmp');
end;
ReleaseDC(GetDesktopWindow, DCDesk);
bmp.Free;
FreeLibrary(hmod);
FreeLibrary(hmod2);
end;
end;
//===============================================================================================================================
var
hWinsta, hdesktop:thandle;
begin
try
while True do
begin
hWinsta := OpenWindowStation('WinSta0', TRUE, GENERIC_ALL);
If hwinsta <> INVALID_HANDLE_VALUE then
begin
SetProcessWindowStation (hWinsta);
hdesktop := OpenDesktop ('default_set', 0, TRUE, GENERIC_ALL);
if (hdesktop <> INVALID_HANDLE_VALUE) then
if SetThreadDesktop (hdesktop) then
begin
Print; // Captures screen of target desktop.
CloseWindowStation (hwinsta);
CloseDesktop (hdesktop);
end;
end;
Sleep(5000);
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
检查错误,当目标桌面打开时,SetThreadDesktop() 调用失败,错误代码为 170(ERROR_BUSY,请求的资源正在使用中)。
var
threahdesk: boolean;
...
threahdesk := SetThreadDesktop (hdesktop);
ShowMessage(IntToStr(GetLastError));
if threahdesk Then
begin
Print;
CloseWindowStation (hwinsta);
CloseDesktop (hdesktop);
end;
后来在一些论坛看到了一些建议,我的实际代码如下:
var
hWinsta, hdesktop:thandle;
threahdesk, setprocwst: Boolean;
////////////////////////////////////////////////////////////////////////////////
begin
try
while True do
begin
Application.Free;
hWinsta:= OpenWindowStation('WinSta0', TRUE, GENERIC_ALL);
If hwinsta <> 0 Then
Begin
setprocwst := SetProcessWindowStation(hWinsta);
if setprocwst then
hdesktop:= OpenDesktop('default_set', 0, TRUE, GENERIC_ALL);
If (hdesktop <> 0) Then
threahdesk := SetThreadDesktop(hdesktop);
Application := TApplication.Create(nil);
Application.Initialize;
Application.Run;
If threahdesk Then
Begin
Print;
CloseWindowStation (hwinsta);
CloseDesktop (hdesktop);
End;
End;
Sleep(5000);
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
【问题讨论】:
-
在上面链接的一个 cmets 中,据说他已经为测试用例创建了一个控制台应用程序并且工作正常。所以,我想知道为什么不在我上面的代码上工作? :-(
-
我编辑了我的问题。
-
在
SetThreadDesktop()退出后调用GetLastError()是无效的,除非它返回FALSE,而您没有检查它。否则,您可能会从较早的 API 调用中获取错误代码。此外,OpenDesktop()在失败时返回 0,而不是INVALID_HANDLE_VALUE(-1)。而且您没有检查SetProcessWindowStation()是否失败。你在错误的地方打电话给CloseWindowStation()和CloseDesktop()。为什么要动态加载GetWindowDC()和BitBlt(),而静态使用ReleaseDC()?您的代码充满了问题。 -
@RemyLebeau,我的截图功能工作正常。您的执行不存在问题。
-
@Saulo 我找不到我的测试用例,但我发现我 3 年前用 C++ 编写的应用程序可以在安全桌面上运行。我觉得可能对你有帮助,所以我把代码推送到了 GitHub。您可以通过此链接访问它:https://github.com/DeviLeo/TrackPadSimulator。我不记得最详细的信息,但我确定我启动了一个服务来启动一个守护进程来处理安全桌面上的键盘和鼠标操作。
标签: delphi