使用信使服务批量发送信息

2021.2.2更新:读取AD中计算机名的时候排除了已禁用的计算机

信使服务

信使服务在XP系统中是用net send来工作的,但从Win7开始改成了msg命令,后面的批处理中我以msg命令为例。如果是老版系统请参照修改。

信使服务在win7之后的名称是Terminal Server。出于安全考虑,这个服务默认是不接收来自其他计算机的信使信息的。要打开接收信息的开关,需要进入注册表
HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Terminal Server
找到值 AllowRemoteRPC ,将它的数值改为 1 即可。
发送信使信息的电脑不用打开这个开关,但是接收信息的一定要打开。
网上有些资料说信使服务需要接收端对发送端的身份进行确认才能正常接收,所以要建立用户凭据。因为我是在AD环境中的,所有的用户都能相互验证身份,所以没有这个情况。工作组中是否需要凭据我并没有验证过。

设计需求

  1. 虽然说是批量发送信息,但是偶尔也会有需要单独发信息的时候。所以需要获取一次输入来选择是群发还是单发,然后再进入不同的流程。原本以为用批处理的goto会比较方便。
  2. 群发消息 时的接收端计算机有哪些,如何保证每次接收信息的客户端列表都是完整的?如果继续使用批处理,估计需要维护一个客户端列表,并且随时进行更新,会比较麻烦。如果有AD的话,可以从组织中直接获取计算机列表,而且每次都是单独去获取一次清单,可以保证在使用程序时的清单是完整且最新的。

VBS设计

需求2决定了我们只能使用VBS,但是对于需求1,我们有两种设计思路:

  1. 全部采用IF判断
    下面展示完整 使用IF来跳转的代码
'主选单
Choice = InputBox ("请输入您想发布的通知类型:"&Chr(10)&Chr(10)&"1.发送信息给指定计算机"&Chr(10)&Chr(10)&"2.发送信息给组织内所有计算机","通知发布程序")

If Choice = "2" Then	'向所有计算机发送通知
	'通过OU来定位计算机列表,可以用input获取,也可以写死在代码里面
	ADPath = InputBox ("请输入您要发通知的域组织","通知发布程序","ou=dep,dc=local,dc=domain")
	Message = InputBox ("请输入通知内容:","通知发布程序")
	If Message = "" Then
		MsgBox "没有通知内容!本程序即将退出!"
	else	'向AD获取计算机名
		Const ADS_SCOPE_SUBTREE = 2
		Set objConnection = CreateObject("ADODB.Connection")
		Set objCommand =   CreateObject("ADODB.Command")
		objConnection.Provider = "ADsDSOObject"
		objConnection.Open "Active Directory Provider"'

		Set objCOmmand.ActiveConnection = objConnection
		objCommand.CommandText = "Select Name, Location from 'LDAP://"&ADPath&"' " & "Where objectClass='computer' AND userAccountControl='4096'"  
		objCommand.Properties("Page Size") = 1000
		objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE 
		Set objRecordSet = objCommand.Execute
		objRecordSet.MoveFirst

		'定义保存结果
		Set oFSO = CreateObject("Scripting.FileSystemObject")
		Set of = oFSO.CreateTextFile("通知结果.txt", True, True)
	
		Do Until objRecordSet.EOF
		On Error Resume Next
		PC1 = objRecordSet.Fields("Name").Value	'将获取到的计算机名赋值给变量
		'检测计算机是否可以域通讯
		Set objWMILocator = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & PC1 & "\root\cimv2")
		If Err = 0 Then	'向可通讯计算机发通知
			set WSHshell=createobject("wscript.shell")
			WSHshell.run "msg /time:1800 /server:"&PC1&" * "& Message,0
			of.writeline "向," & PC1 & ",发送了信息:"& Message
		Else	'记录无法通讯的计算机
		of.writeline "向," & PC1 & ",发送信息的企图失败" 
		End If
		objRecordSet.MoveNext
		Loop
		of.close
		MsgBox "通知结束,请查看通知结果.txt!"
	End If
Else
If Choice = "1" Then	'向指定计算机发通知
	PC2 = InputBox ("请输入您要通知的计算机:","通知发布程序")
	Message2 = InputBox ("请输入通知内容:","通知发布程序")
	If Message2 = "" Then
		MsgBox "没有通知内容!本程序即将退出!"
	Else
		Set oFSO = CreateObject("Scripting.FileSystemObject")
		Set of = oFSO.CreateTextFile("通知结果.txt", True, True)
		set WSHshell=createobject("wscript.shell")
		WSHshell.run "msg /time:1800 /server:"&PC2&" * "& Message2,0
		of.writeline "向," & PC2 & ",发送了信息:"& Message2
		of.close
		MsgBox "通知结束,请查看通知结果.txt!"
	End If
