Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 2798
  • Last Modified:

Hooking the Logon Desktop (Windows C++)

Hello

Is it possible to install a hook in the windows login desktop?

So far, I have built a windows service which means the program can remain running after the user has logged off. However i do not know how to hook the login desktop.

Note, this research is strictly only for educational/experimental purposes.

Any help would be much appreciated,
Southern Curriez
0
cc16
Asked:
cc16
  • 10
  • 9
1 Solution
 
cookreCommented:
Write a piece of code that enumerates and displays the names of  windows stations, desktops, and windows before and after login.  This will tell you which windows on your OS to hook.

You'll use:

EnumWindowStations()
EnumDesktops()
EnumDesktopWindows()

and their derivative opens and call backs.

Once you know which window to hook, use SetWindowsHookEx().

As they say, "the devil's in the details", and there are a lot of details here, but this will get you started.
0
 
cc16Author Commented:
regarding finding which window to hook...

take EnumWindowStatiosn() for example
it will return the names of every WindowStation to the callback function - how will i know which is the correct one?


i will need some exceptions like

IF(STATION_NAME == MAGIC_VALUE_1) //go ahead and call EnumDesktops
IF(DESKTOP_NAME == MAGIC_VALUE_2) //go ahead and call EnumDesktopWindows
IF(DESKTOP_WINDOW_NAME == MAGIC_VALUE_3) //go ahead and hook

but what can i use for these magic values?
0
 
cookreCommented:
That's why you first just log them all - so you can see what the machine looks like before logon and after logon.  It's also a good exercize just to have coded a full enumeration.

Under XP after logon, you may find a desktop called "Winlogon".  Within that desktop, you'll find one of the windows:

* "Windows Security" - what you see when you CTRL/ALT/DEL
* "Computer Locked" - what you see when you lock the workstation
* "Unlock Computer" - the window to enter a password to unlock the workstation

Alas, I don't recall the XP logon window name, but you should see it in your enumeration log.


In your service, do a full enumeration, say, every 30 seconds, making sure you include a time stamp with each full enumeration. Then boot and wait one minute after the logon screen comes up before entering you info, and wait another minute after the box settles down before stopping the service and examining the enumeration log.  It will be fairly easy to identify therein the name of the logon station, desktop, and window.
0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
cc16Author Commented:
for some annoying reason GetWindowText dosnt seem to work when called from this service...

should i just use the only window in Winlogon, or would a global hook work (a global hook would be easier)

here is what it looks like before i log in:




NEW WINDOW STATION: WinSta0
            New Desktop: Default
                        New Desktop Window: 0x000B0050
            New Desktop: Disconnect
            New Desktop: Winlogon
                        New Desktop Window: 0x000C0052


NEW WINDOW STATION: Service-0x0-3e7$
            New Desktop: WinSta0
                        New Desktop Window: 0x000B0050
            New Desktop: Service-0x0-3e7$
                        New Desktop Window: 0x000B0050
            New Desktop: Service-0x0-3e4$
                        New Desktop Window: 0x000B0050
            New Desktop: Service-0x0-3e5$
                        New Desktop Window: 0x000B0050
            New Desktop: SAWinSta
                        New Desktop Window: 0x000B0050


NEW WINDOW STATION: Service-0x0-3e4$
            New Desktop: WinSta0
                        New Desktop Window: 0x000B0050
            New Desktop: Service-0x0-3e7$
                        New Desktop Window: 0x000B0050
            New Desktop: Service-0x0-3e4$
                        New Desktop Window: 0x000B0050
            New Desktop: Service-0x0-3e5$
                        New Desktop Window: 0x000B0050
            New Desktop: SAWinSta
                        New Desktop Window: 0x000B0050


NEW WINDOW STATION: Service-0x0-3e5$
            New Desktop: WinSta0
                        New Desktop Window: 0x000B0050
            New Desktop: Service-0x0-3e7$
                        New Desktop Window: 0x000B0050
            New Desktop: Service-0x0-3e4$
                        New Desktop Window: 0x000B0050
            New Desktop: Service-0x0-3e5$
                        New Desktop Window: 0x000B0050
            New Desktop: SAWinSta
                        New Desktop Window: 0x000B0050


