函数|网络
本文将着重介绍Win32 API库中涉及网络管理的函数。首先我要讲一讲在.Net框架中管理用户的两个方法,第一种是Active Directory方法,这种方法要求你安装Active Directory。如果你打算管理一个小网络上的用户,或者一个未安装Active Directory的独立工作站,为了管理用户而安装Active Directory显得有些不值得。另外一种方法则是这篇文章所要讲到的——使用Win32 API库函数。在这篇文章中,我将介绍如何使用C# 添加、删除和修改用户和组,以及如何查询一个主机或网络的用户和网络信息。我们将用到以下函数
- NetUserAdd
- NetUserDel
- NetUserGetInfo
- NetUserSetInfo
- NetUserChangePassword
- NetUserEnum
- NetUserGetLocalGroups
初始化
首先,正如许多C#开发者都知道的,我们要引入InteropServices名字空间到我们的工程中,以便能够访问dll中的函数。这可以通过如下的代码片断实现:
///// 代码片断 1.0
using System.Runtime.InteropServices;
//// 代码结束
一旦我们拥有了访问权限,我们就可以将dll中的函数声明引入,我们将使用方法和结构体来关联它们。函数调用将在下面讨论:
使用C#添加一个用户
在网络函数中最重要的操作之一就是向一个网络或计算机添加一个用户。要通过C#添加一个用户,我们需要使用NetAddUser函数,该函数允许我们添加用户到特定的计算机,如果我们将servername置空,用户将被添加到本地计算机。在下面的代码片断中,我们将看到如何声明和使用NetUserAdd函数。我们在使用该函数前,需要定义一个结构体USER_INFO_1,来作为NetUserAdd的参数。
///// 代码片断 1.1 声明
[DllImport("Netapi32.dll")]
extern static int NetUserAdd([MarshalAs(UnmanagedType.LPWStr)] string servername, int level, ref USER_INFO_1 buf, int parm_err); //// 代码结束
在使用代码时,你要注意到一点——尽管使用了最后一个int参数,你不需要知道返回的错误值。如果你一定要了解错误,需要修改代码。既然我们已经声明了我们要使用的外部API,我们应该声明结构体了。
/**////// 代码片断 1.2 结构体声明
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct USER_INFO_1
{
public string usri1_name;
public string usri1_password;
public int usri1_password_age;
public int usri1_priv;
public string usri1_home_dir;
public string comment;
public int usri1_flags;
public string usri1_script_path;
}
/**///// 代码结束
在声明之后,我们就可以在我们的程序中调用该函数。下面是NetUserAdd的如何使用的代码:
/**////// 代码片断 1.3 NetUserAdd
USER_INFO_1 NewUser = new USER_INFO_1(); // Create an new instance of the USER_INFO_1 struct
NewUser.usri1_name = "UserTestOne"; // Allocates the username
NewUser.usri1_password = "password"; // allocates the password
NewUser.usri1_priv = 1; // Sets the account type to USER_PRIV_USER
NewUser.usri1_home_dir = null; // We didn't supply a Home Directory
NewUser.comment = "My First User Made through C#"; // Comment on the User
NewUser.usri1_script_path = null; // We didn't supply a Logon Script Path
if(NetUserAdd(null ,1 ,ref NewUser, 0)!=0) // If the call fails we get a non-zero value
{
MessageBox.Show("Error Adding User","Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
/**///// 代码结束
上面的代码将向当前的本地计算机添加用户,但是正如我说的,如果你想向网络上的另一台计算机添加用户,你可以将第一个参数中的null替换为那台计算机的名称。
使用C#删除一个用户
与前面的函数相比,删除用户的函数要简单得多。在上面的代码中,如果添加用户失败,返回非零值。与上面的代码相同,删除用户函数也是如此。要从本地计算机删除用户,你可以使用下面的代码片断。
/**////// 代码片断 1.4 声明
[DllImport("Netapi32.dll")]
extern static int NetUserDel([MarshalAs(UnmanagedType.LPWStr)] string servername, [MarshalAs(UnmanagedType.LPWStr)] string username);
/**///// 代码结束
NetUserDel的调用代码如下:
/**////// 代码片断 1.5 NetUserDel
if(NetUserDel(null ,"UserTestOne")!=0) // If the call fails we get a non-zero value
{
MessageBox.Show("Error Removing User","Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
/**///// 代码结束
与NetUserAdd的调用一样,如果想删除远程计算机上的用户,需要将第一个参数替换为远程计算机名。
使用C#获得并修改用户信息
要在C#中获得用户信息,我们需要调用NetUserGetInfo,这个调用需要使用一个结构体来管理数据,并且用户信息会返回到结构体中。与NetUserGetInfo相关的函数是NetUserSetInfo,你可以使用该函数修改你获得的用户信息。要注意的是,这两个函数相互依赖,例如使用NetUserSetInfo函数,你必须知道用户的权限级别(privilege level),权限级别是通过NetUserGetInfo函数获得的。在下面的代码片断中,我们将看到这两个函数的声明,另外我们将再次使用USER_INFO_1结构体。
/**////// 代码片断 1.6 声明
[DllImport("Netapi32.dll")]
extern static int NetUserGetInfo([MarshalAs(UnmanagedType.LPWStr)] string servername,[MarshalAs(UnmanagedType.LPWStr)] string username,int level,out IntPtr bufptr);
[DllImport("Netapi32.dll")]
extern static int NetUserSetInfo([MarshalAs(UnmanagedType.LPWStr)] string servername,[MarshalAs(UnmanagedType.LPWStr)] string username,int level,ref USER_INFO_1 buf, int error);
/**///// 代码结束
使用这些声明,我们可以十分轻松地获得和修改用户设置。在下面的代码中,我们将获得我们先前添加的用户UserTestOne的用户信息,并且我们将修改一些用户信息。
/**////// 代码片断 1.7 NetUserGetInfo
IntPtr bufPtr;
USER_INFO_1 User = new USER_INFO_1();
if(NetUserGetInfo(null, "Administrator",1,out bufPtr)!=0)
{
MessageBox.Show("Error Getting User Info","Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
User = (USER_INFO_1)Marshal.PtrToStructure(bufPtr, typeof(USER_INFO_1));
MessageBox.Show("Users Name: " + User.usri1_name + " Users Comments: " + User.comment + " Users Privilege Level: " + User.usri1_priv);
/**///// 代码结束
在这个例子中,我们使用了Marshaling来获得数据,这是我唯一找到的有效方法。
/**////// 代码片断 1.8 NetUsetSetInfo
USER_INFO_1 Update = new USER_INFO_1();
Update.comment = "This is Our C# Updated Comment";
Update.usri1_priv = 2; // Note that this can only be obtained programmatically using NetUserGetInfo
if(NetUserSetInfo(null, "UserTestOne",1,ref Update,0)!=0)
{
MessageBox.Show("Error Setting User Info","Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
/**///// 代码结束
我们来总结一下,NetUserSetInfo依靠NetUserGetInfo正常工作。与其他的网络函数一样,如果你修改第一个参数为计算机名称,你将可以远程使用该函数。
使用c#修改用户密码
另外一个网络管理的重要函数是用来修改密码的。函数NetUserChangePassword是在我们能够提供当前用户的原密码的基础上进行工作的。声明NetUserChangePassword可以使用如下代码片断:
/**////// 代码片断 1.9 声明
[DllImport("Netapi32.dll")]
extern static int NetUserChangePassword([MarshalAs(UnmanagedType.LPWStr)] string domainname,[MarshalAs(UnmanagedType.LPWStr)] string username,[MarshalAs(UnmanagedType.LPWStr)] string oldpassword,[MarshalAs(UnmanagedType.LPWStr)] string newpassword);
/**///// 代码结束
使用以上声明,如果我们知道用户原密码的话,我们就可以修改用户的密码。同样,我们可以远程使用该函数,只需要将null参数改为特定计算机的名称即可。
/**////// 代码片断 2.0 NetUserChangePassword
if(NetUserChangePassword(null, "UserTestOne", "password", "ournewpassword")!=0)
{
MessageBox.Show("Error Changing User Password","Error",MessageBoxButtons.OK,MessageBoxIcon.Error);
}
/**///// 代码结束
获得用户列表
在管理一个网络时,有一份准确的网络用户列表或计算机列表是很重要的。如果要获得这样一份列表,我们必须使用NetUserEnum函数,该函数返回用户的名称和相关数据到一个结构体。在这个例子中,我们打算使用USER_INFO_0结构体来传递值。由于我们只需要获得用户名,于是我就将username元素添加到结构体的声明中。要注意的是,这个函数使用网络缓冲(network buffer),该缓冲必须被释放,以节省资源,我们可以使用NetAPIBufferFree函数。一旦我们进行了声明,我们就可以使用如下代码片断获得用户列表。
/**////// 代码片断 2.1 声明
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct USER_INFO_0
{
public String Username;
}
[DllImport("Netapi32.dll")]
extern static int NetUserEnum([MarshalAs(UnmanagedType.LPWStr)] string servername, int level, int filter, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries, out int resume_handle);
[DllImport("Netapi32.dll")]
extern static int NetApiBufferFree(IntPtr Buffer);
/**///// 代码结束
一旦我们进行了声明,我们就可以使用如下代码片断获得用户列表。
/**////// 代码片断 2.2 NetUserEnum
int EntriesRead;
int TotalEntries;
int Resume;
IntPtr bufPtr;
NetUserEnum(null, 0, 2, out bufPtr, -1, out EntriesRead, out TotalEntries, out Resume);
if(EntriesRead> 0)
{
USER_INFO_0[] Users = new USER_INFO_0[EntriesRead];
IntPtr iter = bufPtr;
for(int i=0; i < EntriesRead; i++)
{
Users[i] = (USER_INFO_0)Marshal.PtrToStructure(iter, typeof(USER_INFO_0));
iter = (IntPtr)((int)iter + Marshal.SizeOf(typeof(USER_INFO_0)));
MessageBox.Show(Users[i].Username);
}
NetworkAPI.NetApiBufferFree(bufPtr);
}
/**///// 代码结束
上面的代码通过MessageBox来枚举显示本地计算机上的用户。我们也可以替换null字符串参数指定一台远程计算机。
识别用户组的关系
在这篇文章中我们要了解的最后一个函数是NetUserGetLocalGroups。这个函数允许我们判断一个用户属于哪些组,并且显示这些组。与前面的函数相同,在使用该函数后,我们要清除网络缓冲。NetUserGetLocalGroups的声明同样需要一个结构体LOCALGROUP_USERS_INFO_0,用于返回组名称。
/**////// 代码片断 2.3 声明
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Unicode)]
public struct LOCALGROUP_USERS_INFO_0
{
public string groupname;
}
[DllImport("Netapi32.dll")]
public extern static int NetUserGetLocalGroups([MarshalAs(UnmanagedType.LPWStr)] string servername,[MarshalAs(UnmanagedType.LPWStr)] string username, int level, int flags, out IntPtr bufptr, int prefmaxlen, out int entriesread, out int totalentries);
/**///// 代码结束
通过上面的声明,我们可以使用与前面的函数类似的代码调用该函数。
/**////// 代码片断 2.4 NetUserGetLocalGroups
int EntriesRead;
int TotalEntries;
IntPtr bufPtr;
NetUserGetLocalGroups(null, "Administrator",0,0,out bufPtr,1024,out EntriesRead, out TotalEntries);
if(EntriesRead> 0)
{
LOCALGROUP_USERS_INFO_0[] RetGroups = new LOCALGROUP_USERS_INFO_0[EntriesRead];
IntPtr iter = bufPtr;
for(int i=0; i < EntriesRead; i++)
{
RetGroups[i] = (LOCALGROUP_USERS_INFO_0)Marshal.PtrToStructure(iter, typeof(LOCALGROUP_USERS_INFO_0));
iter = (IntPtr)((int)iter + Marshal.SizeOf(typeof(LOCALGROUP_USERS_INFO_0)));
MessageBox.Show(RetGroups[i].groupname);
}
NetApiBufferFree(bufPtr);
}
/**///// 代码结束
上面的例子返回用户Administrator所在的组。通过本文,我们了解了如何通过.Net平台调用方法使用与管理用户相关的网络函数。关于原作者[见原文]