Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   auf richtigen InputDesktop prüfen (für GetCursorPos) (https://www.delphipraxis.net/155625-auf-richtigen-inputdesktop-pruefen-fuer-getcursorpos.html)

VizeTE 1. Nov 2010 15:26

auf richtigen InputDesktop prüfen (für GetCursorPos)
 
Hallo,

ich habe gerade gelernt das Mouse.Cursor (also GetCursorPos) eine Exception wirft wenn der Desktop der Anwendung nicht der InputDeskop ist.
Das MSDN schlägt mir vor mit OpenInputDesktop dieses zu prüfen. Aber wie stelle ich das an?

Ich habe folgendes probiert (Delphi 5):
Delphi-Quellcode:
function CheckDesktopIsActive: boolean;
var
  hProcDesktop : HDESK;
  hInputDesktop : HDESK;
begin
  Result       := false;
  hInputDesktop := 0;
  hProcDesktop := 0;

  try
    //Handle des Desktops auf dem das Programm läuft ermitteln
    hProcDesktop := GetThreadDesktop(GetCurrentThreadId);
    if hProcDesktop = 0 then
      Exit;

    //Handle des aktuellen Eingabe-Dektops ermitteln
    hInputDesktop := OpenInputDesktop(0, false, 0);

    Result := hProcDesktop = hInputDesktop;
  finally
    if hInputDesktop <> 0 then
      CloseDesktop(hInputDesktop);
  end;
end;
Aber das schlägt immer fehl weil "hInputDesktop <> hProcDesktop".

Oder reicht es etwa schon aus das OpenInputDesktop einen Wert ungleich 0 zurückliefert? Das würde bestimmt so sein wenn es einer Anwendung auf Desktop A nicht erlaubt ist Desktop B zu öffnen. Aber ob das so ist... keine Ahnung.

VizeTE 1. Nov 2010 16:13

AW: auf richtigen InputDesktop prüfen (für GetCursorPos)
 
Ich habe da mal ein Update:

OpenInputDesktop funktioniert beim Login tatsächlich. In diesen Fall liefert mir die Methode 0 zurück.
Wenn allerdings der Bildschirmschoner (mit Kennwortabfrage beim der Reaktivierung!) gestartet wird gibt die Funktion weiterhin einen Wert <> 0 zurück. Mouse.CursorPos liefert aber trotzdem eine Exception.

Dezipaitor 2. Nov 2010 09:12

AW: auf richtigen InputDesktop prüfen (für GetCursorPos)
 
Kennst du dich mit Zeigern aus?

Ist fast genauso, wie mit Handles (HDESK). Du vergleichst Speicheraddressen, um zu prüfen, ob der Inhalt derselbe ist.

Delphi-Quellcode:
var P1, P2 : PINTEGER;
begin
  ..
  P1^ := 1234;
  P2^ := 1234;

  if P1 = P2 then OK;

Dezipaitor 2. Nov 2010 09:20

AW: auf richtigen InputDesktop prüfen (für GetCursorPos)
 
Zitat:

Zitat von VizeTE (Beitrag 1058975)
Ich habe da mal ein Update:

OpenInputDesktop funktioniert beim Login tatsächlich. In diesen Fall liefert mir die Methode 0 zurück.
Wenn allerdings der Bildschirmschoner (mit Kennwortabfrage beim der Reaktivierung!) gestartet wird gibt die Funktion weiterhin einen Wert <> 0 zurück. Mouse.CursorPos liefert aber trotzdem eine Exception.

Nein, tut sie nicht.
Such mal die Funktion in der MSDN, da steht:

Zitat:

If the function fails, the return value is NULL (0). To get extended error information, call GetLastError.

VizeTE 2. Nov 2010 19:37

AW: auf richtigen InputDesktop prüfen (für GetCursorPos)
 
Zitat:

Zitat von Dezipaitor (Beitrag 1059108)
Kennst du dich mit Zeigern aus?

Geht so :zwinker:

Zitat:

Zitat von Dezipaitor (Beitrag 1059108)
Ist fast genauso, wie mit Handles (HDESK). Du vergleichst Speicheraddressen, um zu prüfen, ob der Inhalt derselbe ist.

Delphi-Quellcode:
var P1, P2 : PINTEGER;
begin
  ..
  P1^ := 1234;
  P2^ := 1234;

  if P1 = P2 then OK;

Das Beispiel verstehe ich nicht so recht. Korrigier' mich wenn ich mich täusche aber ein Handle ist doch kein Zeiger auf irgendetwas sondern eher eine Id für Windows um ein Objekt zu identifizieren. Also einfach eine Ganzzahl. Da kann ich doch nichts dereferenzieren?!
Die Handles zu vergleichen habe ich ja schon in meinem Beispiel bei der Threaderöffnung versucht.
Delphi-Quellcode:
Result := hProcDesktop = hInputDesktop;

Zitat:

Zitat von Dezipaitor (Beitrag 1059110)
Zitat:

Zitat von VizeTE (Beitrag 1058975)
Ich habe da mal ein Update:

OpenInputDesktop funktioniert beim Login tatsächlich. In diesen Fall liefert mir die Methode 0 zurück.
Wenn allerdings der Bildschirmschoner (mit Kennwortabfrage beim der Reaktivierung!) gestartet wird gibt die Funktion weiterhin einen Wert <> 0 zurück. Mouse.CursorPos liefert aber trotzdem eine Exception.

Nein, tut sie nicht.
Such mal die Funktion in der MSDN, da steht:

Zitat:

If the function fails, the return value is NULL (0). To get extended error information, call GetLastError.

Ich will nicht bestreiten das das dort steht aber ich habe es ausprobiert und während der Bildschirmschoner läuft schlägt die Funktion nicht fehl und es wird ein Wert <> 0 zurückgegeben. Erst wenn ich die Mouse bewege und somit auf den Anmeldeschirm lande gibt die Funktion 0 zurück.

Ich habe das getestet indem ich in meinem Programm einfach alle 5 Sek. die Funktion und die Cursorposition abgefragt und das Ergebnis in eine Logdatei geschrieben habe. Dann habe ich gewartet bis der Bildschirmschoner startet. Während der Bildschirmschoner läuft bekomme ich - wie oben beschrieben - einen Wert <> 0 zurück. Die Abfrage der CursorPosition liefert jedoch schon eine Exception.

Dezipaitor 3. Nov 2010 09:59

AW: auf richtigen InputDesktop prüfen (für GetCursorPos)
 
Zitat:

Das Beispiel verstehe ich nicht so recht. Korrigier' mich wenn ich mich täusche aber ein Handle ist doch kein Zeiger auf irgendetwas sondern eher eine Id für Windows um ein Objekt zu identifizieren. Also einfach eine Ganzzahl. Da kann ich doch nichts dereferenzieren?!
Die Handles zu vergleichen habe ich ja schon in meinem Beispiel bei der Threaderöffnung versucht.
Naja, sie sind keine Zeiger im Sinne von Speicherverwaltung. Aber Handles zeigen auf Objekte im Kernelstrukturen (Klassen) von Windows. Damit man sie nicht einfach ändern kann, bekommt man eben eine Nummer (ein Handle), die die Struktur eindeutig in deinem Prozess zuordbar macht. D.h. du kannst beliebige Handles anfordern, die auf dieselbe Struktur im Kernel zeigen. Die Nummer hat für dich keinerlei Bedeutung, sondern nur für Windows.
Um nochmal auf Pointer von Delphi zu kommen: Dort hast du in meinem Beispiel gesehen, dass die Werte dieselben sind. Das ist sozusagen dieselbe Kernelklasse. Nur die Zeiger P1 und P2 haben verschiedene Speicheraddresse. Würdest du jetzt da auf die Idee kommen und die Inhalte vergleichen wollen, indem zu P1 mit P2 direkt, als die Speicherstellen an denen sie stehen, vergleichst?

Du musst schon den Namen des Desktops deiner Anwendung herausfinden und dann vergleichen...Nutze dazu GetUserObjectInformation oder TJwSecurityDesktop

Wenn der Anmeldebildschirm oder UAC Bildschirm aktiv ist, dann kann eine normale Anwendung nicht mehr darauf zugreifen und jede API verweigert den Zugriff auf den Desktop. Sobald du 0 für HDESK von OpenInputDesktop zurückbekommst, hast du keinen Zugriff mehr auf den Desktop des Benutzers. Dass du trotzdem Zugriff auf den Desktop des Bildschirmschoner hast liegt daran, dass der Bildschirmschoner in einem eigenen Desktop (InputDesktop = Screensaver) gestartet wird, der Lesezugriff für den Benutzer zulässt (so kann man erkennen, dass der Bildschirmschoner aktiv ist). Sobald der Bildschirmschoner jedoch beendet wird, ist der Winlogon Desktop aktiv und eine normale Anwendung kann garnichts mehr damit anfangen (GetLastError = 5 = Access Denied)
Erst wenn der Benutzer sich mit seinem Passwort verifiziert, kann man den normalen Desktop (default) benutzen.

VizeTE 3. Nov 2010 15:12

AW: auf richtigen InputDesktop prüfen (für GetCursorPos)
 
Zitat:

Zitat von Dezipaitor (Beitrag 1059290)
...Handles zeigen auf Objekte im Kernelstrukturen (Klassen) von Windows. Damit man sie nicht einfach ändern kann, bekommt man eben eine Nummer (ein Handle), die die Struktur eindeutig in deinem Prozess zuordbar macht. D.h. du kannst beliebige Handles anfordern, die auf dieselbe Struktur im Kernel zeigen...

OK, wieder etwas dazu gelernt. Die Problematik mit Pointern ist mir durchaus bewusst. Ich bin aber davon ausgegangen das es pro Kernelstruktur nur ein Handle gibt und ich somit immer das selbe Handle bekomme wenn ich die selbe Struktur abfrage.
Zitat:

Zitat von Dezipaitor (Beitrag 1059290)
Du musst schon den Namen des Desktops deiner Anwendung herausfinden und dann vergleichen

Habe es mit GetUserObjectInformation gelöst. Ist ja ein überschaubarer Aufwand und scheint prima zu funktionieren.
Zitat:

Zitat von Dezipaitor (Beitrag 1059290)
...Dass du trotzdem Zugriff auf den Desktop des Bildschirmschoner hast liegt daran, dass der Bildschirmschoner in einem eigenen Desktop (InputDesktop = Screensaver) gestartet wird, der Lesezugriff für den Benutzer zulässt (so kann man erkennen, dass der Bildschirmschoner aktiv ist). Sobald der Bildschirmschoner jedoch beendet wird, ist der Winlogon Desktop aktiv und eine normale Anwendung kann garnichts mehr damit anfangen (GetLastError = 5 = Access Denied).

Der Punkt der mich überrascht hat war das ich auf den Screensaver Lesezugriff habe aber dennoch nicht die Position der Mouse abrufen kann. Das macht zwar inhaltlich nicht viel Sinn aber ich dachte wenn OpenInputDesktop funktioniert sollte auch GetMousePos funktionieren. GetMousePos verursacht dann aber eine Exception. Aber mit der obigen Lösung (Vergleich des Desktopnamen) lässt sich das ja prima abfangen.

Hier nochmal der fertige Code falls Jemand das gleiche Problem hat:
Delphi-Quellcode:
function CheckDesktopIsActive: boolean;
var
  hThreadDT    : HDESK;
  hInputDT     : HDESK;
  iLen         : DWORD;
  pDesktopName : array[0..255] of char;
  sThreadDTName : string;
  sInputDTName : string;
begin
  Result   := false;
  hInputDT := 0;
  hThreadDT := 0;

  try
    //Handles ermitteln
    hThreadDT := GetThreadDesktop(GetCurrentThreadId);
    if hThreadDT = 0 then
      Exit;
    hInputDT := OpenInputDesktop(0, false, 0);
    if hInputDT = 0 then
      Exit;

    //Namen zu den Handles ermitteln
    GetUserObjectInformation(hThreadDT, UOI_NAME, @pDesktopName, 256, iLen);
    SetString(sThreadDTName, pDesktopName, Pred(iLen));

    GetUserObjectInformation(hInputDT, UOI_NAME, @pDesktopName, 256, iLen);
    SetString(sInputDTName, pDesktopName, Pred(iLen));

    //Namen vergleichen
    Result := sThreadDTName = sInputDTName;
  finally
    if hInputDT <> 0 then
      CloseDesktop(hInputDT);
  end;
end;
Vielen Dank für die Erleuchtung :thumb:


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:22 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz