使用WNetEnumResource函数实现枚举工作组内的主机及其IP
TFBOYSer
发布日期: 1 年前浏览量: 793收藏:3
评分:
star star star star star star star star star_border star_border
*转载请注明来自write-bug.com
背景
之所以会学习到这方面的知识,是因为那段时间正在帮一个游戏工作室开发一个游戏自动登录并创建角色的游戏脚本。当时,我就是使用VS去开发。因为它要求要有一个控制端可以所有的控制端,所以,就分别写了一个客户端程序和控制端程序。客户端都运行在虚拟机内,和控制端在同一网段里。
当时,我就想让客户端在虚拟机里运行,主动去扫描工作组内的主机,那么它工作组内就会有两个主机,一个是虚拟机自己,另一个就是外面的主机。主机上运行则控制端,所以,这样就可以获取主机的IP地址,并自动建立反向连接,传输数据。
所以,当时使用扫描方法,就是使用本文介绍的这个使用 WNetEnumResource 函数的方法。现在,我就把实现原理和过程写成文档,分享给大家。
函数介绍
WNetOpenEnum 函数
WNetOpenEnum函数启动网络资源或现有连接的枚举。 您可以通过调用WNetEnumResource函数继续枚举。
函数声明
DWORD WNetOpenEnum(_In_ DWORD dwScope,_In_ DWORD dwType,_In_ DWORD dwUsage,_In_ LPNETRESOURCE lpNetResource,_Out_ LPHANDLE lphEnum);参数
- dwScope [in]
枚举范围。 此参数可以是以下值之一:
VALUE MEANING RESOURCE_CONNECTED 枚举所有当前连接的资源。 该函数忽略dwUsage参数。 RESOURCE_CONTEXT 仅枚举调用者网络上下文中的资源。 为“网络邻居”视图指定此值。 该函数忽略dwUsage参数 RESOURCE_GLOBALNET 枚举网络上的所有资源 RESOURCE_REMEMBERED 枚举所有记住(持久)连接。 该函数忽略dwUsage参数
- dwType [in]
要枚举的资源类型。 此参数可以是以下值的组合:
VALUE MEANING RESOURCETYPE_ANY 所有资源。 此值不能与RESOURCETYPE_DISK或RESOURCETYPE_PRINT组合 RESOURCETYPE_DISK 所有磁盘资源 RESOURCETYPE_PRINT 所有打印资源
- dwUsage [in]
要枚举的资源使用类型。 此参数可以是以下值的组合:
VALUE MEANING 0 所有资源 RESOURCEUSAGE_CONNECTABLE 所有可连接的资源 RESOURCEUSAGE_CONTAINER 所有容器资源 RESOURCEUSAGE_ATTACHED 如果用户未通过身份验证,设置此值将强制WNetOpenEnum失败。 即使网络允许枚举而不进行身份验证,该功能也会失败 RESOURCEUSAGE_ALL 设置此值相当于设置RESOURCEUSAGE_CONNECTABLE,RESOURCEUSAGE_CONTAINER和RESOURCEUSAGE_ATTACHED
pNet资源[in]
指向指定要枚举的容器的NETRESOURCE结构。 如果dwScope参数不是RESOURCE_GLOBALNET,则此参数必须为NULL。lphEnum [out]
指向可以在随后调用WNetEnumResource中使用的枚举句柄的指针。返回值
- 如果函数成功,返回值为NO_ERROR。
- 如果函数失败,则返回值是系统错误代码。
WNetEnumResource 函数
WNetEnumResource功能继续列出通过调用WNetOpenEnum函数启动的网络资源。
函数声明
DWORD WNetEnumResource(_In_ HANDLE hEnum,_Inout_ LPDWORD lpcCount,_Out_ LPVOID lpBuffer,_Inout_ LPDWORD lpBufferSize);参数
- hEnum [in]
标识枚举实例的句柄。这个句柄必须由WNetOpenEnum函数返回。- lpcCount [in,out]
指向指定所请求条目数的变量的指针。如果所请求的号码为-1,则该函数返回尽可能多的条目。如果函数成功,返回此参数指向的变量包含实际读取的条目数。- lpBuffer [out]
指向接收枚举结果的缓冲区的指针。结果作为NETRESOURCE结构的数组返回。请注意,您分配的缓冲区必须足够大以容纳结构,加上其成员指向的字符串。有关详细信息,请参阅以下备注部分。- lpBufferSize [in,out]
指向变量的指针,该变量指定lpBuffer参数的大小(以字节为单位)。如果缓冲区太小而不能接收一个条目,则此参数将接收所需的缓冲区大小。返回值
- 执行成功,返回 NO_ERROR 或者 ERROR_NO_MORE_ITEMS;
- 否则,错误。
gethostbyname 函数
gethostbyname函数从主机数据库中检索与主机名对应的主机信息。
函数声明
struct hostent* FAR gethostbyname(_In_ const char *name);参数
name[in]
指向要解析的主机的以NULL结尾的名称的指针。
返回值
- 如果没有发生错误,gethostbyname返回一个指向上述主机结构的指针。 否则,它返回一个空指针,并且可以通过调用WSAGetLastError来检索特定的错误号。
inet_ntoa 函数
inet_ntoa函数将(Ipv4)Internet网络地址转换为Internet标准点分十进制格式的ASCII字符串。
函数声明
char* FAR inet_ntoa(_In_ struct in_addr in);参数
in[in]
一个表示Internet主机地址的in_addr结构。
返回值
- 如果没有发生错误,则inet_ntoa返回一个字符指针,指向包含标准“。”表示法中的文本地址的静态缓冲区,否则返回NULL。
实现原理
本文要实现的功能就是遍历网络邻居,获取工作组内的所有在线主机名以及根据主机名获取的IP地址。实现过程如下:
- 首先,我们通过 WSAStartup 函数完成对 Winsock 服务的初始化,因为下面我们会使用到 Socket 函数。
- 然后,我们使用 WNetOpenEnum 设置枚举的范围为本工作组内 RESOURCE_CONTEXT,并获取枚举句柄。
- 接着,我们便调用 WNetEnumResource 函数按照设置的范围去枚举资源,并获取枚举结果。
- 然后,遍历枚举结果,并设置过滤标志 RESOURCEUSAGE_CONTAINER 和 RESOURCETYPE_ANY。通过过滤之后,可以根据远程主机名调用函数gethostbyname 去获取IP地址信息,并通过 inet_ntoa 函数将(Ipv4) Internet网络地址转换为Internet标准点分十进制格式的ASCII字符串。
- 最后,释放内存以及枚举句柄。
这样,整个程序的实现原理和流程就结束了。需要特别注意一点就是,在函数开头一定要先初始化 Winsock 服务!
编码实现
加载库函数
#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;// 通过WSAStartup函数完成对Winsock服务的初始化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));// 根据主机名获取IP地址信息host = ::gethostbyname(szHostName);if (host == NULL){printf("Error Code:%d\n", ::GetLastError());continue;}// 将(Ipv4)Internet网络地址转换为Internet标准点分十进制格式的ASCII字符串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;}
分类:
2006-06-14 16:20:59
| WNetEnumResource |
| VB声明 | |
| Declare Function WNetEnumResource Lib "mpr.dll" Alias "WNetEnumResourceA" (ByVal hEnum As Long, lpcCount As Long, lpBuffer As Any, lpBufferSize As Long) As Long | |
| 说明 | |
| 枚举网络资源 | |
| 返回值 | |
| Long,零表示成功。ERROR_NO_MORE_ITEMS表示不剩下可以枚举的条目。ERROR_MORE_DATA表示条目不能装入lpBuffer。会设置GetLastError。如GetLastError是ERROR_EXTENDED_ERROR,则可用WNetGetLastError取得额外的错误信息 | |
| 参数表 | |
| 参数 | 类型及说明 |
| hEnum | Long,从WNetOpenEnum函数返回的一个句柄 |
| lpcCount | Long,最初设为要枚举的最大资源数量;或设为-1,表示枚举尽可能多的资源。一旦返回,就会设为实际枚举的资源数量 |
| lpBuffer | Any,通常是一个字节缓冲区的首字节。该缓冲区装载了枚举信息(可按引用声明为Byte) |
| lpBufferSize | Long,以字节为单位指定lpBuffer数组的长度。如缓冲区不够大,则设为需要的缓冲区长度 |
| 注解 | |
| 枚举网络条目时,最好用vb一次枚举一个资源。尽量不要使用这个函数同时枚举许多网络资源的功能 | |
这篇文章介绍了如何通过WNetEnumResource函数在Windows环境中枚举本地工作组内的主机,并获取它们的IP地址,以便于实现自动化连接。作者结合WSAStartup和gethostbyname函数,展示了完整的实现过程和关键代码片段。
1382

被折叠的 条评论
为什么被折叠?



