本章将使用Win32 API netapi32.dll 实现UNC的 连接授权, 断开连接, 获取UNC连接状态三项功能.
一. 首先导入DLL文件:
#region Register Win32 DLL File netapi32.dll //导入连接授权函数NetUseAdd [DllImport("netapi32", CharSet = CharSet.Auto, SetLastError = true), SuppressUnmanagedCodeSecurityAttribute] static extern int NetUseAdd( string UncServerName, // not used int Level, // use info struct level 1 or 2 IntPtr Buf, // Buffer ref int ParmError ); //导入断开连接函数NetUseDel [DllImport("netapi32", CharSet = CharSet.Auto, SetLastError = true), SuppressUnmanagedCodeSecurityAttribute] static extern int NetUseDel( string UncServerName, //not used string UseName, //Network Resource what do you want to disconnect; uint ForceCond //disconnect mode; ); //导入Get连接状态函数NetUseGetInfo [DllImport("netapi32", CharSet = CharSet.Auto, SetLastError = true), SuppressUnmanagedCodeSecurityAttribute] static extern int NetUseGetInfo( string UncServerName, string UseName, int Level, ref IntPtr BufPtr);<PRE></PRE>
上面是已对参数类型做过转换的针对C#写的代码, 有关函数原型的资料请参阅 MSDN,
接下对上面的各项参数做个简单的介绍,足够应付本章所讲内容(更详细的请参阅MSDN):
(按由上到下的顺序介绍)
string UncServerName:
这个参数不用, Call的时候置空.
int Level:
代表采用那种struct将参数传入函数, 1代表USE_INFO_1或2代表USE_INFO_2 , 智能在两者之间选择一种.
IntPtr Buf,
一个指向上面对应结构的指针(C#没有指针,但这的作用很像指针,有知道更专业教法的朋友通知一声),
ref int ParmError:
这个好像没怎么用到,所以没仔细研究, 请直接看SourceCode中的用法
uint ForceCond:
断开连接时的模式, 共有三种:
private const uint USE_NOFORCE = 0; //Do not fail the disconnection if open files exist son the connection.
private const uint USE_FORCE = 1; //Fail the disconnection if open files exists on the connection.
private const uint USE_LOTS_OF_FORCE = 2; //Close any open files and delete the connection.
ref IntPtr BufPtr:
传入GetNetUseInfo的Struct IntPrt对象, 负责将获取的信息带出来.
一下为实现三项功能的完整Source Code:
class WinNet { #region Define netapi32.dll need data structure [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] public struct _USE_INFO_2 { internal string ui2_local; internal string ui2_remote; //internal IntPtr ui2_password; // don't pass a string or StringBuilder here!! internal string ui2_password; internal uint ui2_status; internal uint ui2_asg_type; internal uint ui2_refcount; internal uint ui2_usecount; internal string ui2_username; internal string ui2_domainname; } const uint USE_WILDCARD = 0xFFFFFFFF; private const uint USE_NOFORCE = 0; //Do not fail the disconnection if open files exist son the connection. private const uint USE_FORCE = 1; //Fail the disconnection if open files exists on the connection. private const uint USE_LOTS_OF_FORCE = 2; //Close any open files and delete the connection. private const uint USE_OK = 0; //The connection is valid. private const uint USE_PAUSED = 1; // Paused by local workstation. private const uint USE_SESSLOST = 2;// Disconnected. private const uint USE_DISCONN = 3;// An error occurred. private const uint USE_NETERR = 4;// A network error occurred. private const uint USE_CONN = 5;// The connection is being made. private const uint USE_RECONN = 6;// Reconnecting. private static WinNet _instance = new WinNet(); public static WinNet Instance { get { return _instance; } } #endregion #region Register Win32 DLL File netapi32.dll [DllImport("netapi32", CharSet = CharSet.Auto, SetLastError = true), SuppressUnmanagedCodeSecurityAttribute] static extern int NetUseAdd( string UncServerName, // not used int Level, // use info struct level 1 or 2 IntPtr Buf, // Buffer ref int ParmError ); [DllImport("netapi32", CharSet = CharSet.Auto, SetLastError = true), SuppressUnmanagedCodeSecurityAttribute] static extern int NetUseDel( string UncServerName, //not used string UseName, //Network Resource what do you want to disconnect; uint ForceCond //disconnect mode; ); [DllImport("netapi32", CharSet = CharSet.Auto, SetLastError = true), SuppressUnmanagedCodeSecurityAttribute] static extern int NetUseGetInfo( string UncServerName, string UseName, int Level, ref IntPtr BufPtr); #endregion ////// Establish a use record /// /// /// /// /// public static bool UseRecord(string resource, string user, string password, string domain) { bool RtnValue = false; int ret = 1; int paramError = 0; _USE_INFO_2 use2 = new _USE_INFO_2(); IntPtr pBuf = IntPtr.Zero; //use2.ui2_password = IntPtr.Zero; try { pBuf = Marshal.AllocHGlobal(Marshal.SizeOf(use2)); use2.ui2_local = null; use2.ui2_asg_type = USE_WILDCARD; use2.ui2_remote = resource; //use2.ui2_password = Marshal.StringToHGlobalAuto(password); use2.ui2_password = password; use2.ui2_username = user; use2.ui2_domainname = domain; Marshal.StructureToPtr(use2, pBuf, true); ret = NetUseAdd(null, 2, pBuf, ref paramError); if (ret != 0) { throw new Exception(ErrorCodeHandler(ret)); } RtnValue = true; } catch (Exception e) { throw e; } finally { Marshal.FreeHGlobal(pBuf); } return RtnValue; } ////// Destroy a use record /// /// public static void DeleteRecord(string resource) { int ret = 1; try { ret = NetUseDel(null, resource, USE_LOTS_OF_FORCE); if (ret != 0) { throw new Exception(ErrorCodeHandler(ret)); } } catch (Exception e) { throw e; } } public static _USE_INFO_2 GetUseInfo(string resource) { int ret = 1; _USE_INFO_2 ui2 = new _USE_INFO_2(); IntPtr pBuf = IntPtr.Zero; try { pBuf = Marshal.AllocHGlobal(Marshal.SizeOf(new _USE_INFO_2())); ret = NetUseGetInfo(null, resource, 2, ref pBuf); if (ret != 0) { throw new Exception(ErrorCodeHandler(ret)); } ui2 = (_USE_INFO_2)Marshal.PtrToStructure(pBuf, typeof(_USE_INFO_2)); } catch { //throw e; } finally { Marshal.FreeHGlobal(pBuf); } return ui2; } #region Win32 ErrorCode Handler Code public static string ErrorCodeHandler(int Win32ErrorCode) { string RtnMessage = string.Empty; switch (Win32ErrorCode) { case 67: RtnMessage = "找不到网络名."; break; case 1219: RtnMessage = "不允许一个用户使用一个以上用户名与一个服务器或共享资源的多重连接。中断与此服务器或共享资源的所有连接,然后再试一次... "; break; case 2250: RtnMessage = "此网络连接不存在."; break; default: RtnMessage = "Unkown Error Code."; break; } return RtnMessage + "\r\nWin32 Error Code:" + Win32ErrorCode; } #endregion }
关于API调用出项异常的处理, 这也是我在调试代码的时候碰到的一个Bug, 顺便在这个跟大家分享一下:
通常情况下Api调用出现异常的时候, 会通过GetLastError()方法来取得最后一个错误, 而在我实际执行的时候出现N(N>10)次错误,用GetLastError()得到的错误信息都是"重叠 I/O 操作在进行中", 四处求救没得到答案, 后来我发现在每个API结束之后都会Return一个int类型的返回值: 0代表执行成功, 在本章常用到的已在ErrorCodeHandler()方法中进行了说明.
接下来有空的话计划写一个Windows UNC 共享管理的小程序, 其中将涉及到列出当前所有共享的各种状态,及对所有共享的操作.
Remark:
上面的获取共享状态目前仅能判断connection or disconnection, 应为我的项目对这个只要求这两种状态,所以在没有进行详细的分类.