木丰雨 发表于 2012-1-4 11:31:41

远程关机原理设置

远程关机的方法分两种:一种需要在被控制的计算机上编写软件(适用于任何系统)、一种不需要在被控制的计算机上编写软件(只适用于Windows2000、WindowsXP以上任何系统)。
1.有被控端软件
需要编写控制端软件和被控端软件,由控制端软件发送自定义的关机命令字符串,被控端软件收到相应命令后关闭本机。通信方式有TCP、UDP两种,TCP是面向连接的,为了保证可靠的传输可采用它,UDP是无连接的,为了提高传输速度可采用它。由于篇幅限制且UDP方式相对简单,我这里只谈TCP方式。
TCP方式需要通信的一端作为服务端,进行监听(Listen),等待接受(accept)另一端即客户端的连接(connect)。如果仅仅用于关机,将控制端或被控端作为服务端均无不可,但是为了软件的可扩展性,我将控制端作为服务端,关键代码如下:
(1)服务端:
先设定服务端地址和端口,创建套接字并绑定,然后将套接字置为监听模式,启动一个线程处理接收。
sockaddr_in ServerSockAddr;
ServerSockAddr.sin_addr.s_addr=htonl(INADDR_ANY);
ServerSockAddr.sin_family=AF_INET;
ServerSockAddr.sin_port=htons(SERVER_PORT);
m_SockListen=socket(AF_INET,SOCK_STREAM,0);
if (bind(m_SockListen,(sockaddr*)&ServerSockAddr,sizeof(ServerSockAddr)))
MessageBox("绑定错误");
else listen(m_SockListen,5);
AfxBeginThread(&thread,0);
在线程函数中接受客户端的连接,得到一个新的套接字,用于和刚接受连接的那个客户机通信。为了使用户能将在ListCtrl控件上所选择的计算机正确关机,需要将ListCtrl控件的行号与该行客户机的连接套接字对应,将与各客户机连接的所有套接字存放在一个套接字数组m_SockClient[]中,因此只要将客户机信息在ListCtrl控件中所在行号作为套接字数组m_SockClient []中的下标来对应该客户机的套接字即可。在accept函数的第二个参数中返回了发出连接请求的那个客户机的I P地址信息,因此只要将该I P地址与ListCtrll控件上所列出的所有客户机的I P地址一一比较,找到该客户机信息所在行号,然后将该客户机的套接字保存在以该行号为下标的数组套接字元素中。关键代码如下:
UINT thread(LPVOID p)
{
SOCKET SockAccept;
struct sockaddr_in clientaddr;
int iAddrLen=sizeof(struct sockaddr);
ULONG ulClientIpAddr;
CString strIpAddr;
CRemoteOnOffDlg *PowerDlg=(CRemoteOnOffDlg*)AfxGetApp()->GetMainWnd();
while(1)
{
SockAccept=accept(PowerDlg->m_SockListen,(sockaddr*)&clientaddr,&iAddrLen);
ulClientIpAddr=clientaddr.sin_addr.s_addr;
for(int i=0;i<PowerDlg->m_ListHostInfo.GetItemCount();i++)
{
strIpAddr=PowerDlg->m_ListHostInfo.GetItemText(i,2);
if(ulClientIpAddr==inet_addr(strIpAddr))
{
PowerDlg->m_SockClient=SockAccept;
//为了知道哪些客户机已建立了连接,我顺便在ListCtrll控件中对应连接客户机那一行的第五列打"√"作为标记:
PowerDlg->m_ListHostInfo.SetItemText(i,4,"√");
break;
}
}
}
}
最后在用户点击关机按钮或菜单时发送自定义的关机命令字符串:
POSITION pos=m_ListHostInfo.GetFirstSelectedItemPosition();
while(pos)
{
int nItem=m_ListHostInfo.GetNextSelectedItem(pos);//获取选择项
send(m_SockClient,"POWOFF",COM_STR_LEN,0);
closesocket(m_SockClient);//关闭套接字
m_ListHostInfo.SetItemText(nItem,4,"×");

}
(2)客户端
先解析服务器名,然后用s o c k e t创建一个套接字,再用c o n n e c t创建与服务器的连接。最后等待接收关机命令字符串:
CString strServerIPAddr="192.168.1.1";//此处为服务端的IP地址
SOCKET SockClient;
sockaddr_in ServerSockAddr;
ServerSockAddr.sin_addr.s_addr=inet_addr(strServerIPAddr);
ServerSockAddr.sin_family=AF_INET;
ServerSockAddr.sin_port=htons(SERVER_PORT);
SockClient=socket(AF_INET,SOCK_STREAM,0);
while(connect(SockClient,(sockaddr*)&ServerSockAddr,sizeof(ServerSockAddr))!=0);
int iAllRecvLen=0,iThisRecvLen=0;
char strRecvBuf="";
while(iThisRecvLen!=SOCKET_ERROR&&iAllRecvLen<COM_STR_LEN)
{
//循环接收数据
iThisRecvLen=recv(SockClient,strRecvBuf+iAllRecvLen,COM_STR_LEN,0);
iAllRecvLen+=iThisRecvLen;
}
被控端收到命令字符串后,调用ExitWindowsEx函数关闭或重启本客户机。ExitWindowsEx函数在Windows9x系统中可直接使用,在Windows2000或WindowsXP以上系统中默认的情况下进程不具有关机权限,所以要将当前进程的关机权限Enabled。先通过OpenProcessToken函数获得当前进程访问令牌的句柄,该函数声明如下:
BOOL OpenProcessToken (HANDLE ProcessHandle,DWORD DesiredAccess,PHANDLE TokenHandle);
第一参数是要修改权限的进程句柄;第二个参数为对该令牌的访问类型;第三个参数即获得的进程访问令牌的句柄。
为了修改进程令牌权限,还要先定义一个令牌权限TOKEN_PRIVILEGES类型结构变量,该结构定义如下:
typedef struct _TOKEN_PRIVILEGES {
DWORD PrivilegeCount;
LUID_AND_ATTRIBUTES Privileges;
} TOKEN_PRIVILEGES
第一个成员变量为权限数量;第二个成员变量为LUID_AND_ATTRIBUTES类型结构变量数组。该结构定义如下:
typedef struct _LUID_AND_ATTRIBUTES {
LUID Luid;
DWORD Attributes;
} LUID_AND_ATTRIBUTES;
第一个成员变量为某权限的本地唯一标识;第二个成员变量为该权限属性。
为了获取某权限的本地唯一标识,需要通过LookupPrivilegeValue函数,该函数声明如下:
BOOL LookupPrivilegeValueA(LPCSTR lpSystemName,LPCSTR lpName,PLUID lpLuid);
第一个参数为系统名,本地系统为NUL;第二个参数为权限名;第三个参数为返回的权限本地唯一标识。
定义好了令牌权限TOKEN_PRIVILEGES结构变量后,最后通过AdjustTokenPrivileges函数修改访问令牌权限,该函数声明如下:
BOOL AdjustTokenPrivileges (HANDLE TokenHandle,BOOL DisableAllPrivileges,
PTOKEN_PRIVILEGES NewState,DWORD BufferLength,PTOKEN_PRIVILEGES reviousState,
PDWORD ReturnLength);
第一个参数为访问令牌句柄;第二个参数为是否取消所有权限;第三个参数为前面定义好的令牌权限;后面三个参数针用于保存修改前的令牌权限,分别为用于保存的内存长度、保存的内存地址、实际保存的内存长度。
具体代码如下:
if(strcmp("POWOFF",strRecvBuf)==0)
{
closesocket(SockClient);
WSACleanup();
if(GetVersion()<0x80000000)//判断WINDOWS系统版本号
{
HANDLE hProcessToken;
TOKEN_PRIVILEGES tkp;
OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hProcessToken);
LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&tkp.Privileges.Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges.Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hProcessToken,FALSE,&tkp,0,(PTOKEN_PRIVILEGES)NULL,0);
}
//强迫所有进程退出、关闭计算机并切断电源
ExitWindowsEx(EWX_FORCE|EWX_POWEROFF,0);
}
在Windows2000或WindowsXP以上系统中,也可用InitiateSystemShutdown函数代替ExitWindowsEx函数关闭本机,InitiateSystemShutdown函数的使用在下面介绍。
2.无被控端软件
由于Windows2000、WindowsXP以上的系统本身支持远程关机,所以也可不编写被控端软件,Windows API中已提供现成的函数InitiateSystemShutdown,其声明如下:
BOOL InitiateSystemShutdownA(LPSTR lpMachineName,LPSTR lpMessage,DWORD dwTimeout,BOOL bForceAppsClosed,BOOL bRebootAfterShutdown);
第一个参数为机器名,本机为NULL;第二个参数为关机提示对话框中显示的消息;第三个参数为关机前提示的时间;第四个参数为是否强制关闭应用程序;第五个参数为是否重启。
远程关机需要具有相应权限,如果在域环境下可以直接以域管理员身份登录系统获得远程关机的权限,一般在机房或网吧中都是工作组环境,无法直接获得远程关机的权限,怎么办呢?通过试验我找到了一个办法,具体步骤如下:http://zqingqing.com/soft/12463.html
(1)在被控机上通过“计算机管理”建立一个用户,然后在“组策略”中给该用户配置远程关机权限,具体操作为:运行“gpedit.msc”打开“组策略编辑器”。在“组策略”左侧树窗口中依次打开“计算机配置”、“Windows 设置”、“安全设置”、“本地策略”、“用户权利指派”。在“组策略”右侧列表窗口选择“从远端系统强制关机”策略添加该用户。
(2)在工作组环境中无法直接以该用户账号从控制机登录被控机,需要在控制机上创建与被控机上一致的用户账号(用户名和密码都需相同),可预先通过“计算机管理”完成,也可在调用关机代码前临时通过下面的代码来创建:
NET_API_STATUS retStatus = 0;
DWORD dwError = 0;
USER_INFO_1 structUserInfo;
ZeroMemory(&structUserInfo, sizeof(structUserInfo));
structUserInfo.usri1_name = szUserName;
structUserInfo.usri1_password= szPassword;
structUserInfo.usri1_priv = USER_PRIV_USER;
structUserInfo.usri1_flags = UF_NORMAL_ACCOUNT;
retStatus = NetUserAdd(NULL, 1, (LPBYTE)(&structUserInfo), &dwError); //创建用户
//将该用户加到"Administrators"组
_LOCALGROUP_MEMBERS_INFO_3 memberUser;
memberUser.lgrmi3_domainandname = structUserInfo.usri1_name;
retStatus = NetLocalGroupAddMembers(NULL, L"Administrators", 3, (LPBYTE)(&memberUser), 1);
在调用关机代码后如果要删除上面所创建的用户可通过下面代码完成:
NetUserDel(NULL,structUserInfo.usri1_name);
(3)在控制机和被控机的“组策略”中找到“本地策略”的“安全选项”的“网络访问:本地账号的共享和安全模式”,设置为“经典-本地用户以自己的身份验证”。
(4)如果在控制机上已用上面建立的用户账号登录,此时可直接调用InitiateSystemShutdown函数远程关机,否则先要通过下面代码登录:
// LogonUser函数接受登录信息并返回有效登入的安全性访问令牌。
LogonUser(m_strUserName,strMachineName,m_strPassword,LOGON32_LOGON_INTERACTIVE,LOGON32_PROVIDER_DEFAULT,&hLogonToken);
// ImpersonateLoggedOnUser函数接收LogonUser的安全性访问令牌,并将该令牌用到目前的执行中。
ImpersonateLoggedOnUser( hLogonToken );
(5)调用InitiateSystemShutdown函数远程关机
InitiateSystemShutdown(strMachineName,"关机!",30,TRUE,FALSE);
三、结语
本文实现了局域网中的远程开机功能和有客户端无客户端两种方式下的关机功能,在此基础上还可根据需要进行功能扩展。本程序在WindowsXP操作系统下用VC6.0或VS.NET都能调试通过,并投入使用。
bat批处理文件与定时自动关机命令【来源:网络来源 】【时间:2009-10-27】【复制链接】【字体:大 中 小】如果想让Windows 2000也实现自动关机效果,可以把Shutdown.exe复制到系统目录下。
比如你的电脑要在22:00关机,可以选择“开始→运行”,输入“at 22:00 Shutdown -s”,这样,到了22点电脑就会出现“系统关机”对话框,默认有30秒钟的倒计时并提示你保存工作。如果你想以倒计时的方式关机,可以输入“Shutdown.exe -s -t 3600”,这里表示60分钟后自动关机,“3600”代表60分钟。
设置好自动关机后,想取消的话,可以在运行中输入“shutdown -a”。另外输入“shutdown -i”,则可以打开设置自动关机对话框,对自动关机进行设置。
Shutdown.exe的参数,都具有特定的用途,执行每一个都会产生不同的效果,比如“-s”表示关闭本地计算机,“-a”表示取消关机操作,下面列了更多参数,大家可以在Shutdown.exe中按需使用。
   -f:强行关闭应用程序
 -m:\\计算机名:控制远程计算机
  -i:显示图形用户界面,但必须是Shutdown的第一个选项
  -l:注销当前用户
  -r:关机并重启
  -t:时间:设置自动关机倒计时
  -c:“消息内容”:输入关机对话框中的消息内容(不超127个字符)
