[Webinar] Streamline your web hosting managementRegister Today

x
?
Solved

Screen Saver

Posted on 2000-01-10
27
Medium Priority
?
772 Views
Last Modified: 2013-12-28
Created a screen saver blah.scr and installed it.

Problem is that when the screen saver kicks in, the desktop blanks out. If there is a background image, it remains in place, but all icons are removed.

When the screen saver ends, the original desktop is reinstated.

So, I thought, I'll be clever...

I let the screen saver blah.scr shell out an executable blah.exe and then the screen saver ends.

Tried this, and AFA the desktop is concerned, it works fine. When the screen saver kicks in, I see a quick flicker, but the desktop remains as is. So far so good.

But what's strange is that I cannot see the window for blah.exe

For this test I've kept everything very simple. "blah.exe" is just a simple executable popping up a little window. Absolutely nothing else happens in it.

So that is very odd. Blah.exe is definitely running. When I check with TaskManager, it is there.

It's almost as if when the screen saver kicks in NT creates a new "session", with it's own desktop etc.

I tried using the SetForegroundWindow and SetParent APIs to force the window to the foreground or at least on top of the desktop, but nothing helps.

What gives?

Points + A for "this cannot be done" + short explanation.
More points + A for solution.

Cheers

Pino
0
Comment
Question by:caraf_g
  • 16
  • 10
27 Comments
 
LVL 10

Author Comment

by:caraf_g
ID: 2338244
Edited text of question.
0
 
LVL 5

Expert Comment

by:carmine
ID: 2340944
NT does indeed create a new desktop environment for the screen saver.  This is for security purposes, so that you can't see the user desktop whilst the SS is active.

If you want to manipulate the user desktop (melting effects etc), you have to take a bitmap copy of it and display the bitmap in your SS.

Mark
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2344497
Yes, of course. My problem is... how do you do that? Normally you can call the GetDesktopWindow API or even just use 0 for the desktop, but in this case that won't work.

Here's some further information I found out:

Quote
The problem is that, on NT, screen savers are run on a separate desktop than the interactive one (actually its called "Screen-Saver").
And hence, screensavers can't directly interact with the desktop (which is winsta0\Default). In case Win2k, you see this behavior only on password-protected screensavers.

So, a simple solution for this might be to have a separate executable which actually runs on the interactive desktop and captures the screen and stores it away in a memory mapped file and that can in turn be used by the actual screen saver.
End Quote

I don't like this idea.

There might be some other (better) way of doing it.

Craig Clearman <chclear@mvps.org> wrote...

