• Windows服务(system权限)程序显示界面与用户交互,Session0通知Session1里弹出对话框(真的很牛) good


     

    1、VC2008中编写“Windows服务”(Windows Service)程序

    vc2008下新建一个 ATL 项目-》 选择创建一个“服务”类型的ATL 项目TestService,将生成如下代码,
     
    class CTestServiceModule : public CAtlServiceModuleT< CTestServiceModule, IDS_SERVICENAME >
    {
    public :
           DECLARE_LIBID(LIBID_TestServiceLib )
           DECLARE_REGISTRY_APPID_RESOURCEID (IDR_TESTSERVICE, "{1FF78006-B225-4CC0-A7DE-E0C9D31C9937}" )
           HRESULT InitializeSecurity () throw()
          {
                 // TODO : 调用CoInitializeSecurity 并为服务提供适当的
                 // 安全设置
                 // 建议- PKT 级别的身份验证、
                 // RPC_C_IMP_LEVEL_IDENTIFY 的模拟级别
                 // 以及适当的非NULL 安全说明符。
     
                 return S_OK ;
          }
           //重写这个函数来启动任务啦
           HRESULT Run (int nShowCmd = SW_HIDE ) throw()
          {
                 HRESULT hr = S_OK;
                 hr = __super ::PreMessageLoop( nShowCmd);
                 if (hr == S_OK)
                {
                       if (m_bService )
                      {
                             //需要定义#define _ATL_NO_COM_SUPPORT才能启动服务时走到这里
                             //可以在这里启动线程,或者什么其他东西来做自己的工作的啦
                             //这里是什么都没有做了,只输出一条信息
     
                             LogEvent(_T ("widebright 的服务启动咯,呵呵 "));
                             SetServiceStatus(SERVICE_RUNNING );
                      }
                       //进入消息循环,不停的处理消息,可能最后分发到 Handler去处理,调用了OnShutdown等函数的。
                       __super::RunMessageLoop ();
                }
                 if (SUCCEEDED (hr))
                {
                       hr = __super ::PostMessageLoop();
                }
     
                 //可以在适当的时候调用Uninstall函数来卸载掉服务
                 //__super::Uninstall();
                 return hr ;
          }
           //重写,服务退出处理
           void OnShutdown () throw()
          {
                 LogEvent(_T ("TestService 的服务退出咯,一点都不好玩呵呵 "));
          }
     
    };
     
    CTestServiceModule _AtlModule;
     
     
     
    //
    extern "C" int WINAPI _tWinMain (HINSTANCE , HINSTANCE ,
                                    LPTSTR  , int nShowCmd)
    {
        return _AtlModule .WinMain( nShowCmd);
    }
      我只要根据需要重写相应的函数来实现自己想要的功能就行了,比如你想创建的“服务”随系统启动,可以重写CAtlServiceModuleT   的Install函数,把里面的CreateService函数的参数修改一下,例如添加与用户交互可以使用 SERVICE_INTERACTIVE_PROCESS,具体可以去MSDN上查找CreateService这个API的说明。

    如果想处理服务 停止和启动的动作,可以参考CAtlServiceModuleT 的源代码重写OnStop ()等函数。我上面简单到重写了Run函数,输出一条“事件”其实具体 工作是可以放到这里来完成的吧。

    编译,生成程序之后就可以测试了,

    执行“TestService -/Service” 就可以把服务注册到系统了,命令行参数其实是在CAtlServiceModuleT::ParseCommandLine 这个函数里面处理,可以去看一下,必要的话重写也是可以的,加上调用 UnInstall来删除服务的代码也很不错的吧。

    注册后,就看用“sc start” 或者“net start” 等命令来操纵服务了。在“服务”控制器里面控制与可以:如图


    这时候在Run函数中启动一个Notepad.exe,此时没有界面显示,在xp下可以使用下面的方法实现notepad与用户的交互:
    //for xp system
    DWORD _stdcall LaunchAppIntoSession0( LPTSTR lpCommand )
    {
           ////////////////////////////////////////////system show dlg////////////////////
     
           HDESK hdeskCurrent ;
           HDESK hdesk ;
           HWINSTA hwinstaCurrent ;
           HWINSTA hwinsta ;
           hwinstaCurrent = GetProcessWindowStation ();
           if (hwinstaCurrent == NULL)
          {
                 return FALSE ;
          }
           hdeskCurrent = GetThreadDesktop (GetCurrentThreadId());
           if (hdeskCurrent == NULL){
                 return FALSE ;
          }
           //打开winsta0
           //打开winsta0
           hwinsta = OpenWindowStation (L"Winsta0" , FALSE, WINSTA_ALL_ACCESS);
           //          WINSTA_ACCESSCLIPBOARD|
           //          WINSTA_ACCESSGLOBALATOMS |
           //          WINSTA_ENUMDESKTOPS |
           //          WINSTA_CREATEDESKTOP |
           //          WINSTA_CREATEDESKTOP |
           //          WINSTA_ENUMERATE |
           //          WINSTA_EXITWINDOWS |
           //          WINSTA_READATTRIBUTES |
           //          WINSTA_READSCREEN |
           //          WINSTA_WRITEATTRIBUTES);
           if (hwinsta == NULL){
                 return FALSE ;
          }
           if (!SetProcessWindowStation (hwinsta))
          {
                 return FALSE ;
          }
           //打开desktop
           hdesk = OpenDesktop (L"default" , 0, FALSE,
                 DESKTOP_CREATEMENU |
                 DESKTOP_CREATEWINDOW |
                 DESKTOP_ENUMERATE|
                 DESKTOP_HOOKCONTROL|
                 DESKTOP_JOURNALPLAYBACK |
                 DESKTOP_JOURNALRECORD |
                 DESKTOP_READOBJECTS |
                 DESKTOP_SWITCHDESKTOP |
                 DESKTOP_WRITEOBJECTS);
           if (hdesk == NULL){
                 return FALSE ;
          }
           SetThreadDesktop(hdesk );
           ////////////////////////////////////////////end of system show dlg////////////////////
           
           STARTUPINFO si = { sizeof( si) }; 
           SECURITY_ATTRIBUTES saProcess , saThread
           PROCESS_INFORMATION piProcessB , piProcessC
     
     
           // Prepare to spawn Process B from Process A. 
           // The handle identifying the new process 
           // object should be inheritable. 
           saProcess.nLength = sizeof( saProcess); 
           saProcess.lpSecurityDescriptor = NULL
           saProcess.bInheritHandle = TRUE
     
           // The handle identifying the new thread 
           // object should NOT be inheritable. 
           saThread.nLength = sizeof( saThread);
           saThread.lpSecurityDescriptor = NULL;
           saThread.bInheritHandle = FALSE
     
           CreateProcess(NULL , lpCommand, & saProcess, &saThread 
                 FALSE, 0, NULL , NULL, & si, &piProcessB ); 
     
     
           if (!SetProcessWindowStation (hwinstaCurrent))
                 return FALSE ;
           if (!SetThreadDesktop (hdeskCurrent))
                 return FALSE ;
           if (!CloseWindowStation (hwinsta))
                 return FALSE ;
           if (!CloseDesktop (hdesk))
                 return FALSE ;
           return TRUE ;
    }

    这种方法的关键是OpenWindowStation、SetProcessWindowStation、OpenDesktop和SetThreadDesktop这四个函数。这种方法的思路是:当前进程所处于的Session必须有界面交互能力,这样才能显示出对话框。由于第一个交互式用户会登录到拥有WinSta0的Session 0,所以,强制性地把服务所在的进程与WinSta0关联起来,并且打开当前的桌面,把工作线程挂到该桌面上,就可以显示出对话框。

    这种方法在WinXP和Windows2003下工作得不错,很遗憾,在Vista和Windows2008下,一旦执行到OpenWindowStation,试图代开WinSta0工作站时,程序就会出异常。

    首先了解一下程序要具备怎样的条件才能与界面交互。Windows提供了三类对象:用户界面对象(User Interface)、GDI对象和内核对象。内核对象有安全性,而前两者没有。为了对前两者提供安全性,通过工作站对象(Window station)和桌面对象(Desktop)来管理用户界面对象,因为工作站对象和桌面对象有安全特性。简单说来,工作站是一个带有安全特性的对象,它与进程相关联,包含了一个或多个桌面对象。当工作站对象被创建时,它被关联到调用进程上,并且被赋给当前Session。交互式工作站WinSta0,是唯一一个可以显示用户界面,接受用户输入的工作站。它被赋给交互式用户的登录Session,包含了键盘、鼠标和显示设备。所有其他工作站都是非交互式的,这就意味着它们不能显示用户界面,不能接受用户的输入。当用户登录到一台启用了终端服务的计算机上时,每个用户都会启动一个Session。每个Session都会与自己的交互式工作站相联系。桌面是一个带有安全特性的对象,被包含在一个窗口工作站对象中。一个桌面对象有一个逻辑的显示区域,包含了诸如窗口、菜单、钩子等等这样的用户界面对象。

     

    Vista之前,之所以可以通过打开Winsta0和缺省桌面显示对话框,是因为不管是服务还是第一个登录的交互式用户,都是登录到Session 0中。因此,服务程序可以通过强制打开WinSta0和桌面来获得交互能力。

    然而,在Vista和Windows2008中,Session 0专用于服务和其他不与用户交互的应用程序。第一个登录进来,可以进行交互式操作的用户被连到Session 1上。第二个登录进行的用户被分配给Session 2,以此类推。Session 0完全不支持要与用户交互的进程。如果采取在服务进程中启动子进程来显示对话框,子对话框将无法显示;如果采取用OpenWindowStation系统API打开WinSta0的方法,函数调用会失败。总之,Vista和Windows2008已经堵上了在Session 0中产生界面交互的路。这就是原因所在。

     

    那么,是否真的没法在服务中弹出对话框了呢?对于服务进程自身来说,确实如此,操作系统已经把这条路堵上了。但是,我们想要的并不是“在服务进程中弹出对话框”,我们想要的不过是“当服务出现某些状况的时候,在桌面上弹出对话框”。既然在Session 0中无法弹出对话框,而我们看到的桌面是Session X,并非Session 0,很自然的一个想法是:能不能让Session 0通知其他的Session,让当前桌面正显示着的Session弹一个对话框呢?

    幸运的是,还真可以这样做。
     
    //for win7
    DWORD _stdcall LaunchAppIntoDifferentSession( LPTSTR lpCommand )
    {
           DWORD dwRet = 0;
           PROCESS_INFORMATION pi ;
           STARTUPINFO si ;
           DWORD dwSessionId ;
           HANDLE hUserToken = NULL;
           HANDLE hUserTokenDup = NULL;
           HANDLE hPToken = NULL;
           HANDLE hProcess = NULL;
           DWORD dwCreationFlags ;
     
           HMODULE hInstKernel32     = NULL;
           typedef DWORD (WINAPI * WTSGetActiveConsoleSessionIdPROC)();
           WTSGetActiveConsoleSessionIdPROC WTSGetActiveConsoleSessionId = NULL;
     
           hInstKernel32 = LoadLibrary (L"Kernel32.dll" );
     
           if (!hInstKernel32 
          {
                 return FALSE ;
          }
     
           OutputDebugString(L "LaunchAppIntoDifferentSession 1 " );
           WTSGetActiveConsoleSessionId = (WTSGetActiveConsoleSessionIdPROC )GetProcAddress( hInstKernel32,"WTSGetActiveConsoleSessionId" );
     
     
           // Log the client on to the local computer.
           dwSessionId = WTSGetActiveConsoleSessionId ();
     
           do
          {
                 WTSQueryUserToken( dwSessionId ,&hUserToken );
                 dwCreationFlags = NORMAL_PRIORITY_CLASS | CREATE_NEW_CONSOLE;
                 ZeroMemory( &si , sizeof( STARTUPINFO ) );
                 si.cb = sizeof( STARTUPINFO );
                 si.lpDesktop = L"winsta0\default" ;
                 ZeroMemory( &pi , sizeof( pi) );
                 TOKEN_PRIVILEGES tp ;
                 LUID luid ;
     
                 if( !::OpenProcessToken ( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY
                      | TOKEN_DUPLICATE | TOKEN_ASSIGN_PRIMARY | TOKEN_ADJUST_SESSIONID
                      | TOKEN_READ | TOKEN_WRITE , &hPToken ) )
                {
                       dwRet = GetLastError ();
                       break;
                }
                 else;
     
                 if ( !LookupPrivilegeValue ( NULL, SE_DEBUG_NAME, &luid ) )
                {
                       dwRet = GetLastError ();
                       break;
                }
                 else;
                 tp.PrivilegeCount =1;
                 tp.Privileges [0].Luid = luid;
                 tp.Privileges [0].Attributes = SE_PRIVILEGE_ENABLED;
     
                 if( !DuplicateTokenEx ( hPToken, MAXIMUM_ALLOWED, NULL , SecurityIdentification , TokenPrimary, & hUserTokenDup ) )
                {
                       dwRet = GetLastError ();
                       break;
                }
                 else;
     
                 //Adjust Token privilege
                 if( !SetTokenInformation ( hUserTokenDup,TokenSessionId ,(void*)& dwSessionId,sizeof (DWORD) ) )
                {
                       dwRet = GetLastError ();
                       break;
                }
                 else;
     
                 if( !AdjustTokenPrivileges ( hUserTokenDup, FALSE, &tp , sizeof(TOKEN_PRIVILEGES ), (PTOKEN_PRIVILEGES) NULL, NULL ) )
                {
                       dwRet = GetLastError ();
                       break;
                }
                 else;
     
                 LPVOID pEnv =NULL;
                 if( CreateEnvironmentBlock ( &pEnv, hUserTokenDup, TRUE ) )
                {
                       dwCreationFlags|=CREATE_UNICODE_ENVIRONMENT ;
                }
                 else pEnv =NULL;
                
     
                 // Launch the process in the client's logon session.
                 if( CreateProcessAsUser (    hUserTokenDup,    // client's access token
                       NULL,        // file to execute
                       lpCommand,        // command line
                       NULL,            // pointer to process SECURITY_ATTRIBUTES
                       NULL,            // pointer to thread SECURITY_ATTRIBUTES
                       FALSE,            // handles are not inheritable
                       dwCreationFlags,// creation flags
                       pEnv,          // pointer to new environment block
                       NULL,          // name of current directory
                      & si,            // pointer to STARTUPINFO structure
                      & pi            // receives information about new process
                      ) )
                {
                }
                 else
                {
                       dwRet = GetLastError ();
                       break;
                }
          }
           while( 0 );
     
           //Perform All the Close Handles task
           if( NULL != hUserToken )
          {
                 CloseHandle( hUserToken );
          }
           else;
     
           if( NULL != hUserTokenDup)
          {
                 CloseHandle( hUserTokenDup );
          }
           else;
     
           if( NULL != hPToken )
          {
                 CloseHandle( hPToken );
          }
           else;
     
           return dwRet ;
    }
     
    启动服务后显示了system权限的Notepad.exe,并且可以与用户进行交互
    Windows服务(Windows <wbr>Service,system权限)程序显示界面与用户交互(xp,win7通用)


     
    当然,在本例子启动进程的地方创建一个对话框也是可以
    http://blog.sina.com.cn/s/blog_488cff5201017yug.html
  • 相关阅读:
    让资源管理器不显示最近常用文件夹
    票房实际是屌丝血
    为什么读了很多书,还是过不好这一生?
    抱怨就像呕吐
    finally关键字小复习
    Java中菜单组件
    Java的GUI窗体出现乱码解决方法
    Java中GUI的默认窗体布局 和 常见的窗体布局方案
    适配器类(便利类)的由来:当你自己写的类中想用某个接口中个别方法的时候(注意:不是所有的方法),肿么办?
    技术管理者工作成效评估表
  • 原文地址:https://www.cnblogs.com/findumars/p/6147915.html
Copyright © 2020-2023  润新知