没有定时关机软件。而用
bat文件可以解决很多这类问题。
1.关机与重启
  我们先做个让电脑在每天指定时间关机的bat,具体方法如下:
  打开附件中的记事本,然后在里边写入,at 22:00 shutdown -s -f,然后选择“文件→保存”,保存类型选择“所有文件”,然后将其命名为:shutdown.bat,如图1所示。如果你希望每天都在晚上十点关机,则将这个文件拖动到“开始→程序→启动”中,这样每次开机该文件都将被执行,其具体含义是,at 22:00在每天十点shutdown -s -f关机且关闭所有未响应程序。
    如果你需要经常重启机器,可以编写一个快速关机的bat文件,还是打开一个记事本,写入:
  @echo off
  //关闭命令行显示
  %systemroot%\system32\shutdown -r -t 0
  //-r参数表示重启计算机,-t表示时间后边跟随等待秒数,为0则表示马上重启
2.磁盘整理
  Windows XP自带的磁盘碎片整理程序无法进行全盘整理,我们可以编写一个全盘整理的bat,在记事
本里边输入:
  defrag c: /f /v
  defrag d: /f /v
  //有几个分区就写几行,其中-f表示即使可用磁盘空间不足,也强制进行磁盘整理。-v表示显示整理
结果。
  将这个存为bat文件,再双击即可。如果你嫌整理的时间太长,不愿意在一旁守着,可以在文件的结