Quote:
NT launches a new desktop under the default WindowStation whenever you launch a screen saver. The name of the WindowStation that you want is "WinSta0" (It's the only WindowStation that you have rights to manipulate, as well). The name of the new desktop is "Screen-saver". The name of the desktop that you want is "Default".

You need to open the WinSta0 window station, either by enumerating to get the name, or by hardcoding it. When you have the name, you need to call OpenWindowStation with the name, and default properties. In this case, you'll need WINSTA_ENUMDESKTOPS and WINSTA_READSCREEN, at the very least.

Once you have a handle to the window station, you need to enumerate the desktop, or directly open it by the name "Default". When you have the handle to the desktop, you can enumerate its windows, using EnumDesktopWindows. From here, you find the desktop window, and BitBlt it. It's certainly not easy.
End Quote

Not easy.. you can say that again.

It sounds like the right way to go, alright, but I'm no API wizard so this information is not sufficient for me to work it out.

If someone can take the above and provide me with a code sample in any reasonably easy to read language, I'd appreciate it.

I program in VB, but I should have no problem translating from C++

Thanks


Pino
0
Upgrade your Question Security!

Your question, your audience. Choose who sees your identity—and your question—with question security.

 
LVL 86

Accepted Solution

by:
jkr earned 400 total points
ID: 2345107
You'll have to gain access to the user's desktop. The easiest way to do this is to 'impersonate' the security context of the logged-on user using one of the user's process token. Some 'pseudocode' to illustrate it:


HANDLE GetTokenOfLoggedOnUser()
{
 HANDLE hToken;
 HANDLE hProcess;
 DWORD dwPID;
 // find PID of 'explorer.exe'
 // HOWTO: Enumerate Applications in Win32
 // http://support.microsoft.com/support/kb/articles/Q175/0/30.ASP 

    hProcess    =   OpenProcess (   PROCESS_ALL_ACCESS,
                                    TRUE,
                                    dwPID
                                );
   
    if  (   !OpenProcessToken   (   hProcess,
                                    TOKEN_QUERY | TOKEN_IMPERSONATE,
                                    &hToken
                                )
        )   return  (   INVALID_HANDLE_VALUE);

 return ( hToken);
}

//Error checking & 'CloseHandle()' omitted for brevity ;-)


    DWORD dwThreadId;
    HWINSTA hwinstaSave;
    HDESK hdeskSave;
    HWINSTA hwinstaUser;
    HDESK hdeskUser;
    int result;
 
    /*
     * Ensure connection to service window station and desktop, and
     * save their handles.
     */
    GetDesktopWindow();
    hwinstaSave = GetProcessWindowStation();
    dwThreadId = GetCurrentThreadId();
    hdeskSave = GetThreadDesktop(dwThreadId);
 
    /*
     * Impersonate the client and connect to the User's
     * window station and desktop.
     */
    InpersonateLoggedOnUser ( GetTokenOfLoggedOnUser();
    hwinstaUser = OpenWindowStation(lpszWindowStation, FALSE, MAXIMUM_ALLOWED);
    if (hwinstaUser == NULL) {
        RevertToSelf();
        return 0;
    }
    SetProcessWindowStation(hwinstaUser);
    hdeskUser = OpenDesktop(lpszDesktop, 0, FALSE, MAXIMUM_ALLOWED);
    RevertToSelf();
    if (hdeskUser == NULL) {
        SetProcessWindowStation(hwinstaSave);
        CloseWindowStation(hwinstaUser);
        return 0;
    }
    SetThreadDesktop(hdeskUser);
 
    /*
     * Display message box (for example's sakes) or enumerate the windows
     */
    dwGuiThreadId = dwThreadId;
    result = MessageBox(NULL, lpszText, lpszTitle, fuStyle);
    dwGuiThreadId = 0;
 
    /*
     * Restore window station and desktop.
     */
    SetThreadDesktop(hdeskSave);
    SetProcessWindowStation(hwinstaSave);
    CloseDesktop(hdeskUser);
    CloseWindowStation(hwinstaUser);
 
    RevertToSelf();
}  
 
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2345212
OK, I'm going to throw this into my big cauldron and stir it around a bit.

Thanks for your suggestion.

;-)

Pino
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2345855
Right jkr, so far so good. The following is my VB translation of your code:

Option Explicit
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, _
                                                     ByVal bInheritHandle As Long, _
                                                     ByVal dwProcessId As Long) As Long

Declare Function OpenProcessToken _
                 Lib "advapi32.dll" (ByVal ProcessHandle As Long, _
                                     ByVal DesiredAccess As Long, _
                                     TokenHandle As Long) As Long


