| 18.2 | 局域网技术
局域网是由在物理上彼此相隔不远的一组台式计算机和其他设备,以允许用户意见相互通信和共享计算机资源的方式互连在一起的系统,其特点是传输速率高、误码率低,具有较高的稳定性和可扩充性。本节将介绍有关局域网的相关知识。
18.2.1 获取网卡地址
在实际的应用程序中,经常需要在程序运行时获取MAC地址作为某种标识。MAC地址是网络适配器的物理地址,网络适配器又称网卡。而MAC地址能够标识网络中一台惟一的计算机,所以网卡的物理地址是惟一的。如果想获得网卡地址可以通过调用Netbios函数来实现。
下面以一个实例来说明如何获取网卡地址,具体操作步骤如下。
(1)新建一个基于对话框的应用程序,设计对话框资源如图18.1所示。
(2)处理“获取”按钮的单击事件,在程序中调用Netbios函数获取网卡地址。
// 因为是通过NetAPI来获取网卡信息,所以需要包含其题头文件nb30.h #include < nb30.h >
typedef struct _ASTAT_
{
ADAPTER_STATUS adapt;
NAME_BUFFER NameBuff [30];
}ASTAT, * PASTAT;
ASTAT AdaINfo;
---- // 定义一个存放返回网卡信息的变量
---- // 输入参数:lana_num为网卡编号,一般地,从0开始
void CGetMACDlg::OnGetmac()
{
NCB nInfo;
memset(&nInfo,0,sizeof(NCB));
nInfo.ncb_command = NCBRESET;
nInfo.ncb_lana_num = 0; // 指定网卡号
Netbios(&nInfo);
//初始化NetBIOS
memset(&nInfo,0,sizeof(NCB));
nInfo.ncb_command = NCBASTAT;
nInfo.ncb_lana_num = 0;
nInfo.ncb_buffer = (unsigned char*)&AdaINfo;
nInfo.ncb_length = sizeof(ADAPTER_INFO);
strncpy( (char*)nInfo.ncb_callname,"*",NCBNAMSZ);
Netbios(&nInfo);
CString macAddr;
macAddr.Format("%02X%02X-%02X%02X-%02X%02X",AdaINfo.nStatus.adapter_address[0],
AdaINfo.nStatus.adapter_address[1],
AdaINfo.nStatus.adapter_address[2],
AdaINfo.nStatus.adapter_address[3],
AdaINfo.nStatus.adapter_address[4],
AdaINfo.nStatus.adapter_address[5]
);
m_MacAddr.SetWindowText(macAddr);
}
(3)运行程序,效果如图18.2所示。
图18.1 对话框资源设计窗口 图18.2 获取网卡地址
18.2.2 映射网络驱动器
Windows提供的“映射网络驱动器”命令准许用户在“我的电脑”或“Windows资源管理器”中显示网络资源,这使得网络资源更易于查找。对于经常使用的网络资源或者当准确知道想要连接的网络路径和资源名时,可以使用“映射网络驱动器”。设置“映射网络驱动器”可以根据WNetAddConnection2函数连接到指定的网络资源,并用指定的盘符代表这个连接,再使用WNetAddConnection2函数取消到指定网络资源的连接即可。其中,WNetAddConnection2函数是用于创建同一个网络资源的连接。
语法:
DWORD WNetAddConnection2( LPNETRESOURCE lpNetResource,LPCTSTR
lpPassword,LPCTSTR lpUsername,DWORD dwFlags );
WNetAddConnection2函数参数说明如表18.2所示。
表18.2 WNetAddConnection2函数参数说明
参 数 名 称 | 参 数 说 明 |
lpNetResource | 为NETRESOURCE结构指针,标识网络资源 |
lpPassword | 可选的一个密码。如为vbNullString,表示采用当前用户的默认密码。如为一个空字串,则不用任何密码 |
lpUsername | 用于连接的用户名。如为vbNullString,表示使用当前用户 |
dwFlags | 设为零;或指定常数CONNECT_UPDATE_PROFILE,表示创建永久性连接。 |
下面根据一个例子来介绍如何使用“映射网络驱动器”命令,具体操作步骤如下。
ch1802实例位置:mr/18/sl/02
(1)新建一个基于对话框的应用程序,设计对话框资源如图18.3所示。
(2)处理“…”按钮单击事件,选择网络目录。
void CNetDiskDlg::OnBrown()
{
UpdateData(TRUE);
BROWSEINFO bInfo;
bInfo.hwndOwner = this->m_hWnd;
bInfo.pidlRoot = NULL;
bInfo.pszDisplayName = NULL;
bInfo.lpszTitle = "浏览文件夹";
bInfo.ulFlags = 0;
bInfo.lpfn = NULL;
bInfo.lParam = 0;
bInfo.iImage = 0;
//弹出文件浏览对话框
ITEMIDLIST* pItemList = SHBrowseForFolder(&bInfo);
CString folderdir ="C://";
//获取用户选择的目录
SHGetPathFromIDList(pItemList,folderdir.GetBuffer(0));
m_Netdir = folderdir;
UpdateData(FALSE);
}
(3)处理“添加”按钮的单击事件,将网络目录映射到本地磁盘。
void CNetDiskDlg::OnAddnetdisk()
{
UpdateData();
//定义网络资源
NETRESOURCE nsource;
nsource.dwScope = RESOURCE_GLOBALNET;
nsource.dwType =RESOURCETYPE_DISK;
nsource.dwDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
nsource.dwUsage = RESOURCEUSAGE_CONNECTABLE;
nsource.lpLocalName = m_LocalDisk.GetBuffer(0);
nsource.lpRemoteName = m_Netdir.GetBuffer(0);
nsource.lpProvider = NULL;
nsource.lpComment = NULL;
//添加网络资源
DWORD ret = WNetAddConnection2(&nsource,NULL,NULL,0);
if (ret==NO_ERROR)
{
MessageBox("设置成功");
}
else
{
MessageBox("设置失败");
}
}
(4)运行程序,效果如图18.4所示。
图18.3 资源设计窗口 图18.4 映射网络驱动器
18.2.3 获取局域网计算机名称和IP
在实际的应用程序中,经常需要在程序检索整个局域网络,获取局域网络中的计算机名和IP地址。如果想获得计算机名主要使用了Windows API函数库中的WnetOpenEnum、WnetEnumResource和WnetCloseEnum函数,而在使用这些函数之前,需要初始化向程序中导入mpr.lib库和头文件winnetwk.h;如果想获得计算机IP主要使用了gethostbyname函数,在使用该函数之前,需要导入ws2_32.lib库和头文件afxsock.h。其中各函数的介绍如下:
(1)WnetOpenEnum函数
WnetOpenEnum函数用于启动对网络资源进行枚举的过程。
语法:
DWORD WnetOpenEnum( DWORD dwScope,DWORD dwType,DWORD dwUsage,
LPNETRESOURCE lpNetResource,LPHANDLE lphEnum );
WnetOpenEnum函数参数说明如表18.3所示。
表18.3 WnetOpenEnum函数参数说明
参 数 名 称 | 参 数 说 明 |
DwScope | 表示网络枚举的范围 |
DwType | 表示枚举的资源类型 |
DwUsage | 表示枚举资源的用法 |
LpNetResource | 用于返回网络资源信息 |
LphEnum | 表示枚举的资源句柄指针 |
(2)WnetEnumResource函数
WnetEnumResource函数用于枚举网络资源。
语法:
DWORD WnetEnumResource( HANDLE hEnum,LPDWORD lpcCount,LPVOID lpBuffer
,LPDWORD lpBufferSize );
WnetEnumResource函数参数说明如表18.4所示。
表18.4 WnetEnumResource函数参数说明
参 数 名 称 | 参 数 说 明 |
hEnum | 由WnetOpenEnum函数的参数lphEnum传入,表示枚举的资源句柄 |
lpcCount | 用来决定获取的资源数目最大值 |
lpBuffer | 向枚举结果存放的缓冲区地址 |
lpBufferSize | 指向枚举结果存储缓冲区大小的变量地址 |
(3)WnetCloseEnum函数
WnetCloseEnum函数用于结束一次枚举操作。
语法:
DWORD WnetCloseEnum( HANDLE hEnum );
hEnum:由WnetOpenEnum函数的参数lphEnum传入。
(4)gethostbyname函数
gethostbyname函数能够通过计算机的名称返回其网络信息,这个信息中包括IP地址。
语法:
struct hostent FAR * gethostbyname ( const char FAR * name );
name:包含计算机名称的字符串。
下面以一个具体实例介绍如何获得网络计算的名称和IP。程序设计具体步骤如下。
ch1803实例位置:mr/18/sl/03
(1)新建一个基于对话框的应用程序,设计对话框资源如图18.5所示。
图18.5 主对话框资源设计窗口
(2)在对话框的OnInitDialog方法中获取网络中的计算机和IP。
//初始化网络库
WSADATA wsd;
WSAStartup(MAKEWORD(2,2),&wsd);
//设置表格风格
m_grid.SetExtendedStyle(LVS_EX_FLATSB
|LVS_EX_FULLROWSELECT
|LVS_EX_HEADERDRAGDROP
|LVS_EX_ONECLICKACTIVATE
|LVS_EX_GRIDLINES);
//向表格中插入列
m_grid.InsertColumn(0,"计算机名",LVCFMT_LEFT,200,0);
m_grid.InsertColumn(1,"IP地址",LVCFMT_LEFT,200,0);
DWORD Count=0xFFFFFFFF,Bufsize=4096,Res;
NETRESOURCE* nRes;
NETRESOURCE* nRes1;
NETRESOURCE* nRes2;
HANDLE lphEnum;
LPVOID Buf = new char[4096];
LPVOID Buf1 = new char[4096];
LPVOID Buf2 = new char[4096];
//打开列举的网络资源
Res = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY,
RESOURCEUSAGE_CONTAINER,NULL,&lphEnum);
//获取列举的网络资源信息
Res=WNetEnumResource(lphEnum,&Count,Buf,&Bufsize);
nRes=(NETRESOURCE*)Buf;
for(DWORD n=0;n<Count;n++,nRes++)
{
DWORD Count1=0xFFFFFFFF;
Res = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY,
RESOURCEUSAGE_CONTAINER,nRes,&lphEnum);
Res=WNetEnumResource(lphEnum,&Count1,Buf1,&Bufsize);
nRes1=(NETRESOURCE*)Buf1;
for(DWORD i=0;i<Count1;i++,nRes1++)
{
DWORD Count2=0xFFFFFFFF;
Res = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY,
RESOURCEUSAGE_CONTAINER,nRes1,&lphEnum);
Res=WNetEnumResource(lphEnum,&Count2,Buf2,&Bufsize);
nRes2=(NETRESOURCE*)Buf2;
for(DWORD j=0;j<Count2;j++,nRes2++)
{
m_grid.InsertItem(j,0);
CString sName=nRes2->lpRemoteName;
sName=sName.Right(sName.GetLength()-2);
m_grid.SetItemText(j,0,sName);
CString str="";
struct hostent * pHost;
pHost = gethostbyname(sName);
if(pHost==NULL)
{
m_grid.SetItemText(j,1,"无法获得IP地址");
}
else
{
for(int n=0;n<4;n++)
{
CString addr;
if(n > 0)
{
str += ".";
}
addr.Format("%u",(unsigned int)((unsigned char*)pHost->h_addr_list[0])[n]);
str += addr;
}
m_grid.SetItemText(j,1,str);
}
}
}
}
delete Buf;
delete Buf1;
delete Buf2;
WNetCloseEnum(lphEnum);
(3)运行程序,效果如图18.6所示。
图18.6 获取局域网计算机名称和IP
18.2.4 获得网上共享资源
在有局域网的单位中,如果能将一些资源共享,不但能提高资源的有效利用,还能提高工作效率,节省资源。但资源共享后,如果不进行有效的管理和利用,将不能高效、合理地使用网上资源。如果想获得网上共享资源可以根据Windows API函数库中的WnetOpenEnum、WnetEnumResource和WnetCloseEnum函数来实现。有关这些函数的介绍请参考18.2.3节获取局域网计算机名称和IP。
下面根据一个例子来介绍如何获得网上共享资源,以方便查看局域网上的资源。具体操作步骤如下。
ch1804实例位置:mr/18/sl/04
(1)新建一个基于对话框的应用程序,设计对话框资源如图18.7所示。
图18.7 网上共享资源设计窗口
(2)在对话框的OnInitDialog方法中列举共享资源。
//创建图像列表控件
m_imagelist.Create(16,16,ILC_COLOR24|ILC_MASK,0,0);
//向图像列表中添加图标
m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON3));
m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON1));
m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON2));
m_imagelist.Add(AfxGetApp()->LoadIcon(IDI_ICON4));
HICON hIcon=::LoadIcon(AfxGetResourceHandle(),MAKEINTRESOURCE(IDR_MAINFRAME));
m_Tree.SetImageList(&m_imagelist,LVSIL_NORMAL);
m_Root=m_Tree.InsertItem("整个网络",0,0);
m_Tree.Expand(m_Root,TVE_EXPAND);
DWORD Count=0xFFFFFFFF,Bufsize=4096,Res;
NETRESOURCE* nRes;
NETRESOURCE* nRes1;
NETRESOURCE* nRes2;
NETRESOURCE* nRes3;
HANDLE lphEnum;
LPVOID Buf = new char[4096];
LPVOID Buf1 = new char[4096];
LPVOID Buf2 = new char[4096];
LPVOID Buf3 = new char[4096];
//打开网络资源列举
Res = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY,
RESOURCEUSAGE_CONTAINER,NULL,&lphEnum);
//列举网络资源
Res=WNetEnumResource(lphEnum,&Count,Buf,&Bufsize);
nRes=(NETRESOURCE*)Buf;
for(DWORD n=0;n<Count;n++,nRes++)
{
DWORD Count1=0xFFFFFFFF;
Res = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY,
RESOURCEUSAGE_CONTAINER,nRes,&lphEnum);
Res=WNetEnumResource(lphEnum,&Count1,Buf1,&Bufsize);
nRes1=(NETRESOURCE*)Buf1;
for(DWORD i=0;i<Count1;i++,nRes1++)
{
m_Group = m_Tree.InsertItem(nRes1->lpRemoteName,1,1,m_Root);
DWORD Count2=0xFFFFFFFF;
Res = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY,
RESOURCEUSAGE_CONTAINER,nRes1,&lphEnum);
Res=WNetEnumResource(lphEnum,&Count2,Buf2,&Bufsize);
nRes2=(NETRESOURCE*)Buf2;
for(DWORD j=0;j<Count2;j++,nRes2++)
{
CString sName = nRes2->lpRemoteName;
sName = sName.Right(sName.GetLength()-2);
m_Name = m_Tree.InsertItem(sName,2,2,m_Group);
DWORD Count3=0xFFFFFFFF;
Res = WNetOpenEnum(RESOURCE_GLOBALNET, RESOURCETYPE_ANY,
RESOURCEUSAGE_CONNECTABLE,nRes2,&lphEnum);
Res=WNetEnumResource(lphEnum,&Count3,Buf3,&Bufsize);
nRes3=(NETRESOURCE*)Buf3;
for(DWORD k=0;k<Count3;k++,nRes3++)
{
CString sShare = nRes3->lpRemoteName;
sShare = sShare.Right(sShare.GetLength()-3-sName.GetLength());
m_Tree.InsertItem(sShare,3,3,m_Name);
}
}
}
}
delete Buf;
delete Buf1;
delete Buf2;
delete Buf3;
WNetCloseEnum(lphEnum);
(3)运行程序,效果如图18.8所示。
图18.8 获得网上共享资源
18.2.5 在局域网中发送短消息
Windows系统提供有信使服务,用户可以通过该服务向局域网中的用户发送信息。为了使用Windows信使服务,需要使用netapi函数NetMessageBufferSend。为了使用该函数,需要从netapi32.dll动态库中导入该函数。
下面根据一个例子来介绍如何在局域网中发送短消息。具体操作步骤如下。
ch1805实例位置:mr/18/sl/05
(1)新建一个基于对话框的应用程序,向对话框中添加按钮、编辑框、静态文本等控件,如图18.9所示。
(2)处理“发送”按钮的单击事件,向指定计算机发送信息。
typedef UINT ( __stdcall *funNetMessageBufferSend)
(LPCWSTR,LPCWSTR,LPCWSTR,LPCWSTR,DWORD);
void CSendMsgDlg::OnSend()
{
CString name;
m_Merchine.GetWindowText(name);
if (name.IsEmpty())
{
MessageBox("请输出对方名称");
return;
}
//加载网络库
HINSTANCE hInstance = LoadLibrary("netapi32.dll");
//获取函数入口地址
funNetMessageBufferSend NetMessageBufferSend = (funNetMessageBufferSend)
GetProcAddress(hInstance,"NetMessageBufferSend");
if (NetMessageBufferSend)
{
CString content;
m_Content.GetWindowText(content);
CString localname;
localname.GetBufferSetLength(MAX_PATH);
gethostname(localname.GetBuffer(0), MAX_PATH);
NetMessageBufferSend(NULL,name.AllocSysString(),NULL
,_T(content.AllocSysString()),content.GetLength()*2);
content.ReleaseBuffer();
localname.ReleaseBuffer();
}
FreeLibrary(hInstance);
}
(3)运行程序,效果如图18.10所示。
图18.9 对话框设计窗口 图18.10 在局域网中发送短消息