使用WNetEnumResource函数实现枚举工作组内的主机及其IP

背景

之所以会学习到这方面的知识,是因为那段时间正在帮一个游戏工作室开发一个游戏自动登录并创建角色的游戏脚本。当时,我就是使用VS去开发。因为它要求要有一个控制端,所以,就分别写了一个客户端程序和控制端程序。客户端都运行在虚拟机内,和控制端在同一网段里。

当时,我就想让客户端在虚拟机里运行,主动去扫描工作组内的主机,那么它工作组内就会有两个主机,一个是虚拟机自己,另一个就是外面的主机。主机上运行则控制端,所以,这样就可以获取主机的IP地址,并自动建立反向连接,传输数据。

函数介绍

// 启动网络资源或现有连接的枚举
// 如果函数成功,返回值为NO_ERROR。
DWORD WNetOpenEnum(
     _In_  DWORD         dwScope,				// 枚举范围
     _In_  DWORD         dwType,				// 要枚举的资源类型
     _In_  DWORD         dwUsage,				// 要枚举的资源使用类型
     _In_  LPNETRESOURCE lpNetResource,			// 指向指定要枚举的容器的NETRESOURCE结构
     _Out_ LPHANDLE      lphEnum				// 指向可以在随后调用WNetEnumResource中使用的枚举句柄的指针
);


// 继续列出通过调用 WNetOpenEnum 函数启动的网络资源
// 执行成功,返回 NO_ERROR 或者 ERROR_NO_MORE_ITEMS;
DWORD WNetEnumResource(
     _In_    HANDLE  hEnum,					// 标识枚举实例的句柄。由WNetOpenEnum
     _Inout_ LPDWORD lpcCount,				// 指向指定所请求条目数的变量的指针
     _Out_   LPVOID  lpBuffer,				// 指向接收枚举结果的缓冲区的指针
     _Inout_ LPDWORD lpBufferSize			// lpBuffer 缓冲区大小
);

// 从主机数据库中检索与主机名对应的主机信息
// 如果没有发生错误,gethostbyname返回一个指向上述主机结构的指针。 否则,它返回一个空指针
struct hostent* FAR gethostbyname(
       _In_ const char *name			// 指向要解析的主机的以NULL结尾的名称的指针
);


// 将(Ipv4)Internet网络地址转换为Internet标准点分十进制格式的ASCII字符串
// 如果没有发生错误,则inet_ntoa返回一个字符指针,否则返回NULL
char* FAR inet_ntoa(
      _In_ struct   in_addr in			// 一个表示Internet主机地址的in_addr结构
);

实现原理

本文要实现的功能就是遍历网络邻居,获取工作组内的所有在线主机名以及根据主机名获取的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;
    // 通过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;
}

测试

在这里插入图片描述

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值