NEW WINDOW STATION: SAWinSta
            New Desktop: SADesktop
                        New Desktop Window: 0x000B0050
0
 
cookreCommented:
Do you create the service with:
CreateService(...
                    ,...
                    ,..
                    ,SERVICE_ALL_ACCESS
                    ,SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS      
                    ,SERVICE_AUTO_START
                    ,SERVICE_ERROR_NORMAL
                    ,...
                    ,null
                    ,0
                    ,null
                    ,null
                    ,null);




,SERVICE_ALL_ACCESS
                      ,SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS
                      ,SERVICE_AUTO_START
                      ,SERVICE_ERROR_NORMAL
0
 
cc16Author Commented:
i wasnt using CreateService...

is

ServiceStatus.dwServiceType = SERVICE_ALL_ACCESS | SERVICE_WIN32_OWN_PROCESS | SERVICE_INTERACTIVE_PROCESS | SERVICE_AUTO_START | SERVICE_ERROR_NORMAL;

ok?
0
 
cc16Author Commented:
Oh i get it... CreateService is to put the service into the database...
0
 
cc16Author Commented:
Ok I added it to the DB using CreateService.

Now GetWindowText returns ÝÝÝÝÝ each time (the length of the Ý string varies). I also couldnt help noticing that Ý is 11111111 in binary.

Anyways i dont think GetWindowText is so important.

The important thing is do I hook the only window in Winlogon, or should i use a global hook, or what?
0
 
cookreCommented:
I'll post some code later this weekend.

As far as the eventual hook is concerned, I believe it would have to be global.  A hook is either for a single thread or for all threads, and I believe GINA may very well have several threads to worry about.  With the global hook, you're not realy concerned with any specific thread, rather you'll want to see the messages associated with the specific window.
0
 
cookreCommented:
Slight delay - I lost a drive.
Should have everything rebuilt by tomorrow.
0
 
cookreCommented:
Here's some straight C code you can use to enumerate stations, desktops, and windows.

#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <winbase.h>
#include <winsvc.h>
#include <process.h>
#include <shellapi.h>
#include <time.h>
#include <sys/types.h>
#include <sys/timeb.h>


BOOL CALLBACK EnumWindowStaCallback(LPTSTR,LPARAM);
BOOL CALLBACK EnumDesktopCallback(LPTSTR,LPARAM);
BOOL CALLBACK EnumWindowsCallback(HWND,LPARAM);
void          PutMSG(char*);

char MyFullName[512];
char LOGFile[512];
char EXEFile[512];


////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////// main
/////////////////////////////////////////////////////////////////////////////////
void main(int argc ,char * argv[])
{
int   RC;
char  ts[512];

// Get our full name
GetModuleFileName(NULL,MyFullName,511);
sprintf(EXEFile,"%s",MyFullName);

// Put the log file in the same directory
MyFullName[strlen(MyFullName)-4]='\0';
sprintf(LOGFile,"%s.log",MyFullName);

RC=EnumWindowStations(EnumWindowStaCallback,0);
return;
}

/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////// EnumWindowStaCallback
///////////////////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK EnumWindowStaCallback(char* StationName,LPARAM lParam)
{
char ts[512];
HWINSTA hstation;
BOOL RC;

// Say who we found
PutMSG(" ");
sprintf(ts,"Station: <%s>",StationName);
PutMSG(ts);

// Enumerate this station's desktops
hstation=OpenWindowStation(StationName,true,STANDARD_RIGHTS_REQUIRED
                                           |WINSTA_ACCESSGLOBALATOMS
                                           |WINSTA_ENUMDESKTOPS
                                           |WINSTA_ENUMERATE
                                           |WINSTA_READATTRIBUTES
                                           |WINSTA_READSCREEN);
if (hstation==NULL)
   {
   sprintf(ts,"OpenWindowStation failure(%d)",GetLastError());
   PutMSG(ts);
   return true;
   }

RC=EnumDesktops(hstation,EnumDesktopCallback,lParam);
if (!RC)
   {
   sprintf(ts,"EnumDesktops failure(%d)",GetLastError());
   PutMSG(ts);
   }

CloseWindowStation(hstation);

return true;
}