尾输入:shutdown -s -f,让其执行完关机即可。
二、一律从简——让网络操作变轻松
  访问共享文件夹、备份网络数据、切换网络配置等常规网络操作,如果采用传统做法,实在是费时费
力。用bat文件,一切将可以简化为一个双击动作。
1.备份还原网络配置
  对于经常使用笔记本电脑在两地上网的朋友,切换网络配置就成了家常便饭,总是该来该去实在麻烦
,用bat文件可以使一切都变得简单。
  首先选择“开始→运行”,输入:cmd,回车后进入命令行界面,然后输入:netsh -c interface
dump>d:\home.txt,回车后,系统将会把你当前的网络配置备份到d盘的home.txt文档中。
  然后开始编写bat文件,只有简单的一行:netsh -f d:\home.txt,保存为bat文件,以后双击该文
件就会应用home.txt中的网络配置。如果你觉得两个文件烦,也可以将两个文件写成一个bat文件:
  @echo off
  netsh interface ip set address name="本地连接" source=static addr=10.10.10.1
mask=255.255.255.0
  //设置本地连接的ip地址为:10.10.10.1,子网掩码为:255.255.255.0
  netsh interface ip set address name="本地连接" gateway=110.10.10.2 gwmetric=0
  //设置网关
  netsh interface ip set dns name="本地连接" source=static addr=10.10.10.3