Else	
MsgBox "输入错误!本程序即将退出!"
End If
End If
  1. 写两个不同的过程,然后通过IF来调用这两个过程。
    下面展示完整 使用call来跳转的代码
main '执行主进程

Sub Main() '定义主进程

'获得通知类型
msgType = InputBox ("请输入数字进行选择:"&chr(10)&chr(10)&"1.通知全部店铺"&chr(10)&chr(10)&"2.通知指定计算机","通知发布程序","")
'根据输入选择需要调用的过程
if msgType = "1" then Call msgtoall()
if msgType = "2" then Call msgonce()
If msgType = "" Then MsgBox "输入被取消,本程序即将退出!"
wscript.quit
End sub

sub msgtoall()	'定义全体通知过程

msgFile = InputBox ("请输入消息内容:","通知发布程序","")	'取得通知内容
	If msgFile = "" Then
		MsgBox "没有通知内容!本程序即将退出!"
	Else

'定位AD组织,可以input录入,也可以直接写死
ADPath = InputBox ("请输入您要发通知的域组织","通知发布程序","ou=dep,dc=local,dc=domain")

'获得组织中的计算机名列表
Const ADS_SCOPE_SUBTREE = 2
Set objConnection = CreateObject("ADODB.Connection")
Set objCommand =   CreateObject("ADODB.Command")
objConnection.Provider = "ADsDSOObject"
objConnection.Open "Active Directory Provider"'

Set objCOmmand.ActiveConnection = objConnection
objCommand.CommandText = "Select Name, Location from 'LDAP://"&ADPath&"' " & "Where objectClass='computer' AND userAccountControl='4096'"  
objCommand.Properties("Page Size") = 1000
objCommand.Properties("Searchscope") = ADS_SCOPE_SUBTREE 
Set objRecordSet = objCommand.Execute
objRecordSet.MoveFirst

'运行结果保存设置
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set of = oFSO.CreateTextFile("消息发送记录.txt", True, True)
'计算机名写为变量
Do Until objRecordSet.EOF
   On Error Resume Next
   PC1 = objRecordSet.Fields("Name").Value
   '检测计算机是否可以域通讯
   Set objWMILocator = GetObject("winmgmts:" & "{impersonationLevel=impersonate}!\\" & PC1 & "\root\cimv2")

'向可通讯的计算机发送通知
	Set WSShell=wscript.createobject("wscript.shell")
    If Err = 0 Then 
	For Each item In col
	WSShell.run "msg /time:1800 /server:"& PC1 &" * "& msgFile,0
	of.writeline "向," & PC1 & ",发送了信息:" & msgFile
	Next
	Else
	of.writeline "向," & PC1 & ",发送信息失败" 
 End If
 objRecordSet.MoveNext
Loop
of.close
MsgBox "通知已经全部发送完毕,请查看消息发送记录"
End If
End sub

'单独通知
sub msgonce()
PC2 = InputBox ("请输入要通知的计算机名:","通知发布程序","")
'运行结果保存设置
Set oFSO = CreateObject("Scripting.FileSystemObject")
Set of = oFSO.CreateTextFile("消息发送记录.txt", True, True)

'取得消息内容
msgFile2 = InputBox ("请输入消息内容:","通知发布程序","")
	If msgFile2 = "" Then
		MsgBox "没有通知内容!本程序即将退出!"
		wscript.quit
	else

Set WSShell=wscript.createobject("wscript.shell")
WSShell.run "msg /time:1800 /server:"&PC2&" * "&msgFile2 , 0
of.writeline "向," & PC2 & ",发送了信息:" & msgFile2
of.close
MsgBox "通知已经全部发送完毕,请查看消息发送记录"
End if
end sub

过程中已解决的问题

虽然都是用记事本来进行编辑VBS程序,但是记事本保存时也有多种编码可以选择。
如果使用UTF编码保存文档,会导致运行VBS程序时报错。
比如执行上面的代码时,会提示choice赋值的行缺少一个’)'。
将文档另存为ANSI编码后,则不会有报错。

尚未解决的问题

调用msg发送信息时,也会有成功和失败,但是我们上面的代码只能确定调用msg成功了,无法判断和记录msg调用成功后,信息是否发送失败的情况。
如果有大佬帮我完善一下的话,感激不尽。
但是,只要计算机都在AD环境,并且根据我本文第一段所说,修改了注册表的话,一般情况下都会调用成功。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值