【转】c++实现远程开关机,另,我自己加了段错误判断码,以便查错。

原作者:ZhangZhiHao_ 
来源:CSDN 
原文:https://blog.csdn.net/qq_24282081/article/details/87268410 
版权声明:本文为博主原创文章,转载请附上博文链接!

 

把远程开、关机写成了一个类的两个静态函数。

这两个功能的实现都需要事先对目标主机进行一些设置。其中远程开机需要目标主机主板支持,并且插上网线。部分主机的设置已经写明。另可实现方法:https://www.cnblogs.com/findumars/p/6009474.html

原理参考:https://blog.csdn.net/smstong/article/details/16879347
--------------------- 

 

 

RemoteControl.h

/*****************************************************
Author: 张志浩
Mail: 791745123@qq.com
Time: 2019-1-20
Function:
远程开关机工具,需要对目标主机作如下设置
远程开机:
1、BIOS设置
(DELL):
system setup->BIOS设置->Power Management->设置Deep Sleep Control为Disable Wake on Lan为LAN only
(ASUS):
Advance->APM->开启【Power On By PCIE】(PCIE: PCI-Ethernet)
2、系统设置
step one:设备管理器->网络适配器->选择对应网卡设备, 属性->高级->开启【Wake on Magic Packet】->开启【Wake on Pattern match】->开启【关机 网络唤醒】->关闭【环保节能】
step two:设备管理器->网络适配器->选择对应网卡设备, 属性->电源管理->关闭【允许计算机关闭此设备以节约电源】
远程关机:
1、启动Guest账户
管理->计算机管理->本地用户和组->用户->选择Guest进入属性->关闭【账户已停用】
2、允许Guest用户从网络访问此计算机(Win + r, 输入 gpedit.msc)
本机计算机策略->计算机配置->Windows设置-->安全设置-->本地策略-->用户权限分配->从【拒绝从网络访问这台计算机】中删除Guest
3、允许Guest用户从远端系统强制关机
本机计算机策略->计算机配置->Windows设置-->安全设置-->本地策略-->用户权限分配->将Guest加入到【从远程系统强制关机】
Version: v 1.0
*****************************************************/
#pragma once

#include <Windows.h>
#include <stdio.h>

#pragma comment(lib, "ws2_32.lib")
#pragma comment(lib, "advapi32.lib")

class CRemoteControl
{
public:
	CRemoteControl(void);
	~CRemoteControl(void);


public:
	static int Startup(char *chMac);
	static int Shutdown(char *chIP, int iTimeOut, bool bForceAppsClosed, bool bRebootAfterShutDown);
	static void ErrorExit(LPTSTR lpszFunction);
};



 

RemoteControl.cpp

#include "stdafx.h"
#include "RemoteControl.h"
#include "strsafe.h"


CRemoteControl::CRemoteControl(void)
{

}


CRemoteControl::~CRemoteControl(void)
{
}

int CRemoteControl::Startup(char *chMac)
{
	//判断Mac地址格式
	for (char *a = chMac; *a; a++)
	{
		if (*a != '-' && !isxdigit(*a))
		{
			OutputDebugStringA("Mac address must be like this: 00-d0-4c-bf-52-ba");
			return -1;
		}
	}

	//字符串转数组
	int dstaddr[6];
	int i = sscanf_s(chMac, "%2x-%2x-%2x-%2x-%2x-%2x", &dstaddr[0], &dstaddr[1], &dstaddr[2], &dstaddr[3]
		, &dstaddr[4], &dstaddr[5]);


	if (i != 6)
	{
		OutputDebugStringA("Invalid mac address!");
		return -1;
	}

	//构造Magic Packet (包格式: "FFFFFFFFFFFF" + 重复16编mac地址)
	unsigned char ether_addr[6];
	for (i = 0; i < 6; i++)
	{
		ether_addr[i] = dstaddr[i];
	}

	u_char magicpacket[200];
	memset(magicpacket, 0xff, 6);
	int packetsize = 6;
	for (i = 0; i < 16; i++)
	{
		memcpy(magicpacket + packetsize, ether_addr, 6);
		packetsize += 6;
	}

	//创建广播套接字
	WSADATA WSAData;
	if (WSAStartup(MAKEWORD(2, 0), &WSAData) != 0)
	{
		OutputDebugStringA("WSAStartup failed");
		return -1;
	}

	SOCKET sock = socket(AF_INET, SOCK_DGRAM, 0);
	if (sock == INVALID_SOCKET)
	{
		OutputDebugStringA("Socket Create error");
		return -1;
	}

	BOOL bOptval = TRUE;
	int iOptLen = sizeof(BOOL);
	if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&bOptval, iOptLen) == SOCKET_ERROR)
	{
		OutputDebugStringA("setsocketopt error!");
		closesocket(sock);
		WSACleanup();
		return -1;
	}

	sockaddr_in to;
	to.sin_family = AF_INET;
	to.sin_port = htonl(0);
	to.sin_addr.S_un.S_addr = htonl(INADDR_BROADCAST);

	//发送魔法包
	if (sendto(sock, (const char*)magicpacket, packetsize, 0, (const struct sockaddr*)&to, sizeof(to)) == SOCKET_ERROR)
	{
		OutputDebugStringA("Send error!");
	}
	else
	{
		OutputDebugStringA("Send success!");
	}

	closesocket(sock);
	WSACleanup();
	return 0;
}