Private Declare Function RevertToSelf Lib "advapi32.dll" () As Long
Private Declare Function ImpersonateLoggedOnUser Lib "kernel32" (ByVal hToken As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function GetDesktopWindow Lib "user32" () As Long
Private Declare Function GetProcessWindowStation Lib "user32" () As Long
Private Declare Function GetCurrentThreadId Lib "kernel32" () As Long
Private Declare Function GetThreadDesktop Lib "user32" (ByVal dwThread As Long) As Long
Private Declare Function OpenWindowStation _
                Lib "user32" _
                Alias "OpenWindowStationA" (ByVal lpszWinSta As String, _
                                            ByVal fInherit As Boolean, _
                                            ByVal dwDesiredAccess As Long) As Long
Private Declare Function SetProcessWindowStation Lib "user32" (ByVal hWinSta As Long) As Long
Private Declare Function OpenDesktop _
                Lib "user32" _
                Alias "OpenDesktopA" (ByVal lpszDesktop As String, _
                                      ByVal dwFlags As Long, _
                                      ByVal fInherit As Boolean, _
                                      ByVal dwDesiredAccess As Long) As Long
Private Declare Function CloseWindowStation Lib "user32" (ByVal hWinSta As Long) As Long
Private Declare Function SetThreadDesktop Lib "user32" (ByVal hDesktop As Long) As Long
Private Declare Function CloseDesktop Lib "user32" (ByVal hDesktop As Long) As Long


Private Const STANDARD_RIGHTS_REQUIRED As Long = &HF0000
Private Const SYNCHRONIZE As Long = &H100000
Private Const PROCESS_ALL_ACCESS As Long = STANDARD_RIGHTS_REQUIRED Or SYNCHRONIZE Or &HFFF
Private Const TOKEN_IMPERSONATE As Long = &H4
Private Const TOKEN_QUERY As Long = &H8
Private Const INVALID_HANDLE_VALUE As Long = -1
Private Const MAXIMUM_ALLOWED As Long = &H2000000

Private Function GetTokenOfLoggedOnUser() As Long

Dim hToken As Long
Dim hProcess As Long
Dim dwPID As Long
' // find PID of 'explorer.exe'
' // HOWTO: Enumerate Applications in Win32
' // http://support.microsoft.com/support/kb/articles/Q175/0/30.ASP

    hProcess = OpenProcess(PROCESS_ALL_ACCESS, _
                           CLng(True), _
                           dwPID)
    If OpenProcessToken(hProcess, _
                        TOKEN_QUERY Or TOKEN_IMPERSONATE, _
                        hToken) = 0 Then
        GetTokenOfLoggedOnUser = INVALID_HANDLE_VALUE
    Else
        GetTokenOfLoggedOnUser = hToken
    End If

End Function

Public Function DoSomething() As Long

'//Error checking & 'CloseHandle()' omitted for brevity ;-)


Dim dwThreadId As Long
Dim hwinstaSave As Long
Dim hdeskSave As Long
Dim hwinstaUser As Long
Dim hdeskUser As Long
Dim result As Long

Dim lpszWindowStation As String
Dim lpszDesktop
    '/*
    ' * Ensure connection to service window station and desktop, and
    ' * save their handles.
    ' */
    GetDesktopWindow
    hwinstaSave = GetProcessWindowStation
    dwThreadId = GetCurrentThreadId
    hdeskSave = GetThreadDesktop(dwThreadId)
   
    '/*
    ' * Impersonate the client and connect to the User's
    ' * window station and desktop.
    ' */
    ImpersonateLoggedOnUser GetTokenOfLoggedOnUser
    hwinstaUser = OpenWindowStation(lpszWindowStation, False, MAXIMUM_ALLOWED)
    If hwinstaUser = 0 Then
        RevertToSelf
        DoSomething = 0
        Exit Function
    End If
    SetProcessWindowStation hwinstaUser
    hdeskUser = OpenDesktop(lpszDesktop, 0, False, MAXIMUM_ALLOWED)
    RevertToSelf
    If hdeskUser = 0 Then
        SetProcessWindowStation hwinstaSave
        CloseWindowStation hwinstaUser
        DoSomething = 0
    End If
    SetThreadDesktop hdeskUser
   
    '/*
    ' * Display message box (for example's sakes) or enumerate the windows
    ' */
    'dwGuiThreadId = dwThreadId;
    'result = MessageBox(NULL, lpszText, lpszTitle, fuStyle);
    'dwGuiThreadId = 0;
   
    '/*
    ' * Restore window station and desktop.
    ' */
    SetThreadDesktop hdeskSave
    SetProcessWindowStation hwinstaSave
    CloseDesktop hdeskUser
    CloseWindowStation hwinstaUser
   
    RevertToSelf

End Function



That leaves a few questions:

lpszWindowStation
What do I set that to? "WinSta0" ?

lpszDesktop
What do I set that to? "Desktop"? Or "Default"?

CloseHandle
When exactly should that be called?

Thanks for your help so far


Pino
0
 
LVL 86

Expert Comment

by:jkr
ID: 2346047
Yes, The window station should be 'WinSta0' (though hard-coding the name is not recommended by MS) and the desktop's name indeed is 'Default'.

