![]() |
Programm auf eigenem "Desktop" ausführen
Hallo zusammen.
Ich möchte über mein Programm eine Installation durchführen. Wichtig ist aber, dass der Benutzer währenddessen nicht interagiert oder die Fenster wechselt. Dazu blocke ich mit BlockInput die Eingabe. Leider ist das durch die IBM-Kralle (Strg+Alt+Entf) leicht umgehbar. Ich würde daher gerne das Programm auf einem eigenen Desktop (sowas wie der SecureDesktop ab Vista) ausführen und auch die Anzeige dorthin wechseln, so dass der User nur abwarten kann. Hat jemand eine Idee wie ich sowas (oder eine Alternativlösung) realisieren kann? |
Re: Programm auf eigenem "Desktop" ausführen
![]() ![]() aber nicht vergessen, der Thread, mit welchem dieses "Fenster" angezeigt werden soll, darf kein "Fensterhandle" irgendeiner Art auf einem anderem Desktop besitzen, sonst kann man nicht auf den anderen/neuen Desktop wechseln und dort ein Fenster erzeugen. Heißt, wenn du mit der VCL arbeiten willst, daß du 2 Programme oder zumindestens 2 Programminstanzen benötigst, da die VCL einige "unsichtbare" Fenster zur Verwaltung im Hauptthread erstellt. Wegen diesen kannst du den Hauptthread nicht auf den anderen Desktop übergeben und die VCL kann/darf man aber nur von Hauptthread aus verwenden. Ergo: - Programm 1 erstellt Desktop - Programm 1 macht diesen Desktop sichtbar - Programm 1 startet Programm 2 geziehlt auf diesem Desktop - Programm 2 arbeitet - ... - Programm 2 beendet sich und übergibt die Kontrolle an Programm 1 - Programm 1 macht den ursprünglichen Desktop wieder sichtbar - Programm 1 löscht den erstellten Desktop (Programm 2 kann auch eine weitere Instantz des 1. Programms sein) |
Re: Programm auf eigenem "Desktop" ausführen
Wenn irgendein Installationsprogramm mich blockieren würde, wäre ich doch sehr misstrauisch und würde als Erstes die Internetverbindung kappen mit ZoneAlarm. Wenn das nicht ginge, wäre da immer noch der Powerknopf...
|
Re: Programm auf eigenem "Desktop" ausführen
Zitat:
@himitsu: Ich hoffe du meinst Desktop :wink: . Ist komplizierter als ich gewollt habe, aber ich werde mich mal drangeben. |
Re: Programm auf eigenem "Desktop" ausführen
Zitat:
Folgende Möglichkeiten, Switch Desktop oder bei allen Prozessen vorübergehend die Threads deaktivieren. D.h. Prozesse suchen, deren Threads ermitteln und diese suspenden, nach der Installation wieder resumen. Anbei Desktop Switch sample und Thread Suspend Demo, getestet unter W2K
Delphi-Quellcode:
{$APPTYPE CONSOLE} uses Windows, Sysutils; var OldDesktop: HDESK = 0; NewDesktop: HDESK = 0; Deskname: array [0..255] of Char; procedure CreateNewDesktop; begin OldDesktop := GetThreadDesktop(GetCurrentThreadID); StrPCopy(Deskname, 'Desktop' + IntToStr(GetCurrentThreadID)); NewDesktop := CreateDesktop(Deskname, nil, nil, 0, DESKTOP_CREATEMENU or DESKTOP_CREATEWINDOW or DESKTOP_SWITCHDESKTOP or DESKTOP_READOBJECTS or DESKTOP_WRITEOBJECTS or STANDARD_RIGHTS_REQUIRED, nil); if NewDesktop <> 0 then begin SetThreadDesktop(NewDesktop); SwitchDesktop(NewDesktop); end; end; procedure ReleaseDesktop; begin if newDesktop <> 0 then begin SetThreadDesktop(OldDesktop); SwitchDesktop(OldDesktop); end; end; function lpThreadFunc(ptrData: Pointer): Integer; var si: TStartupInfo; pi: TProcessInformation; begin ZeroMemory(@si, SizeOf(si)); si.cb := SizeOf(si); si.dwFlags := CREATE_NEW_CONSOLE or CREATE_NEW_PROCESS_GROUP or DETACHED_PROCESS; si.wShowWindow := SW_SHOWNORMAl; si.lpDesktop := ptrData; CreateProcess(nil, PChar('calc.exe'), nil, nil, false, CREATE_NEW_CONSOLE, nil, nil, si, pi); WaitForSingleObject(pi.hProcess, INFINITE); GetExitCodeProcess(pi.hProcess, DWORD(result)); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); end; var dwThreadID: DWORD; hThread: DWORD; begin CreateNewDesktop; hThread := beginThread(nil, 0, lpThreadFunc, @deskname, 0 , dwThreadID); WaitForSingleObject(hThread, INFINITE); CloseHandle(dwThreadID); ReleaseDesktop; end. Thread Suspend Demo, startet calc.exe
Delphi-Quellcode:
lg. Astat{$APPTYPE CONSOLE} uses windows, psapi, tlhelp32, SysUtils; const THREAD_GET_CONTEXT = $0008; THREAD_SET_CONTEXT = $0010; THREAD_SUSPEND_RESUME = $0002; function AdjustTokenPrivileges(TokenHandle: THandle; DisableAllPrivileges: BOOL; const NewState: TTokenPrivileges; BufferLength: DWORD; PreviousState: PTokenPrivileges; ReturnLength: PDWORD): BOOL; stdcall; external 'advapi32.dll' name 'AdjustTokenPrivileges' function OpenThread(dwDesiredAccess: DWORD; bInheritHandle: BOOL; dwThreadId: DWORD): DWORD; stdcall; external 'kernel32.dll'; function EnableThreadPrivilege(const Enable: Boolean; const Privilege: string): Boolean; const PrivAttrs: array [Boolean] of DWORD = (0, SE_PRIVILEGE_ENABLED); var Token: THandle; TokenPriv: TTokenPrivileges; HaveToken: Boolean; begin result := false; Token := 0; HaveToken := OpenThreadToken(GetCurrentThread, TOKEN_ADJUST_PRIVILEGES, False, Token); if (not HaveToken) and (GetLastError = ERROR_NO_TOKEN) then HaveToken := OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES, Token); if HaveToken then begin TokenPriv.PrivilegeCount := 1; LookupPrivilegeValue(nil, PChar(Privilege), TokenPriv.Privileges[0].Luid); TokenPriv.Privileges[0].Attributes := PrivAttrs[Enable]; AdjustTokenPrivileges(Token, False, TokenPriv, SizeOf(TokenPriv), nil, nil); Result := GetLastError = ERROR_SUCCESS; CloseHandle(Token); end; end; function EnableProcessPrivilegeEx(hProcess: DWORD; const Privilege: string): Boolean; var Token: THandle; TokenPriv: TTokenPrivileges; begin Result := False; if OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, Token) then begin TokenPriv.PrivilegeCount := 1; LookupPrivilegeValue(nil, PChar(Privilege), TokenPriv.Privileges[0].Luid); TokenPriv.Privileges[0].Attributes := SE_PRIVILEGE_ENABLED; AdjustTokenPrivileges(Token, False, TokenPriv, SizeOf(TokenPriv), nil, nil); Result := GetLastError = ERROR_SUCCESS; CloseHandle(Token); end; end; function EnableProcessPrivilege(const Enable: Boolean; const Privilege: string): Boolean; const PrivAttrs: array [Boolean] of DWORD = (0, SE_PRIVILEGE_ENABLED); var Token: THandle; TokenPriv: TTokenPrivileges; begin Result := False; if OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES, Token) then begin TokenPriv.PrivilegeCount := 1; LookupPrivilegeValue(nil, PChar(Privilege), TokenPriv.Privileges[0].Luid); TokenPriv.Privileges[0].Attributes := PrivAttrs[Enable]; AdjustTokenPrivileges(Token, False, TokenPriv, SizeOf(TokenPriv), nil, nil); Result := GetLastError = ERROR_SUCCESS; CloseHandle(Token); end; end; procedure SetThreadState(Suspend: Boolean); var hProc, h32: DWORD; hThread: DWORD; PE32: TProcessEntry32; TE32: TThreadEntry32; szName: array[0..MAX_PATH -1] of char; sName, sCurName: string; begin sCurName := ExtractFileName(ParamStr(0)); h32 := CreateToolHelp32Snapshot(TH32CS_SNAPPROCESS or TH32CS_SNAPTHREAD, 0); if h32 <> ERROR_INVALID_HANDLE then begin PE32.dwSize := SizeOf(ProcessEntry32); TE32.dwSize := Sizeof(ThreadEntry32); if Thread32First(h32, TE32) then begin repeat EnableProcessPrivilege(true, 'SeDebugPrivilege'); EnableProcessPrivilege(true, 'SeSecurityPrivilege'); EnableProcessPrivilege(true, 'SeTakeOwnershipPrivilege'); EnableProcessPrivilege(true, 'SeCreateTokenPrivilege'); //-- ToDo für jeden Prozess nur einmal aufrufen, //-- Exclude Liste mit GetSecurityInfo -> NT_ATHORITÄT_SYSTEM ersetzen usw. hProc := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, TE32.th32OwnerProcessID); if hProc <> 0 then begin ZeroMemory(@szName, MAX_PATH); GetModuleFileNameEx(hProc, 0, szName, MAX_PATH); sName := ExtractFileName(string(szName)); if (CompareText(sName, '?') = 0) or (CompareText(sName, 'lsass.exe') = 0) or (CompareText(sName, 'calc.exe') = 0) or (CompareText(sName, sCurName) = 0) or (CompareText(sName, 'winlogon.exe') = 0) or (CompareText(sName, 'csrss.exe') = 0) or (CompareText(sName, 'services.exe') = 0) or (CompareText(sName, 'smss.exe') = 0) then begin CloseHandle(hProc); continue; end; CloseHandle(hProc); end else continue; EnableThreadPrivilege(true, 'SeDebugPrivilege'); EnableThreadPrivilege(true, 'SeSecurityPrivilege'); EnableThreadPrivilege(true, 'SeTakeOwnershipPrivilege'); EnableThreadPrivilege(true, 'SeCreateTokenPrivilege'); hThread := OpenThread(THREAD_GET_CONTEXT or THREAD_SET_CONTEXT or THREAD_SUSPEND_RESUME, false, TE32.th32ThreadID); if hThread <> 0 then begin if Suspend then SuspendThread(hThread) else ResumeThread(hThread); end; until not Thread32Next(h32, TE32); end; end; CloseHandle(h32); end; procedure StartTestApp; var si: TStartupInfo; pi: TProcessInformation; begin ZeroMemory(@si, SizeOf(si)); si.cb := SizeOf(si); si.dwFlags := CREATE_NEW_CONSOLE or CREATE_NEW_PROCESS_GROUP or DETACHED_PROCESS; si.wShowWindow := SW_SHOWNORMAl; si.lpDesktop := nil; CreateProcess(nil, PChar('calc.exe'), nil, nil, false, CREATE_NEW_CONSOLE, nil, nil, si, pi); WaitForInputIdle(pi.hProcess, INFINITE); SetThreadState(True); WaitForSingleObject(pi.hProcess, INFINITE); SetThreadState(false); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); end; begin StartTestApp; end. |
Re: Programm auf eigenem "Desktop" ausführen
@Astat: Ich habs versucht, es klappt auch soweit, dass ich einen neuen Desktop bekomme. Aber dort geht kein Calc auf und ich komme dadurch auch nicht mehr zurück, außer durch abmelden.
Anbei das Mini-Beispiel. Ist zu 98% Copy&Paste von deinem Beispiel. Das andere sind Hilfsunits aus meinem Programm (aber alle ohne eigenes Formular). OS ist Win 7 Pro
Delphi-Quellcode:
program Handyman;
{$APPTYPE CONSOLE} uses Windows, SysUtils, ArtisanPackage in 'src\ArtisanPackage.pas', Jobs in 'src\Jobs.pas', Utils in 'src\Utils.pas'; var OldDesktop: HDESK = 0; NewDesktop: HDESK = 0; Deskname: array [0..255] of char; procedure CreateNewDesktop; begin OldDesktop := GetThreadDesktop(GetCurrentThreadID); StrPCopy(Deskname, 'Desktop' + IntToStr(GetCurrentThreadID)); NewDesktop := CreateDesktop(Deskname, nil, nil, 0, DESKTOP_CREATEMENU or DESKTOP_CREATEWINDOW or DESKTOP_SWITCHDESKTOP or DESKTOP_READOBJECTS or DESKTOP_WRITEOBJECTS or STANDARD_RIGHTS_REQUIRED, nil); if NewDesktop <> 0 then begin SetThreadDesktop(NewDesktop); SwitchDesktop(NewDesktop); end; end; procedure ReleaseDesktop; begin if newDesktop <> 0 then begin SetThreadDesktop(OldDesktop); SwitchDesktop(OldDesktop); end; end; function lpThreadFunc(ptrData: Pointer): integer; var si: TStartupInfo; pi: TProcessInformation; begin ZeroMemory(@si, SizeOf(si)); si.cb := SizeOf(si); si.dwFlags := CREATE_NEW_CONSOLE or CREATE_NEW_PROCESS_GROUP or DETACHED_PROCESS; si.wShowWindow := SW_SHOWNORMAl; si.lpDesktop := ptrData; CreateProcess(nil, PChar('calc.exe'), nil, nil, False, CREATE_NEW_CONSOLE, nil, nil, si, pi); WaitForSingleObject(pi.hProcess, 5000); GetExitCodeProcess(pi.hProcess, DWORD(Result)); CloseHandle(pi.hProcess); CloseHandle(pi.hThread); end; var dwThreadID: DWORD; hThread: DWORD; begin try CreateNewDesktop; hThread := beginThread(nil, 0, lpThreadFunc, @deskname, 0, dwThreadID); WaitForSingleObject(hThread, 10000); CloseHandle(dwThreadID); ReleaseDesktop; except on E: Exception do Writeln(E.ClassName, ': ', E.Message); end; end. |
Re: Programm auf eigenem "Desktop" ausführen
Mal ein paar Anreize, zur Fehlereingränzung:
Delphi-Quellcode:
procedure CreateNewDesktop;
begin ... if NewDesktop <> 0 then begin SetThreadDesktop(NewDesktop); SwitchDesktop(NewDesktop); end else FEHLERAUSWERUNG; end; function lpThreadFunc(ptrData: Pointer): integer; begin ... if not CreateProcess(nil, PChar('calc.exe'), nil, nil, False, CREATE_NEW_CONSOLE, nil, nil, si, pi) then FEHLERAUSWERUNG; end; end; begin ... hThread := beginThread(nil, 0, lpThreadFunc, @deskname, 0, dwThreadID); if hThread = 0 then FEHLERAUSWERUNG; ... |
Re: Programm auf eigenem "Desktop" ausführen
Ich hab mir auch mal Luckies Mini-Beispiel genommen und die Fehlerbehandlung die himitsu vorgeschlagen hat eingebaut. Ergebnis: CreateProcess sagt "Datei existiert nicht" .. sie ist aber definitiv da.
Code
Delphi-Quellcode:
procedure BaitAndSwitchDesktop;
var OldDesk: HDESK; NewDesk: HDESK; pi: PROCESS_INFORMATION; si: _STARTUPINFOW; begin OldDesk := GetThreadDesktop(GetCurrentThreadID); NewDesk := CreateDesktop(PChar('Handyman'), nil, nil, 0, DESKTOP_CREATEWINDOW or DESKTOP_SWITCHDESKTOP or DESKTOP_CREATEMENU, nil); if NewDesk <> 0 then begin if SetThreadDesktop(NewDesk) = False then Writeln(SysErrorMessage(GetLastError)); FillChar(si, SizeOf(si), 0); si.lpDesktop := 'Handyman'; si.cb := SizeOf(si); si.dwFlags := STARTF_USESHOWWINDOW; si.wShowWindow := SW_NORMAL; if CreateProcess('', PChar('C:\Windows\System32\calc.exe'), nil, nil, False, CREATE_NEW, nil, nil, si, pi) = False then Writeln(SysErrorMessage(GetLastError)); // if Windows.SwitchDesktop(NewDesk) = False then // Writeln(IntToStr(GetLastError)); MessageBox(0, 'Here I am', 'Foobar Desktop', MB_ICONINFORMATION); if Windows.SwitchDesktop(OldDesk) = False then Writeln(SysErrorMessage(GetLastError)); if SetThreadDesktop(OldDesk) = False then Writeln(SysErrorMessage(GetLastError)); if CloseDesktop(NewDesk) = False then Writeln(SysErrorMessage(GetLastError)); end; if CloseDesktop(OldDesk) = False then Writeln(SysErrorMessage(GetLastError)); end; |
Re: Programm auf eigenem "Desktop" ausführen
Das Problem war der Parameter CREATE_NEW für CreateProcess. Nimmt man da einfach den Parameter 0 klappt alles hervorragend. Thema daher erledigt
|
Re: Programm auf eigenem "Desktop" ausführen
Liste der Anhänge anzeigen (Anzahl: 1)
// EDIT: Alles zu spät, dabei schaffte ich doch mal 1000m unter 5 Min. :gruebel: :cheers:
Hallo Codewalker. 1. Teste mal mit aktiver WndProc, anbei Sample, dabei sollte mindestens die Mainform am Desktop sein. 2. Besorg dir auf die brutale Methode alle Privilleges die du kriegst, und teste mit 1. nochmals.
Delphi-Quellcode:
3. Wenns noch nicht geht, fällt mir nur noch die UACL oder wie das Zeugs unter Vista und 7 auch immer heisst ein.function AdjustTokenPrivileges(TokenHandle: THandle; DisableAllPrivileges: BOOL; const NewState: TTokenPrivileges; BufferLength: DWORD; PreviousState: PTokenPrivileges; ReturnLength: PDWORD): BOOL; stdcall; external 'advapi32.dll' name 'AdjustTokenPrivileges' function EnableProcessPrivilege(const Enable: Boolean; const Privilege: string): Boolean; const PrivAttrs: array [Boolean] of DWORD = (0, SE_PRIVILEGE_ENABLED); var Token: THandle; TokenPriv: TTokenPrivileges; begin Result := False; try if OpenProcessToken(GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES, Token) then begin TokenPriv.PrivilegeCount := 1; LookupPrivilegeValue(nil, PChar(Privilege), TokenPriv.Privileges[0].Luid); TokenPriv.Privileges[0].Attributes := PrivAttrs[Enable]; AdjustTokenPrivileges(Token, False, TokenPriv, SizeOf(TokenPriv), nil, nil); Result := GetLastError = ERROR_SUCCESS; CloseHandle(Token); end; except Result := False; end; end; begin EnableProcessPrivilege(true, 'SeIncreaseBasePriorityPrivilege'); EnableProcessPrivilege(true, 'SeDebugPrivilege'); EnableProcessPrivilege(true, 'SeCreateTokenPrivilege'); EnableProcessPrivilege(true, 'SeAssignPrimaryTokenPrivilege'); EnableProcessPrivilege(true, 'SeLockMemoryPrivilege'); EnableProcessPrivilege(true, 'SeIncreaseQuotaPrivilege'); EnableProcessPrivilege(true, 'SeUnsolicitedInputPrivilege'); EnableProcessPrivilege(true, 'SeMachineAccountPrivilege'); EnableProcessPrivilege(true, 'SeTcbPrivilege'); EnableProcessPrivilege(true, 'SeSecurityPrivilege'); EnableProcessPrivilege(true, 'SeTakeOwnershipPrivilege'); EnableProcessPrivilege(true, 'SeLoadDriverPrivilege'); EnableProcessPrivilege(true, 'SeSystemProfilePrivilege'); EnableProcessPrivilege(true, 'SeSystemtimePrivilege'); EnableProcessPrivilege(true, 'SeProfileSingleProcessPrivilege'); EnableProcessPrivilege(true, 'SeIncreaseBasePriorityPrivilege'); EnableProcessPrivilege(true, 'SeCreatePagefilePrivilege'); EnableProcessPrivilege(true, 'SeCreatePermanentPrivilege'); EnableProcessPrivilege(true, 'SeBackupPrivilege'); EnableProcessPrivilege(true, 'SeRestorePrivilege'); EnableProcessPrivilege(true, 'SeShutdownPrivilege'); EnableProcessPrivilege(true, 'SeDebugPrivilege'); EnableProcessPrivilege(true, 'SeAuditPrivilege'); EnableProcessPrivilege(true, 'SeSystemEnvironmentPrivilege'); EnableProcessPrivilege(true, 'SeChangeNotifyPrivilege'); EnableProcessPrivilege(true, 'SeRemoteShutdownPrivilege'); EnableProcessPrivilege(true, 'SeUndockPrivilege'); EnableProcessPrivilege(true, 'SeSyncAgentPrivilege'); EnableProcessPrivilege(true, 'SeEnableDelegationPrivilege'); EnableProcessPrivilege(true, 'SeManageVolumePrivilege'); end. PS. Einen hab ich noch. :warn: Womöglich neues Berechtigungskonzept ab "Viesda", könnte mir vorstellen dass Zugriff auf neue erstellten Destktop und Station, ein Access Token brauchen??? :gruebel: :?: lg. Astat |
Re: Programm auf eigenem "Desktop" ausführen
Wenn du die VCL auf den neuen Desktop bewegen willst, dann musst du sie einfach beenden. Das sollte klappten, wenn ich mich nicht irre.
Delphi-Quellcode:
Ansonsten, ab SetThreadDesktop alles in einen Thread verlagern.
uses ..., Forms, ...;
begin Forms.Application.Free; ... SetThreadDesktop ... Forms.Application := TApplication.Create(nil); Forms.Application.Initialize; ... end. Du solltest das aber mit einer brandneuen VCL Anwendung testen, da weiter Units Hooks und Handles erstellen könnten, die das verhindern. EDIT: :warn: Wenn Thread gewählt, sollte man im Thread "Application" am Ende freigeben und dann im Hauptprogramm das Application wieder neu erstellen (d.h. nachdem man auf den Thread gewartet hat). D.h. es sollte am Ende so sein, dass VCL im Hauptthread wieder läuft. Hatte deshalb mal ein Problem, das ich dadurch umgangen habe. |
Re: Programm auf eigenem "Desktop" ausführen
Hallo Dezipaitor, Super Tipp Danke! :thumb:
lg. Astat |
Re: Programm auf eigenem "Desktop" ausführen
Zitat:
Bah, da könne ich noch mehr erzählen.. :wall: |
Re: Programm auf eigenem "Desktop" ausführen
Hallo Dezipaitor, nö das funkt tadellos.
Verwende so ein Teil als "NOT AUS" bei Virenbefall. Dh. suspende alle Threads ausser des "Local Security Authority Subsystem Service" (LSASS.EXE) und CSRSS.exe (Thread und WindowCreation) und des Session Manager (SMSS.exe). Starte IceSword, und suspende dann auch noch die LSASS.EXE und CSRSS.exe. Jetzt wird das System ausgemistet, Gegenseitige Prozess und Dateiüberwachung der Schädlinge nicht mehr möglich. Lokal funktioniert dies perfekt!! (W2K, XP getestet.) Remote übers Netzwerk gibt es Probleme weil Winlogon und das Gesammte Netzwerk lahmgelegt wird. ISt aber auch möglich wenn mal Winlogon.exe und die Netzwerdienste in den zugehörigen svchost.exe'n nicht lahmlegt. Allerdings kann man dann einen Sasser, der ja im Kontext der svchost.exe und Winlogon.exe läuft nicht mehr entfernen. PS. Versuch mal das beigelegte Sample, und urteile erst dann. :warn: PSS. Noch besser Du besorgst dir den Processexplorer von Sysinternals, und Suspendest mit diesem mal alle relevanten Prozesse, um vorab mal genau zu checken was du bei den einzelnen OS (W2K, XP, Vista, W7 machen darfst und was nicht! Dienste nicht vergessen, keine Angst der SCM dein Alias Dienstemanager springt dich nicht an! lg. Astat |
Re: Programm auf eigenem "Desktop" ausführen
Zu meinen Codeschnipsel bzw. den von Asta oben hab ich aber nochmal eine Frage:
Was ist, wenn CreateDesktop den Fehler liefert "Die Ressource wird bereits verwendet". Wie komme ich denn dan das HDESK-Handle, wenn ich nur den Namen habe. So etwas wie ein OpenDesktop habe ich nicht gefunden. |
Re: Programm auf eigenem "Desktop" ausführen
|
Re: Programm auf eigenem "Desktop" ausführen
Danke habs gefunden. OpenDesktop braucht da Handle, aber das kriegt man über EnumDesktops (auch wenn man dafür eine zusätzlich Callback-Proc braucht)
|
Re: Programm auf eigenem "Desktop" ausführen
Zitat:
Code:
__in LPTSTR lpszDesktop,
|
Re: Programm auf eigenem "Desktop" ausführen
Ach, korrigiere: Ich habe als Parameter den Namen meines Desktops übergeben, aber bekam immer ein ungültiges Handle als Result. Über EnumDesktops habe ich dann das gültige Handle bekommen. (schon so viel gebastelt heute, da komme ich schonmal ein bißchen durcheinander :wink: )
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 22:50 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024-2025 by Thomas Breitkreuz