C#中使用网络函数 (第一部分 用户函数)[翻译]

函数|网络

本文将着重介绍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平台调用方法使用与管理用户相关的网络函数。关于原作者[见原文]

时间: 2024-08-31 03:46:14

C#中使用网络函数 (第一部分 用户函数)[翻译]的相关文章

各位高手,如何用javascript函数清空DataGrid中某一行第一个单元格中的数值

问题描述 各位高手,如何用javascript函数清空DataGrid中某一行第一个单元格中的数值 解决方案 解决方案二:隐藏可以不?解决方案三:你可以在ITEMDATEBING事件中给这个单元格设置个ID然后再在脚本中隐藏这个单元格

C语言中操作密码文件的一些函数总结_C 语言

C语言setpwent()函数:从头读取密码文件中的账号数据 头文件: #include <pwd.h> #include <sys/types.h> 定义函数: void setpwent(void); 函数说明:setpwent()用来将getpwent()的读写地址指回密码文件开头. 范例 #include <pwd.h> #include <sys/types.h> main() { struct passwd *user; int i; for(i

python中map、any、all函数用法分析

  这篇文章主要介绍了python中map.any.all函数用法,实例分析了map.any.all函数的相关使用技巧,具有一定参考借鉴价值,需要的朋友可以参考下 本文实例讲述了python中map.any.all函数用法.分享给大家供大家参考.具体分析如下: 最近想学python,就一直比较关注python,昨天在python吧看到有个帖子提问怎么在python中怎么判断密码是否符合规范,回帖中有很多用循环的,除此外还有一个没有用循环,代码非常简练,下面是代码: ? 1 2 3 4 5 6 d

NodeJS中利用Promise来封装异步函数

这篇文章主要介绍了NodeJS中利用Promise来封装异步函数,使用统一的链式API来摆脱多重回调的噩梦,非常的实用的小技能,希望小伙伴们能够喜欢 在写Node.js的过程中,连续的IO操作可能会导致"金字塔噩梦",回调函数的多重嵌套让代码变的难以维护,利用CommonJs的Promise来封装异步函数,使用统一的链式API来摆脱多重回调的噩梦. Node.js提供的非阻塞IO模型允许我们利用回调函数的方式处理IO操作,但是当需要连续的IO操作时,你的回调函数会多重嵌套,代码很不美观

【转】【UNITY3D 游戏开发之七】C# 中的委托、事件、匿名函数、LAMBDA 表达式

本站文章均为 李华明Himi 原创,转载务必在明显处注明:  转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/unity3d-game/1613.html       Unity3d 开发中,常用的莫过于委托和事件了,所以转载一篇相关文章,写的比较详细的,这里分享一下.      对于匿名函数以及Lambda表达式也是非常常用的,这里就直接分享链接,童鞋们自行学习.     匿名函数.Lambda表达式:http://www.cnblogs.com/

php array_walk 对数组中的每个元素应用用户自定义函数详解_php实例

php array_walk 对数组中的每个元素应用用户自定义函数 array_walk 使用用户自定义函数对数组中的每个元素做回调处理 基本语法 bool array_walk ( array &$array , callable $funcname [, mixed $userdata = NULL ] ) 将用户自定义函数 funcname 应用到 array 数组中的每个单元. array_walk() 不会受到 array 内部数组指针的影响. array_walk() 会遍历整个数组

NodeJS中利用Promise来封装异步函数_node.js

 在写Node.js的过程中,连续的IO操作可能会导致"金字塔噩梦",回调函数的多重嵌套让代码变的难以维护,利用CommonJs的Promise来封装异步函数,使用统一的链式API来摆脱多重回调的噩梦. Node.js提供的非阻塞IO模型允许我们利用回调函数的方式处理IO操作,但是当需要连续的IO操作时,你的回调函数会多重嵌套,代码很不美观,而且不易维护,而且可能会有许多错误处理的重复代码,也就是所谓的"Pyramid of Doom". 复制代码 代码如下: st

php中current、next与reset函数用法实例_php技巧

本文实例讲述了php中current.next与reset函数用法.分享给大家供大家参考. 具体代码如下: 复制代码 代码如下: $array=array('step one','step two','step three','step four');  //定义一个数组 echo current($array)."<br/>n";       //返回数组第一个元素 next($array);          //数组指针后移一位 next($array);      

string中c_str(),data(),copy(p,n)函数的用法总结_C 语言

标准库的string类提供了3个成员函数来从一个string得到c类型的字符数组:c_str().data().copy(p,n). 1. c_str():生成一个const char*指针,指向以空字符终止的数组. 注:①这个数组的数据是临时的,当有一个改变这些数据的成员函数被调用后,其中的数据就会失效.因此要么现用先转换,要么把它的数据复制到用户自己可以管理的内存中.注意.看下例: 复制代码 代码如下: const char* c; string s="1234"; c = s.c