I'll try to find a MSDN article related to this issue, the title is 'Interacting with the User in a Service' - but MSDN online is soooo slow...

Oh, yes, and the token handle should be closed whenever 'DoSomething()' is left ;-)

I don't know whether VB supports SEH, but I'd do it that way

HANDLE hToken = GetTokenOfLoggedOnUser();

__try
{
  if ( ...)
  {
    __leave;
  }
}
__finally
{
 CloseHandle ( hToken);
}
0
 
LVL 86

Expert Comment

by:jkr
ID: 2346103
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2347065
What is "SEH"?
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2347073
Oh, by the way.... I think this is going to lead to a solution. So thanks very much...

....increasing the points...
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2347075
;-)
0
 
LVL 86

Expert Comment

by:jkr
ID: 2347098
Thanx! (also still at work in Ireland - oh jes, I forgot, I'm 1h before you ;-)

BTW: SEH is 'Structured Exception Handling'
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2347134
"also still at work in Ireland"

No, I'm not that daft... I'm at home but I just logged in to check my e-mail and saw the notification.

Thanks for the explanation. I'm not sure what SEH means even though now I know what it stands for but yes, VB does support error handling and I guess with a bit of intelligent programming you can even put in some structure <g>
0
 
LVL 86

Expert Comment

by:jkr
ID: 2347401
Back home again... (ready to go out ;-)

SEH is the Win32 way of handling exceptions, the 'usual' constructs are

__try
{
// code
}
__except ( <condition>) // mostly EXCEPTION_EXECUTE_HANDLER
{
// exception handling code
}
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2348933
jkr, do you use VB at all?

Anyway, I've tried the code, but I'm not getting the desired result.

I've taken the Jim & Don's Screen Saver as a starting point.

http://www.arcatapet.com/vbsource/jdsvrvb5.zip

The bit that copies the screen can be found in the sub <doh> CopyScreen.

I added an extra module to this project. This module contains the code that I'll post in the next comment. As you can see the module contains a procedure named DoSomething.

Finally, replace all calls to CopyScreen with DoSomething in the original Jim & Don's Screen Saver project.

Then run. It runs OK in the test and preview modes, but still shows an empty-ish desktop.

Any help greatly appreciated. (you too ameba, if you're still listening in...)

Code in next comment.
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2348935
Option Explicit
Private Declare Function StretchBlt& Lib "gdi32" (ByVal hDestDC&, ByVal x&, ByVal Y&, ByVal nWidth&, ByVal nHeight&, ByVal hSrcDC&, ByVal XSrc&, ByVal YSrc&, ByVal nSrcWidth&, ByVal nSrcHeight&, ByVal dwRop&)

Private Declare Function GetDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hDC As Long) As Long
Private Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, _
                                                     ByVal bInheritHandle As Long, _
                                                     ByVal dwProcessId As Long) As Long

Declare Function OpenProcessToken _
                 Lib "advapi32.dll" (ByVal ProcessHandle As Long, _
                                     ByVal DesiredAccess As Long, _
                                     TokenHandle As Long) As Long


Private Declare Function RevertToSelf Lib "advapi32.dll" () As Long
Private Declare Function ImpersonateLoggedOnUser Lib "advapi32" (ByVal hToken As Long) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
Private Declare Function GetDesktopWindow Lib "user32" () As Long
Private Declare Function GetProcessWindowStation Lib "user32" () As Long
Private Declare Function GetCurrentThreadId Lib "kernel32" () As Long
Private Declare Function GetThreadDesktop Lib "user32" (ByVal dwThread As Long) As Long
Private Declare Function OpenWindowStation _
                Lib "user32" _
                Alias "OpenWindowStationA" (ByVal lpszWinSta As String, _
                                            ByVal fInherit As Boolean, _
                                            ByVal dwDesiredAccess As Long) As Long
Private Declare Function SetProcessWindowStation Lib "user32" (ByVal hWinSta As Long) As Long
Private Declare Function OpenDesktop _
                Lib "user32" _
                Alias "OpenDesktopA" (ByVal lpszDesktop As String, _
                                      ByVal dwFlags As Long, _
                                      ByVal fInherit As Boolean, _
                                      ByVal dwDesiredAccess As Long) As Long