void CRemoteControl::ErrorExit(LPTSTR lpszFunction)
{
	// Retrieve the system error message for the last-error code

	LPVOID lpMsgBuf;
	LPVOID lpDisplayBuf;
	DWORD dw = GetLastError();

	FormatMessage(
		FORMAT_MESSAGE_ALLOCATE_BUFFER |
		FORMAT_MESSAGE_FROM_SYSTEM |
		FORMAT_MESSAGE_IGNORE_INSERTS,
		NULL,
		dw,
		MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
		(LPTSTR)&lpMsgBuf,
		0, NULL);

	// Display the error message and exit the process

	lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
		(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
	StringCchPrintf((LPTSTR)lpDisplayBuf,
		LocalSize(lpDisplayBuf) / sizeof(TCHAR),
		TEXT("%s failed with error %d: %s"),
		lpszFunction, dw, lpMsgBuf);
	MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK);

	LocalFree(lpMsgBuf);
	LocalFree(lpDisplayBuf);
	ExitProcess(dw);
}


int CRemoteControl::Shutdown(char *chIP, int iTimeOut, bool bForceAppsClosed, bool bRebootAfterShutDown)
{
	HANDLE hToken;
	TOKEN_PRIVILEGES tkp;

	BOOL fResult;
	if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
		return false;

	LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
	tkp.PrivilegeCount = 1;
	tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
	AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
	if (GetLastError() != ERROR_SUCCESS)
		return false;

	WCHAR wszIP[20];
	memset(wszIP, 0, sizeof(wszIP));
	MultiByteToWideChar(CP_ACP, 0, chIP, strlen(chIP) + 1, wszIP, sizeof(wszIP) / sizeof(wszIP[0]));

	//发送指令,iTimeOut如果小于60,则对方机器关机前会显示“一分钟内关机”
	fResult = ::InitiateSystemShutdown(wszIP, NULL, iTimeOut, bForceAppsClosed, bRebootAfterShutDown);
	if (!fResult)
	{
		ErrorExit(_T("shutdown()"));
		return false;
	}
		

	tkp.Privileges[0].Attributes = 0;
	AdjustTokenPrivileges(hToken, false, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
	if (GetLastError() != ERROR_SUCCESS)
		return false;

	return true;
}

一切都设置好了之后,发现关机指令发送失败,错误码是 53,好像是说 网络上没有找到这台主机。想了想,把目标机子的防火墙给关掉,再试,一切OK。

另:亲测成功,远程开机,附上技嘉主板(GIGABYTE) Z370 HD3 型号的BIOS设置,

BIOS栏的【Fast Boot】为【Enabled】,  【Network Stack Driver Support】为 【Enabled】。 

Chipset 栏内的 【PCH  Lan  Controller】 --->【Enabled】,  【Wake on Lan Enable】 ----> 【Enabled】。

Power栏内的【Platform Power Management】------>【disabled】,  【Erp】------->【disabled】

 

另:如果   设备管理器->网络适配器-> 看到的网卡是 intel(R) Ethernet Connection (2) I219-V,但在右击,属性-->高级,那里却找不到  “ 唤醒魔术包 ”  那一个选项,那是因为没有更新驱动的缘故,在 “驱动程序” 标签页,更新一下驱动程序,后就可以看到有“ 唤醒魔包” 这一项了。

GIGABYTE Z370 HD3主板的intel(R) Ethernet Connection (2) I219-V 网卡“高级”标签那里一共要设置4项。

属性--> 高级-->唤醒魔包 -->  开启。

属性--> 高级-->唤醒模式匹配 -->  开启

属性--> 高级-->节能以太网 -->  关闭

属性--> 高级-->系统闲置电源节能 -->  禁用

 

“电源管理 ”标签页要设置3项(注意,与realtek的网卡设置不一样。)

属性->电源管理->勾选【允许计算机关闭此设备以节约电源】。勾选 【允许此设备唤醒计算机】,去掉只勾选【只允许幻数据包唤醒计算机】

注:昨天程序测试了还是可以远程开关机的,但今天一早再测,就不可以了。而另一个用C#写的开机却又可以,wireshark抓包(可以输入WOL来过滤掉其它包)发现C++的这个运行时没发远程开机的UDP包,得启计算机再试又可以了。唉,什么玩意啊,难道C++的远程开机与C#的在socket的广播上有冲突。

后来了解到,网上有一种说法说本机上的虚拟网卡影响,导致本机的广播包发送不出去。把本机上的虚拟网卡禁用掉就行了。但我之前的做法是重启没禁用虚拟网卡也可以发送成功啊,记录一下,以免以后遇到类似的事情。

 

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值