服务器之家:专注于服务器技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C# - Windows服务编写(Windows Service,system权限)程序显示界面与用户交互(xp,win7通用)

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

2021-11-18 11:53C#教程网 C#

这篇文章主要介绍了Windows服务编写Windows Service,system权限程序显示界面与用户交互(xp,win7通用) ,需要的朋友可以参考下

1、vc2008中编写“windows服务”(windows service)程序

vc2008下新建一个 atl 项目-》 选择创建一个“服务”类型的atl 项目testservice,将生成如下代码,

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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” 等命令来操纵服务了。在“服务”控制器里面控制与可以:如图

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

3、这时候在run函数中启动一个notepad.exe,此时没有界面显示,在xp下可以使用下面的方法实现notepad与用户的交互:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
//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关联起来,并且打开当前的桌面,把工作线程挂到该桌面上,就可以显示出对话框。

4、这种方法在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弹一个对话框呢?

幸运的是,还真可以这样做。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
//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,并且可以与用户进行交互

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


当然,在本例子启动进程的地方创建一个对话框也是可以显示对话框的。

延伸 · 阅读

精彩推荐
  • C#C#微信公众号与订阅号接口开发示例代码

    C#微信公众号与订阅号接口开发示例代码

    这篇文章主要介绍了C#微信公众号与订阅号接口开发示例代码,结合实例形式简单分析了C#针对微信接口的调用与处理技巧,需要的朋友可以参考下...

    smartsmile20127762021-11-25
  • C#SQLite在C#中的安装与操作技巧

    SQLite在C#中的安装与操作技巧

    SQLite,是一款轻型的数据库,用于本地的数据储存。其优点有很多,下面通过本文给大家介绍SQLite在C#中的安装与操作技巧,感兴趣的的朋友参考下吧...

    蓝曈魅11162022-01-20
  • C#C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    C#设计模式之Strategy策略模式解决007大破密码危机问题示例

    这篇文章主要介绍了C#设计模式之Strategy策略模式解决007大破密码危机问题,简单描述了策略模式的定义并结合加密解密算法实例分析了C#策略模式的具体使用...

    GhostRider10972022-01-21
  • C#三十分钟快速掌握C# 6.0知识点

    三十分钟快速掌握C# 6.0知识点

    这篇文章主要介绍了C# 6.0的相关知识点,文中介绍的非常详细,通过这篇文字可以让大家在三十分钟内快速的掌握C# 6.0,需要的朋友可以参考借鉴,下面来...

    雨夜潇湘8272021-12-28
  • C#VS2012 程序打包部署图文详解

    VS2012 程序打包部署图文详解

    VS2012虽然没有集成打包工具,但它为我们提供了下载的端口,需要我们手动安装一个插件InstallShield。网上有很多第三方的打包工具,但为什么偏要使用微软...

    张信秀7712021-12-15
  • C#如何使用C#将Tensorflow训练的.pb文件用在生产环境详解

    如何使用C#将Tensorflow训练的.pb文件用在生产环境详解

    这篇文章主要给大家介绍了关于如何使用C#将Tensorflow训练的.pb文件用在生产环境的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴...

    bbird201811792022-03-05
  • C#深入理解C#的数组

    深入理解C#的数组

    本篇文章主要介绍了C#的数组,数组是一种数据结构,详细的介绍了数组的声明和访问等,有兴趣的可以了解一下。...

    佳园9492021-12-10
  • C#利用C#实现网络爬虫

    利用C#实现网络爬虫

    这篇文章主要介绍了利用C#实现网络爬虫,完整的介绍了C#实现网络爬虫详细过程,感兴趣的小伙伴们可以参考一下...

    C#教程网11852021-11-16