Private Declare Function CloseWindowStation Lib "user32" (ByVal hWinSta As Long) As Long
Private Declare Function SetThreadDesktop Lib "user32" (ByVal hDesktop As Long) As Long
Private Declare Function CloseDesktop Lib "user32" (ByVal hDesktop As Long) As Long


Private Const STANDARD_RIGHTS_REQUIRED As Long = &HF0000
Private Const SYNCHRONIZE As Long = &H100000
Private Const PROCESS_ALL_ACCESS As Long = STANDARD_RIGHTS_REQUIRED Or SYNCHRONIZE Or &HFFF
Private Const TOKEN_IMPERSONATE As Long = &H4
Private Const TOKEN_QUERY As Long = &H8
Private Const INVALID_HANDLE_VALUE As Long = -1
Private Const MAXIMUM_ALLOWED As Long = &H2000000
Private Const WINSTA_ENUMDESKTOPS As Long = &H1&
Private Const WINSTA_READSCREEN As Long = &H200&

Private Function GetTokenOfLoggedOnUser() As Long

Dim hToken As Long
Dim hProcess As Long
Dim dwPID As Long
' // find PID of 'explorer.exe'
' // HOWTO: Enumerate Applications in Win32
' // http://support.microsoft.com/support/kb/articles/Q175/0/30.ASP

    hProcess = OpenProcess(PROCESS_ALL_ACCESS, _
                           CLng(True), _
                           dwPID)
    If OpenProcessToken(hProcess, _
                        TOKEN_QUERY Or TOKEN_IMPERSONATE, _
                        hToken) = 0 Then
        GetTokenOfLoggedOnUser = INVALID_HANDLE_VALUE
    Else
        GetTokenOfLoggedOnUser = hToken
    End If

End Function

Public Function DoSomething(canvas As Object) As Long

'//Error checking & 'CloseHandle()' omitted for brevity ;-)


Dim dwThreadId As Long
Dim hwinstaSave As Long
Dim hdeskSave As Long
Dim hwinstaUser As Long
Dim hdeskUser As Long
Dim result As Long

Dim lpszWindowStation As String
Dim lpszDesktop
Dim hToken As Long

lpszWindowStation = "WinSta0"
lpszDesktop = "Default"

Dim hDCUser As Long
    '/*
    ' * Ensure connection to service window station and desktop, and
    ' * save their handles.
    ' */
    GetDesktopWindow
    hwinstaSave = GetProcessWindowStation
    dwThreadId = GetCurrentThreadId
    hdeskSave = GetThreadDesktop(dwThreadId)
   
    '/*
    ' * Impersonate the client and connect to the User's
    ' * window station and desktop.
    ' */
    hToken = GetTokenOfLoggedOnUser
    ImpersonateLoggedOnUser hToken
    hwinstaUser = OpenWindowStation(lpszWindowStation, False, MAXIMUM_ALLOWED Or WINSTA_ENUMDESKTOPS Or WINSTA_READSCREEN)
    If hwinstaUser = 0 Then
        RevertToSelf
        DoSomething = 0
        Exit Function
    End If
    SetProcessWindowStation hwinstaUser
    hdeskUser = OpenDesktop(lpszDesktop, 0, False, MAXIMUM_ALLOWED)
    RevertToSelf
    If hdeskUser = 0 Then
        SetProcessWindowStation hwinstaSave
        CloseWindowStation hwinstaUser
        DoSomething = 0
        Exit Function
    End If
    SetThreadDesktop hdeskUser

    'Copy the desktop to the canvas...
    hDCUser = GetDC(hdeskUser)
    canvas.AutoRedraw = True
    Call StretchBlt(canvas.hDC, 0, 0, canvas.Width, canvas.Height, hDCUser, 0, 0, Screen.Width, Screen.Height, SRCCOPY)
    canvas.AutoRedraw = False
    ReleaseDC hdeskUser, hDCUser
   
    '/*
    ' * Restore window station and desktop.
    ' */
    SetThreadDesktop hdeskSave
    SetProcessWindowStation hwinstaSave
    CloseDesktop hdeskUser
    CloseWindowStation hwinstaUser
   
    RevertToSelf

    CloseHandle hToken