register=PRIMARY
  //设置主dns
  netsh interface ip add dns name="本地连接" addr=10.10.10.4
  //设置备份dns
  netsh interface ip set wins name="本地连接" source=static addr=none
  //设置wins
2.在局域网中群发信息
  如果你局域网中的机器没有禁止信使服务的话(启用方法为:在“运行”里输入:services.msc,双
击里边的Messenger,选择启用即可),当需要定时向全网发送消息时,完全可以用批处理文件实现,实
现方法是:
  rem 准备发布“9点断网”给局域网所有计算机
  //显示在bat运行前,起到提示作用
  pause
  //暂停运行,按任意键继续,如果需要定时运行,则不要此段
  net send * 9点断网
  //发送下“9点断网”的信息,网段中所有的机器,“*”表示所有的机器
  将其保存为bat文件,双击运行,再按键盘上的任意键,信息将被发送到全网。如果你希望它定时发
送,则将pause字段去掉,然后将其加入到计划任务中即可。
3.打开或关闭网络共享
  当你需要共享某个远程的文件夹时,总要跑到本地去操作,如果数量众多,就有可能跑断腿,我们可
以编写一个自动共享硬盘的bat文件,将文件发给用户,只要其双击该文件,即可实现共享的目的。
  @echo off
  echo REGEDIT4>c:\1.reg
  echo >>c