///////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////// EnumDesktopCallback
///////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK EnumDesktopCallback(LPTSTR DesktopName,LPARAM lParam)
{
char ts[512];
HDESK hdesktop;
BOOL RC;

// Say who we found
sprintf(ts,"         Desktop: <%s>",DesktopName);
PutMSG(ts);

// Enumerate this desktop's windows
hdesktop=OpenDesktop(DesktopName,0,true,DESKTOP_ENUMERATE|DESKTOP_READOBJECTS);
if (hdesktop==NULL)
   {
   sprintf(ts,"         OpenDesktop failure(%d)",GetLastError());
   PutMSG(ts);
   return true;
   }
RC=EnumDesktopWindows(hdesktop,EnumWindowsCallback,lParam);
if (!RC)
   {
   sprintf(ts,"         EnumWindows failure(%d)",GetLastError());
   PutMSG(ts);
   }

CloseDesktop(hdesktop);
return true;
}

///////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////// EnumWindowsCallback
///////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK EnumWindowsCallback(HWND hwnd,LPARAM lParam)
{
int  TitleLen;
char Title[512];
char ts[512];

// Say who we found
Title[0]='\0';
TitleLen=GetWindowText(hwnd,Title,511);
Title[511]='\0';
sprintf(ts,"                  Window: <%s>",Title);
PutMSG(ts);

return true;
}

/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////// PutMSG
////////////////////////////////////////////////////////////////////////////////////
void PutMSG(char * msg)
{
FILE   * fh;
char   tmpbuf[128];
struct _timeb tstruct;

_strtime(tmpbuf);
_ftime( &tstruct );

fh=fopen(LOGFile,"a");
fprintf(fh,"%s.%u - %s\n",tmpbuf,tstruct.millitm,msg);
fclose(fh);
}




0
 
cc16Author Commented:
Wow that is a fast recovery time for a lost drive :O

the code is cool... it works n everything, but im getting

11:23:18.539 -          Desktop: <Winlogon>
11:23:18.539 -          OpenDesktop failure(5)

for the winlogon desktop. maybe it is because i was already logged on or something.

im *really* getting held up with excess assignments here, so i dont think i will be able to spend any time on this until the weekend.

when i do eventually get around to it, ill put your code into a service and see what's going on...

cheers,
southern curriez
0
 
cookreCommented:
>>Wow that is a fast recovery time for a lost drive :O
Actually, the drive crashed on Saturday and the restore and various installs today went faster than expected.
(sure am glad I daily incrementals)

Anyway, if the error 5 is accurate, that means it's a rights problem.  

0
 
cookreCommented:
I just ran it and got a similar result.

I posted the code from an old archive of working code from a little over a year ago - when it worked.  I haven't moved to XP SP2 yet, so I can only assume one of the many intermediate fixes, uh, 'fixed' something.

I'll look into it.
0
 
cookreCommented:
I put in in a LocalSystem service, and it was happy with WinLogon:

#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <winbase.h>
#include <winsvc.h>
#include <process.h>
#include <shellapi.h>
#include <time.h>
#include <sys/types.h>
#include <sys/timeb.h>
#include <winreg.h>


VOID WINAPI   SvcMain(DWORD,LPTSTR*);
VOID WINAPI   SvcHandler(DWORD);
VOID          Install(char*,char*);
BOOL CALLBACK EnumWindowStaCallback(LPTSTR,LPARAM);
BOOL CALLBACK EnumDesktopCallback(LPTSTR,LPARAM);
BOOL CALLBACK EnumWindowsCallback(HWND,LPARAM);

void          PutMSG(char*);

char MyFullName[512];
char LOGFile[512];
char EXEFile[512];
char SvcName[512];


