Screen Saver

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
LVL 10
caraf_gAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

caraf_gAuthor Commented:
Edited text of question.
0
carmineCommented:
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
caraf_gAuthor Commented:
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
Cloud Class® Course: Microsoft Azure 2017

Azure has a changed a lot since it was originally introduce by adding new services and features. Do you know everything you need to about Azure? This course will teach you about the Azure App Service, monitoring and application insights, DevOps, and Team Services.

jkrCommented:
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

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
caraf_gAuthor Commented:
OK, I'm going to throw this into my big cauldron and stir it around a bit.

Thanks for your suggestion.

;-)

Pino
0
caraf_gAuthor Commented:
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
jkrCommented:
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
caraf_gAuthor Commented:
What is "SEH"?
0
caraf_gAuthor Commented:
Oh, by the way.... I think this is going to lead to a solution. So thanks very much...

....increasing the points...
0
caraf_gAuthor Commented:
;-)
0
jkrCommented:
Thanx! (also still at work in Ireland - oh jes, I forgot, I'm 1h before you ;-)

BTW: SEH is 'Structured Exception Handling'
0
caraf_gAuthor Commented:
"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
jkrCommented:
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
caraf_gAuthor Commented:
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
caraf_gAuthor Commented:
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
jkrCommented:
>>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
caraf_gAuthor Commented:
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
caraf_gAuthor Commented:
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
jkrCommented:
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
caraf_gAuthor Commented:
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
jkrCommented:
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
caraf_gAuthor Commented:
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
jkrCommented:
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
caraf_gAuthor Commented:
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
jkrCommented:
Sorry for the delay, but I'm awfully busy here (deploying a new product version) - the explanation will follow ;-)
0
caraf_gAuthor Commented:
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
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Windows OS

From novice to tech pro — start learning today.