End Function
0
 
LVL 86

Expert Comment

by:jkr
ID: 2349175
>>jkr, do you use VB at all?

Never used VB - the last time I used BASIC is now about 16 years ago...

Well, regarding the code - you still haven't retrieved explorer.exe's PID... (at least that's what's obvious for me) - you might want to try to use a hard-coded PID for testing purposes.
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2349246
Not at all.. How would you retrieve explorer.exe's pid in C++ then.

Just shows my ignorance.. I never noticed that anything was amiss in that little procedure..
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2349389
Hm... I tried your suggestion, ran TaskManager, found that Explorer.exe's PID = 163 in my case, hard-coded 163 in dwPID, and tried it out.

Didn't work, I'm afraid.

PS - BASIC is a far cry from VB. Although VB is far from perfect, at least one can say "I'm a professional VB developer" without feeling too much like a prat. <g> Hey, I'm making good money out of being a "professional VB developer" so you won't hear me complaining. 2<g>s
0
 
LVL 86

Expert Comment

by:jkr
ID: 2349561
No complaints about VB - I'm just into C/C++ and think I won't change this ;-)

Well, back to the original issue (I know that the above code works) - could you try (as a 1st step) to simply display a message box instead of trying to copy the canvas?
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2349660
Right, tried this:

dwGUIThreadID = dwThreadId
result = MessageBoxA(0, "Hello", "Hello World", 0)
dwGUIThreadID = 0

I hear the "ping" of the message box, but I just see the screen saver. When I move my mouse, there's the message box on an empty desktop.

Then I changed it as follows:


dwGUIThreadID = dwThreadId
result = MessageBoxA(hdeskUser, "Hello", "Hello World", 0)
dwGUIThreadID = 0

I still hear the ping of the message box as before, but this time, when I move the mouse, the message box is nowhere to be seen. The screen saver also doesn't seem to be running anymore (checked with TaskMAnager).
0
 
LVL 86

Expert Comment

by:jkr
ID: 2350268
Well, I'm stumped. I use code like this a lot to interact with tthe desktop from services...

But there's one thig I noticed that doesn't seem correct:

    hDCUser = GetDC(hdeskUser)

IMHO, this should read


    hDCUser = GetDC(GetDesktopWindow());