// Service management
SERVICE_STATUS         SvcStatusInfo;
SERVICE_STATUS_HANDLE  SvcStatusHandle;
SERVICE_TABLE_ENTRY    DispatchTable[]={{"EnumSvc",SvcMain},{NULL, NULL}};



////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////// main
/////////////////////////////////////////////////////////////////////////////////
void main(int argc ,char * argv[])
{
strcpy(SvcName,"EnumSvc");

// Get our full name
GetModuleFileName(NULL,MyFullName,511);
sprintf(EXEFile,"%s",MyFullName);

// Put the log file in the same directory
MyFullName[strlen(MyFullName)-4]='\0';
sprintf(LOGFile,"%s.log",MyFullName);

// -i parm says to install and start the service
if (argc>1)
   {
   if (0==stricmp(argv[1],"-i"))
      {
      Install(EXEFile,SvcName);
      return;
      }
   }

// We're not installing - this is the real thing
if (!StartServiceCtrlDispatcher(DispatchTable))
   {
   char ts[512];
   sprintf(ts,"StartServiceCtrlDispatcher failure (%d)",GetLastError());
   PutMSG(ts);
   }
return;
}

////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////// Install
////////////////////////////////////////////////////////////////////////////////////
VOID Install(char* ExePath, char* SvcName)
{
SC_HANDLE SCM,svc;
char      ts[256];

PutMSG("Installing");

SCM=OpenSCManager(NULL,NULL,SC_MANAGER_CREATE_SERVICE);
if (SCM==0)
   {
   sprintf(ts,"OpenSCManager failure (%d)",GetLastError());
   PutMSG(ts);
   }
else
   {
   svc=CreateService(SCM
                    ,SvcName
                    ,SvcName
                    ,SERVICE_ALL_ACCESS
                    ,SERVICE_WIN32_OWN_PROCESS|SERVICE_INTERACTIVE_PROCESS
                    ,SERVICE_AUTO_START
                    ,SERVICE_ERROR_NORMAL
                    ,ExePath
                    ,NULL
                    ,NULL
                    ,NULL
                    ,".\\LocalSystem"
                    ,"");
   if (svc==0)
      {
      sprintf(ts,"CreateService failure (%d)",GetLastError());
      PutMSG(ts);
      }
   else
      {
      PutMSG("Install successful");
      if (!StartService(svc,0,NULL))
         {
         sprintf(ts,"StartService failure (%d)",GetLastError());
         PutMSG(ts);
         }
      CloseServiceHandle(svc);
      }
   CloseServiceHandle(SCM);
   }
}

//////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////// SvcMain
//////////////////////////////////////////////////////////////////////////////
VOID WINAPI SvcMain(DWORD SvcArgc,LPTSTR *SvcArgv )
{
int   RC;
DWORD status=0;
char  ts[512];

SvcStatusInfo.dwServiceType             =SERVICE_WIN32;
SvcStatusInfo.dwCurrentState            =SERVICE_START_PENDING;
SvcStatusInfo.dwControlsAccepted        =SERVICE_ACCEPT_STOP
                                        |SERVICE_ACCEPT_SHUTDOWN
                                        |SERVICE_ACCEPT_PAUSE_CONTINUE;
SvcStatusInfo.dwWin32ExitCode           =0;
SvcStatusInfo.dwServiceSpecificExitCode =0;
SvcStatusInfo.dwCheckPoint              =0;
SvcStatusInfo.dwWaitHint                =0;

SvcStatusHandle=RegisterServiceCtrlHandler(SvcName,SvcHandler);
if (SvcStatusHandle==0)
   {
   sprintf(ts,"RegisterServiceCtrlHandler failure (%d)",GetLastError());
   PutMSG(ts);
   //@@@@@ shut down
   return;
   }

// oopsie?
status=GetLastError();
if (status!=NO_ERROR)
   {
   SvcStatusInfo.dwCurrentState            =SERVICE_STOPPED;
   SvcStatusInfo.dwCheckPoint              =0;
   SvcStatusInfo.dwWaitHint                =0;
   SvcStatusInfo.dwWin32ExitCode           =status;
   SvcStatusInfo.dwServiceSpecificExitCode =0;

   SetServiceStatus(SvcStatusHandle,&SvcStatusInfo);
   return;
   }

// Initialization complete - report running status
SvcStatusInfo.dwCurrentState =SERVICE_RUNNING;
SvcStatusInfo.dwCheckPoint   =0;
SvcStatusInfo.dwWaitHint     =0;

if (!SetServiceStatus(SvcStatusHandle,&SvcStatusInfo))
   {
   sprintf(ts,"SetServiceStatus failure(%d)",GetLastError());
   PutMSG(ts);
   //@@@@@ shut down
   }

PutMSG("SvcMain Initialized");
while (true)
      {
      RC=EnumWindowStations(EnumWindowStaCallback,0);
      sprintf(ts,"RC=%d LE=%d",RC,GetLastError());
      Sleep(10000);
      }
}