:\j.reg echo "Flags"=dword:00000102>>c:\j.reg
  //Flags用于是确定共享目录的访问方式,其中"Flags"=dword:00000102是完全共享,101是只读,
103是根据密码访问。
  echo "Parm1enc"=123:>>c:\1.reg
  //Parm1enc键值保存着完全访问方式下的密码
  echo "Parm2enc"=456:>>c:\1.reg
  //Parm2enc键值保存着只读访问方式下的密码
  echo "Path"="D:\">>c:\1.reg
  //Path键值是共享文件夹的路径
  echo "Remark"="我共享了你的D盘">>c:\1.reg
  //Remark键值是备注
  echo "Type"=dword:00000000>>c:\1.reg
  REGEDIT /S c:\j.reg DEL c:\j.reg cls exit
  用户运行这个bat文件后,再注销一下,文件夹就会被共享出来了。如果你每天都需要访问某个共享
文件夹,且这个共享文件夹设置了密码,你还可以让bat 文件帮你在开机的时候先输入密码,你再访问时
就只需要双击打开即可。输入:net use \\192.168.0.1\IPC$ "1234" /user:"administrator,这个表
示访问PC机192.168.0.1上边的共享文件夹,用administrator用户,密码为: 1234.将其保存为bat文件
,并将其加入到启动组中即可。
  共享文件夹管理不当,会产生严重安全隐患,所以有时为了安全,需要批量卸载掉共享文件夹,其实
只要将命令:net share c$ /del加入到bat文件中即可,其中c$为共享文件夹的名字。
4.备份网络数据
  (1)你还可以利用bat文件将数据备份到局域网内的某台文件服务器上,这样就省去了复制粘贴的麻
烦,具体做法是:
  set source=e:\work
  //文件来源为本机e盘的work文件夹
  set dest=\\192.168.0.1\backup
  //目标文件夹为PC机192.168.0.1的backup文件夹
  net use \\192.168.0.1\IPC$ "1234" /user:"administrator"
  xcopy %source% %dest% /e /v /r /y /z
  //将源盘文件拷贝到目的服务器
  将其保存为bat文件,以后只要双击此文件即可完成备份任务。
(2)在某台机器上备份服务器数据库:
    在服务器上将数据库目录如(DATA目录)设为只读共享,在某机上新建一个备份目录如(BACK目录,
目录下再新建一目录为DATA),在记事本上敲入如下内容:
xcopy \\192.168.0.1\路径\DATA F:\back\DATA\ /c /y /Z /s /E /V /R /I /Q /
H
将以上内容存为BAT文件,放在BACK目录下即可。
(3)用RAR备份
  1、制作备份文件列表
  WinRAR之所以能够自己找到需要的文件进行备份,秘密就在一个列表文件上。这个以1st为后缀名的
文件实际上是一个纯文本文件,其中的内容便是需要备份的文件列表,里面有需要备份的文件或文件夹在
网络上的完整路径,例如“\\computer1\d\lj\work”。以下便是果冻所制作的备份文件列表中的一段:
  \\tv1\share\work //1号机上的工作文件夹
  \\tv2\share\work //2号机上的工作文件夹
其中,//后面的文字是对前面内容的注释,有了注释能够便于管理,尤其是在需要备份的文件和文件夹数
目众多的情况下。当然,这些注释不会对列表文件造成任何影响。
  在服务器的D盘建立一个文件夹,取名为data,在其中新建一个文本文件,输入以上文件列表后,再
另存为bak.1st文件即可(文件名可任意取,但后缀名不能变)。
  提示:对局域网内某台电脑上的数据进行备份时,要备份的内容必须处于共享状态,同时还得拥有登
录对方电脑的权限。
  2、创建备份命令
  完成上述操作后,我们可以通知WinRAR进行数据备份的有关工作了。打开data文件夹,在空白处点击右键,选择“新建→快捷方式”,在弹出的“创建快捷方式”对话框中输入以下命令:
>>(http://www.zqingqing.com/html/czxt/index.html)URL(http://www.zqingqing.com/html/2009-10/8950.html)


页: [1]
查看完整版本: 远程关机原理设置