背景
之所以会学习到这方面的知识,是因为那段时间正在帮一个游戏工作室开发一个游戏自动登录并创建角色的游戏脚本。当时,我就是使用VS去开发。因为它要求要有一个控制端,所以,就分别写了一个客户端程序和控制端程序。客户端都运行在虚拟机内,和控制端在同一网段里。
当时,我就想让客户端在虚拟机里运行,主动去扫描工作组内的主机,那么它工作组内就会有两个主机,一个是虚拟机自己,另一个就是外面的主机。主机上运行则控制端,所以,这样就可以获取主机的IP地址,并自动建立反向连接,传输数据。
函数介绍
DWORD
WNetOpenEnum(
_In_ DWORD dwScope
,
_In_ DWORD dwType
,
_In_ DWORD dwUsage
,
_In_ LPNETRESOURCE lpNetResource
,
_Out_ LPHANDLE lphEnum
);
DWORD
WNetEnumResource(
_In_ HANDLE hEnum
,
_Inout_ LPDWORD lpcCount
,
_Out_ LPVOID lpBuffer
,
_Inout_ LPDWORD lpBufferSize
);
struct hostent
* FAR
gethostbyname(
_In_
const char *name
);
char* FAR
inet_ntoa(
_In_
struct in_addr in
);
实现原理
本文要实现的功能就是遍历网络邻居,获取工作组内的所有在线主机名以及根据主机名获取的IP地址。实现过程如下:
首先,我们通过 WSAStartup 函数完成对 Winsock 服务的初始化,因为下面我们会使用到 Socket 函数。然后,我们使用 WNetOpenEnum 设置枚举的范围为本工作组内 RESOURCE_CONTEXT,并获取枚举句柄。接着,我们便调用 WNetEnumResource 函数按照设置的范围去枚举资源,并获取枚举结果。然后,遍历枚举结果,并设置过滤标志 RESOURCEUSAGE_CONTAINER 和 RESOURCETYPE_ANY。通过过滤之后,可以根据远程主机名调用函数gethostbyname 去获取IP地址信息,并通过 inet_ntoa 函数将(Ipv4) Internet网络地址转换为Internet标准点分十进制格式的ASCII字符串。
编码实现
#include <Winnetwk.h>
#pragma comment(lib, "Mpr.lib")
#pragma comment(lib, "Ws2_32.lib")
BOOL
EnumNetResource()
{
NETRESOURCE
*NetResource
= NULL;
HANDLE hEnum
;
unsigned int i
;
char szHostName
[MAX_PATH
] = { 0 };
hostent
*host
= NULL;
char *lpszIP
= NULL;
WSADATA wsaData
= { 0 };
::WSAStartup(MAKEWORD(2, 2), &wsaData
);
::WNetOpenEnum(RESOURCE_CONTEXT
, NULL, NULL, NULL, &hEnum
);
if (hEnum
)
{
DWORD Count
= 0xFFFFFFFF;
DWORD BufferSize
= 2048;
BYTE
*pBuffer
= new BYTE
[2048];
::WNetEnumResource(hEnum
, &Count
, pBuffer
, &BufferSize
);
NetResource
= (NETRESOURCE
*)pBuffer
;
for (i
= 0; i
< BufferSize
/ sizeof(NETRESOURCE
); i
++, NetResource
++)
{
if (NetResource
->dwUsage
== RESOURCEUSAGE_CONTAINER
&&
NetResource
->dwType
== RESOURCETYPE_ANY
)
{
if (NetResource
->lpRemoteName
)
{
::RtlZeroMemory(szHostName
, MAX_PATH
);
::lstrcpy(szHostName
, (char *)((DWORD64
)NetResource
->lpRemoteName
+ 2));
host
= ::gethostbyname(szHostName
);
if (host
== NULL)
{
printf("Error Code:%d\n", ::GetLastError());
continue;
}
lpszIP
= ::inet_ntoa(*(in_addr
*)host
->h_addr_list
[0]);
printf("NetResource->lpRemoteName = %s\n", NetResource
->lpRemoteName
);
printf("NetResource->lpLocalName = %s\n", NetResource
->lpLocalName
);
printf("ComputerName = %s\n", szHostName
);
printf("ComputerIP = %s\n", lpszIP
);
}
}
}
delete[]pBuffer
;
pBuffer
= NULL;
::WNetCloseEnum(hEnum
);
}
return TRUE
;
}
测试