/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////// EnumWindowStaCallback
///////////////////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK EnumWindowStaCallback(char* StationName,LPARAM lParam)
{
char ts[512];
HWINSTA hstation;
BOOL RC;

// Say who we found
PutMSG(" ");
sprintf(ts,"Station: %s",StationName);
PutMSG(ts);

// Enumerate this station's desktops
hstation=OpenWindowStation(StationName,true,STANDARD_RIGHTS_REQUIRED
                                           |WINSTA_ACCESSGLOBALATOMS
                                           |WINSTA_ENUMDESKTOPS
                                           |WINSTA_ENUMERATE
                                           |WINSTA_READATTRIBUTES
                                           |WINSTA_READSCREEN);
if (hstation==NULL)
   {
   sprintf(ts,"OpenWindowStation failure(%d)",GetLastError());
   PutMSG(ts);
   return true;
   }

RC=EnumDesktops(hstation,EnumDesktopCallback,lParam);
if (!RC)
   {
   sprintf(ts,"EnumDesktops failure(%d)",GetLastError());
   PutMSG(ts);
   }

CloseWindowStation(hstation);

return true;
}

///////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////// EnumDesktopCallback
///////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK EnumDesktopCallback(LPTSTR DesktopName,LPARAM lParam)
{
char ts[512];
HDESK hdesktop;
BOOL RC;

// Say who we found
sprintf(ts,"         Desktop: %s",DesktopName);
PutMSG(ts);

// Enumerate this desktop's windows
hdesktop=OpenDesktop(DesktopName,0,true,DESKTOP_ENUMERATE|DESKTOP_READOBJECTS|MAXIMUM_ALLOWED);
if (hdesktop==NULL)
   {
   sprintf(ts,"         OpenDesktop failure(%d)",GetLastError());
   PutMSG(ts);
   return true;
   }
RC=EnumDesktopWindows(hdesktop,EnumWindowsCallback,lParam);
if (!RC)
   {
   sprintf(ts,"         EnumWindows failure(%d)",GetLastError());
   PutMSG(ts);
   }

CloseDesktop(hdesktop);
return true;
}

///////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////// EnumWindowsCallback
///////////////////////////////////////////////////////////////////////////////////////
BOOL CALLBACK EnumWindowsCallback(HWND hwnd,LPARAM lParam)
{
int  TitleLen;
char Title[512];
char ts[512];

// Say who we found
Title[0]='\0';
TitleLen=GetWindowText(hwnd,Title,511);
Title[511]='\0';
sprintf(ts,"                  Window: %s",Title);
PutMSG(ts);

return true;
}

