Windows服务(Windows Service,system权限)程序显示界面与用户交互(xp,win7通用)

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 );
}
2、我只要根据需要重写相应的函数来实现自己想要的功能就行了
比如你想创建的“服务”随系统启动,可以重写CAtlServiceModuleT 的Install函数,把里面的CreateService函数的参数修改一下,例如添加与用户交互可以使用 SERVICE_INTERACTIVE_PROCESS,具体可以去MSDN上查找CreateService这个API的说明。

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

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

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

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


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


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

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

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

然而,在VistaWindows2008中,Session 0专用于服务和其他不与用户交互的应用程序。第一个登录进来,可以进行交互式操作的用户被连到Session 1上。第二个登录进行的用户被分配给Session 2,以此类推。Session 0完全不支持要与用户交互的进程。如果采取在服务进程中启动子进程来显示对话框,子对话框将无法显示;如果采取用OpenWindowStation系统API打开WinSta0的方法,函数调用会失败。总之,VistaWindows2008已经堵上了在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\n"  );
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  ;
}

5、启动服务后显示了system权限的Notepad.exe,并且可以与用户进行交互



当然,在本例子启动进程的地方创建一个对话框也是可以显示对话框的。
  673人阅读   评论(1)   收藏   举报

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 );
}
2、我只要根据需要重写相应的函数来实现自己想要的功能就行了
比如你想创建的“服务”随系统启动,可以重写CAtlServiceModuleT 的Install函数,把里面的CreateService函数的参数修改一下,例如添加与用户交互可以使用 SERVICE_INTERACTIVE_PROCESS,具体可以去MSDN上查找CreateService这个API的说明。

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

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

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

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


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


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

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

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

然而,在VistaWindows2008中,Session 0专用于服务和其他不与用户交互的应用程序。第一个登录进来,可以进行交互式操作的用户被连到Session 1上。第二个登录进行的用户被分配给Session 2,以此类推。Session 0完全不支持要与用户交互的进程。如果采取在服务进程中启动子进程来显示对话框,子对话框将无法显示;如果采取用OpenWindowStation系统API打开WinSta0的方法,函数调用会失败。总之,VistaWindows2008已经堵上了在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\n"  );
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  ;
}

5、启动服务后显示了system权限的Notepad.exe,并且可以与用户进行交互



当然,在本例子启动进程的地方创建一个对话框也是可以显示对话框的。
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值