(excuse the C/C++ notation, but I think it's better to give a correct statement this way than a incorrect VB expression ;-)
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2359110
Hi jkr,

I tried GetDesktopWindow to no avail.

I then went down the following line of reasoning. Normally for a BitBlt to work from a device context for an existing window, that window needs to be visible. When the screen saver is running, it essentially is not.

So how can I make the user's desktop visible? Eventually I found some promising API call: SwitchDesktop.

Basically, I would find out the handle for desktop "Default" in window station "WinSta0", and then use that in SwitchDesktop to make it visible.

Alas...

It doesn't work for me. After reading the MSDN documentation, I realised that it is probably because (according to the documentation) it is not supposed to work for a secure desktop (such as screen-saver in Windows NT).

I've come to the conclusion that it may well be impossible to get an image of the user's desktop from a Windows NT screen saver, unless you do something like run an exe in the background that keeps polling the desktop every minute or so and save it away in a file. But I REALLY don't want to go down that route.

Any further ideas?

PS - no worries, I've still learnt a lot from this thread, so I think you still deserve the points you got.
0
 
LVL 86

Expert Comment

by:jkr
ID: 2360276
Hmm, there _is_ a way to acces a secured desktop, but it requires using LSA - the drawback is that it works with altering the DACL, and in order to do that, you have to be admin or the owner of the object (and it's quite non-trivial even in C/C++, as using acces tokens is accessing opaque memory - offsets, field lengths, and noone knows what's in there ;-)

   BOOL AddTheAceDesktop(HDESK hdesk, PSID psid)
   {
        ACL_SIZE_INFORMATION aclSizeInfo;
        BOOL                 bDaclExist;
        BOOL                 bDaclPresent;
        BOOL                 bSuccess  = FALSE; // assume function will
                                                // fail
        DWORD                dwNewAclSize;
        DWORD                dwSidSize = 0;
        DWORD                dwSdSizeNeeded;
        PACL                 pacl;
        PACL                 pNewAcl;
        PSECURITY_DESCRIPTOR psd       = NULL;
        PSECURITY_DESCRIPTOR psdNew    = NULL;
        PVOID                pTempAce;
        SECURITY_INFORMATION si        = DACL_SECURITY_INFORMATION;
        unsigned int         i;

        __try
             {
             //
             // obtain the security descriptor for the desktop object
             //
             if (!GetUserObjectSecurity(
                  hdesk,
                  &si,
                  psd,
                  dwSidSize,
                  &dwSdSizeNeeded
                  ))
                  {
                  if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
                       {
                       psd = (PSECURITY_DESCRIPTOR)HeapAlloc(
                            GetProcessHeap(),
                            HEAP_ZERO_MEMORY,
             dwSdSizeNeeded
             );
                       if (psd == NULL)
                            __leave;

                       psdNew = (PSECURITY_DESCRIPTOR)HeapAlloc(
                            GetProcessHeap(),
                            HEAP_ZERO_MEMORY,
                            dwSdSizeNeeded
             );
                       if (psdNew == NULL)
                            __leave;

                       dwSidSize = dwSdSizeNeeded;

                       if (!GetUserObjectSecurity(
                            hdesk,
                            &si,
                            psd,
                            dwSidSize,
                            &dwSdSizeNeeded
                            ))
                            __leave;
                       }
                  else
                       __leave;
                  }

             //
             // create a new security descriptor
             //
             if (!InitializeSecurityDescriptor(
                  psdNew,
                  SECURITY_DESCRIPTOR_REVISION
                  ))
               __leave;

             //
             // obtain the dacl from the security descriptor
             //
             if (!GetSecurityDescriptorDacl(
                  psd,
                  &bDaclPresent,
                  &pacl,
                  &bDaclExist
                  ))
                  __leave;

             //
             // initialize
             //
             ZeroMemory(&aclSizeInfo, sizeof(ACL_SIZE_INFORMATION));
             aclSizeInfo.AclBytesInUse = sizeof(ACL);

             //
             // call only if NULL dacl
             //
             if (pacl != NULL)
                  {
                  //
                  // determine the size of the ACL info
                  //
                  if (!GetAclInformation(
                       pacl,
                       (LPVOID)&aclSizeInfo,
                       sizeof(ACL_SIZE_INFORMATION),
                       AclSizeInformation
                       ))
                       __leave;
                   }

             //
             // compute the size of the new acl
             //
        dwNewAclSize = aclSizeInfo.AclBytesInUse +
                            sizeof(ACCESS_ALLOWED_ACE) +
                            GetLengthSid(psid) - sizeof(DWORD);

             //
             // allocate buffer for the new acl
             //
             pNewAcl = (PACL)HeapAlloc(
                  GetProcessHeap(),
                  HEAP_ZERO_MEMORY,
                  dwNewAclSize
                  );
             if (pNewAcl == NULL)
                  __leave;

             //
             // initialize the new acl
             //
             if (!InitializeAcl(pNewAcl, dwNewAclSize, ACL_REVISION))
                  __leave;

             //
             // if DACL is present, copy it to a new DACL
             //
             if (bDaclPresent) // only copy if DACL was present
                  {
                  // copy the ACEs to our new ACL
                  if (aclSizeInfo.AceCount)
                       {
                       for (i=0; i < aclSizeInfo.AceCount; i++)
                            {
                            // get an ACE
                            if (!GetAce(pacl, i, &pTempAce))
                                 __leave;

                            // add the ACE to the new ACL
                            if (!AddAce(
                                 pNewAcl,
                                 ACL_REVISION,
                                 MAXDWORD,
                                 pTempAce,
                                 ((PACE_HEADER)pTempAce)->AceSize
                                 ))
                                 __leave;
                             }
                        }
                  }

             //
             // add ace to the dacl
             //
             if (!AddAccessAllowedAce(
                  pNewAcl,
                  ACL_REVISION,
                  DESKTOP_ALL,
                  psid
                  ))
                  __leave;

             //
             // set new dacl to the new security descriptor
             //
             if (!SetSecurityDescriptorDacl(
                       psdNew,
                       TRUE,
                       pNewAcl,
                       FALSE
                       ))
                  __leave;

             //
             // set the new security descriptor for the desktop object
             //
             if (!SetUserObjectSecurity(hdesk, &si, psdNew))
                  __leave;

             //
             // indicate success
             //
             bSuccess = TRUE;
             }
        __finally
            {
            //
            // free buffers
            //
            if (pNewAcl != NULL)
                 HeapFree(GetProcessHeap(), 0, (LPVOID)pNewAcl);

             if (psd != NULL)
                  HeapFree(GetProcessHeap(), 0, (LPVOID)psd);

             if (psdNew != NULL)
                  HeapFree(GetProcessHeap(), 0, (LPVOID)psdNew);
             }

        return bSuccess;
   }

0
 
LVL 10

Author Comment

by:caraf_g
ID: 2360784
I'll have a look at this tomorrow. However, thinking about this.... I'm not trying to "access" a secured desktop, in fact I'm try to "get out of" a secured desktop - I'm in a screen saver (with a secure desktop) and I'm trying to display the original user's desktop.... But perhaps it's much of a muchness..

In the mean time though, could you write up an explanation, in layman's terms, what the above code does. I should be able to convert it to VB, but such a description would help tremendously.

Cheers

Pino
0
 
LVL 86

Expert Comment

by:jkr
ID: 2368303
Sorry for the delay, but I'm awfully busy here (deploying a new product version) - the explanation will follow ;-)
0
 
LVL 10

Author Comment

by:caraf_g
ID: 2477519
I never actually solved the problem at hand, but I found a workaround that did more or less what I wanted to do.

I created a screen-saver something.scr, and an executable something.exe

When the screen-saver activates it checks whether the executable is already
running (with FindWindow). If it is not, it uses CreateProcess to activate
the executable in "WinSta0/Default". Then it exits. I'll cover what it does
otherwise in a sec.

When the executable starts, it checks whether the screen-saver's main window
is visible (again with FindWindow). It waits until it is not. Then it proceeds. By now, the screen-saver has deactivated, and the original desktop is once again displayed on the workstation. Now it activates the screen
saver again.

The screen saver once again checks whether the executable is already running, and this time it is. It now proceeds to grab the desktop window, etc.... and show the main form.

What does work:
- The screen saver successfully grabs the desktop window and displays it. As soon as the user moves the mouse or accesses the keyboard it deactivates.
- The executable checks the screen-saver window every 10 seconds. If it is no longer there it exits too.
- Whilst the screen-saver is running, NT does not activate it again (wonder how it does this? I would have expected it to at least start up, then realise it shouldn't, and exit again. So I would at least have expected a short flicker or interruption. But nothing seems to happen?)

What's still a bit dodgy:
- I noticed that the screen-saver deactivated when an e-mail came in. I did not access keyboard or mouse.

All in all, a reasonably OK solution to my problem.

Thanks to all who've shown interest in this question and who've helped me with suggestions and tips.

Regards

Pino
0

Featured Post

Take Control of Web Hosting For Your Clients

As a web developer or IT admin, successfully managing multiple client accounts can be challenging. In this webinar we will look at the tools provided by Media Temple and Plesk to make managing your clients’ hosting easier.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This is an update to some code that someone else posted on Experts Exchange. It is an alternate approach, I think a little easier to use, & makes sure that things like the Task Bar will update.
I was recently poking around with LibreOffice and figured out how easy it is to add great vector clip art to one's own LibreOffice gallery collection.
If you’ve ever visited a web page and noticed a cool font that you really liked the look of, but couldn’t figure out which font it was so that you could use it for your own work, then this video is for you! In this Micro Tutorial, you'll learn yo…
How to fix incompatible JVM issue while installing Eclipse While installing Eclipse in windows, got one error like above and unable to proceed with the installation. This video describes how to successfully install Eclipse. How to solve incompa…

607 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question