///////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////// SvcHandler
//////////////////////////////////////////////////////////////////////////////
VOID WINAPI SvcHandler(DWORD fdwControl)
{
char ts[256];

switch (fdwControl)
       {
       case SERVICE_CONTROL_STOP:

       case SERVICE_CONTROL_SHUTDOWN:

            SvcStatusInfo.dwWin32ExitCode = 0;
            SvcStatusInfo.dwCurrentState  = SERVICE_STOPPED;
            SvcStatusInfo.dwCheckPoint    = 0;
            SvcStatusInfo.dwWaitHint      = 0;
            SetServiceStatus(SvcStatusHandle,&SvcStatusInfo);

            PutMSG("Stopped");
            return;

       case SERVICE_CONTROL_INTERROGATE:
            break;

       default:
               sprintf(ts,"Unhandled message (%d)",fdwControl);
               PutMSG(ts);
               break;
       };
if (!SetServiceStatus(SvcStatusHandle,  &SvcStatusInfo))
   {
   sprintf(ts,"SetServiceStatus failure (%d)",GetLastError());
   PutMSG(ts);
   }
}

/////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////// PutMSG
////////////////////////////////////////////////////////////////////////////////////
void PutMSG(char * msg)
{
FILE   * fh;
char   tmpbuf[128];
struct _timeb tstruct;

_strtime(tmpbuf);
_ftime( &tstruct );

fh=fopen(LOGFile,"a");
fprintf(fh,"%s.%u - %s\n",tmpbuf,tstruct.millitm,msg);
fclose(fh);
}


0
 
cc16Author Commented:
i finally managed to get on top of my workload, so my spare time is gradually increasing...

i tested out (my modified c++ code) in a service and everything (including GetWindowText) worked fine...

i wrote a new code which was basically

if(WINDOW_STATION == "WinSta0")
{
        if(DESKTOP == "Winlogon")
        {
                launch_cmd();
        }
}

void launch_cmd()
{
     STARTUPINFO si ;
     PROCESS_INFORMATION pi ;
     
     ZeroMemory(&si, sizeof(si));
     si.cb = sizeof (STARTUPINFO);
     si.wShowWindow = SW_SHOW;
     si.lpDesktop = THE_HDESK_OF_WINLOGON;
     CreateProcess (NULL, "cmd.exe"  , NULL , NULL , FALSE , 0 , NULL , NULL, &si, &pi);
}

however it didnt work...

any ideaz why?
0
 
cc16Author Commented:
* replace THE_HDESK_OF_WINLOGON with THE_STRING_NAME_OF_WINLOGON
0
 
cc16Author Commented:
i got it to work

thanks for the help :)
0
 
cookreCommented:
Well, with all that spare time they can dump more work on you...
0
 
tmakaroCommented:
Help, I am using that code example above in a windows service in Vista and i am not seeing my windows. here is the output i get:

17:13:24.988 -  
17:13:24.989 - Station: <WinSta0>
17:13:24.989 -          Desktop: <Default>
17:13:24.989 -                   Window: <Wrapper Controlled JVM Console ID 114132523 (Do not close)>
17:13:24.990 -                   Window: <>
17:13:24.990 -                   Window: <>
17:13:24.990 -                   Window: <Default IME>
17:13:24.991 -                   Window: <Default IME>
17:13:24.991 -          Desktop: <Disconnect>
17:13:24.991 -          EnumWindows failure(183)
17:13:24.991 -          Desktop: <Winlogon>
17:13:24.992 -          EnumWindows failure(183)
17:13:24.992 -  
17:13:24.992 - Station: <Service-0x0-3e7$>
17:13:24.993 - OpenWindowStation failure(5)
17:13:24.993 -  
17:13:24.993 - Station: <Service-0x0-3e4$>
17:13:24.994 - OpenWindowStation failure(5)
17:13:24.994 -  
17:13:24.994 - Station: <Service-0x0-3e5$>
17:13:24.995 - OpenWindowStation failure(5)
17:13:24.995 -  
17:13:24.996 - Station: <msswindowstation>
17:13:24.996 - OpenWindowStation failure(5)
0

Featured Post

Concerto Cloud for Software Providers & ISVs

Can Concerto Cloud Services help you focus on evolving your application offerings, while delivering the best cloud experience to your customers? From DevOps to revenue models and customer support, the answer is yes!

Learn how Concerto can help you.

  • 10
  • 9
Tackle projects and never again get stuck behind a technical roadblock.
Join Now