.Net访问共享文件(带身份验证)

摘要 : . Net access shared files (with authentication)

  
前段时间有一个项目需要实现访问远程共享的功能, 在访问远程服务器时需要提供凭据。在功能实现过程中同事碰到一些问题,通过WMI没有实现。笔者认为这是一个比较典型的常见问题,如果没有接触过,可能一 下子找不到解决方案,因此想借助BLOG写下一个实现方案,为被此类问题困扰的朋友提供一个参考实现。
实现分析
通过对功能需求的分析,笔者一开始想到的是使用 WMI,不过同事试了一下,没有达到预想效果,所以没有再去考虑,打算直接调用mpr.dll来实现该功能。mpr.dll是Windws操作系统网络通 讯相关模块。在.NET中需要通过平台调用(platform invoke)来调用其中的方法。
针对需要实现的功能,需要用到mpr.dll中的两个方法。对于mpr.dll中包含的具体功能这里不进行阐述,大家通过MSDN可以找到详细说明。

DWORD WNetAddConnection2(

LPNETRESOURCE lpNetResource,

LPCTSTR lpPassword,
LPCTSTR lpUsername,
DWORD dwFlags
);
该方法主要功能是添加远程网络连接。
参数描述如下:
lpNetResource:是一个包含网络访问资源的结构,在这里作为参数用来指定详细的网络连接信息,具体请看下面。
lpPassword:指出访问远程计算机的密码。在Windows 95/98/Me 系统中,该值必须设置为NULL或空字符串。
lpUsername:指出访问远程计算机的用户名。在Windows 95/98/Me 系统中,该值必须设置为NULL或空字符串。
dwFlags:指出连接选项,包含六种设置,具体可以看MSDN。

方法返回值:如果方法执行成功返回“NO_ERROR”即“0”。如果执行失败返回System Error Code

NETRESOURCE结构通过多个字段指定网络连接资源信息。定义如下:

typedef struct _NETRESOURCE {

DWORD dwScope;
DWORD dwType;
DWORD dwDisplayType;
DWORD dwUsage;
LPTSTR lpLocalName;
LPTSTR lpRemoteName;
LPTSTR lpComment;
LPTSTR lpProvider;
} NETRESOURCE;
各个成员描述如下:
dwScope:指定枚举范围。设置RESOURCE_CONNECTED,RESOURCE_GLOBALNET,RESOURCE_REMEMBERED三值之一。
dwType:指定访问的资源类型。设置三个值之一。RESOURCETYPE_ANY表示所有资源;RESOURCETYPE_DISK表示磁盘资源;RESOURCETYPE_PRINT表示打印机。
dwDisplayType:指出资源显示类型。RESOURCEDISPLAYTYPE_DOMAIN;RESOURCEDISPLAYTYPE_SERVER;RESOURCEDISPLAYTYPE_SHARE;RESOURCEDISPLAYTYPE_GENERIC。
dwUsage:描述资源的使用方式。设置RESOURCEUSAGE_CONNECTABLE 或RESOURCEUSAGE_CONTAINER。
lpLocalName:同dwScope关联,指定本地映射。
lpRemoteName:远程访问文件夹路径。
lpComment:描述。
lpProvider:资源提供者,可以设置为NULL。
DWORD WNetCancelConnection2(
LPCTSTR lpName,
DWORD dwFlags,
BOOL fForce
);
通过该方法实现释放存在的网络连接。
各成员描述如下:
lpName:指定本地驱动器或远程共享资源

dwFlags:表示断开连接方式。设置 0 或CONNECT_UPDATE_PROFILE。

fForce:如果当前正在使用远程资源,是否进行强制断开连接。如果设置FALSE,在有远程资源使用的情况下关闭连接操作将会失败。

方法返回值:如果方法执行成功返回“NO_ERROR”即“0”。如果执行失败返回System Error Code

针对以上两个方法,有些System Error Code返回值,并不意味远程资源不可以访问,
例如:
ERROR_SESSION_CREDENTIAL_CONFLICT
1219

Multiple connections to a server or shared resource by the same user, using more than one user name, are not allowed. Disconnect all previous connections to the server or shared resource and try again.

ERROR_REMOTE_SESSION_LIMIT_EXCEEDED
1220

An attempt was made to establish a session to a network server, but there are already too many sessions established to that server.

代码实现
便于在.NET下的使用,笔者写了一个WNetConnectionHelper静态类,对这些方法进行包装。
这里用到P/Invoke,需要引用System.Runtime.InteropServices命名空间。
由于代码简短,因此给出完整代码:
//http://blog.csdn.net/zhzuo
public static class WNetConnectionHelper
{

    [DllImport("mpr.dll", EntryPoint = "WNetAddConnection2")]

    private static extern uint WNetAddConnection2(NetResource lpNetResource, string lpPassword, string lpUsername, uint dwFlags);

    [DllImport("Mpr.dll", EntryPoint = "WNetCancelConnection2"))]

    private static extern uint WNetCancelConnection2(string lpName, uint dwFlags, bool fForce);

    [StructLayout(LayoutKind.Sequential)]

    public class NetResource

    {

        public int dwScope;

        public int dwType;

        public int dwDisplayType;

        public int dwUsage;

        public string lpLocalName;

        public string lpRemoteName;

        public string lpComment;

        public string lpProvider;

    }

    public static uint WNetAddConnection(NetResource netResource,string username,string password)

    {

        uint result = WNetAddConnection2(netResource, password, username, 0);

        return result;

    }

    public static uint WNetAddConnection(string username, string password, string remoteName, string localName)

    {

        NetResource netResource = new NetResource();

        netResource.dwScope = 2;       //RESOURCE_GLOBALNET

        netResource.dwType = 1;       //RESOURCETYPE_ANY

        netResource.dwDisplayType = 3; //RESOURCEDISPLAYTYPE_GENERIC

        netResource.dwUsage = 1;       //RESOURCEUSAGE_CONNECTABLE

        netResource.lpLocalName = localName;

        netResource.lpRemoteName = remoteName.TrimEnd('\\');

        //netResource.lpRemoteName = lpComment;

        //netResource.lpProvider = null;

        uint result = WNetAddConnection2(netResource, password, username, 0);

        return result;

    }

    public static uint WNetCancelConnection(string name,uint flags,bool force)

    {

        uint nret = WNetCancelConnection2(name, flags, force);

        return nret;

    }

}
应用示例
列举远程文件目录资源。
try
{

    string result = WNetConnectionHelper.WNetAddConnection("192.120.40.10\\ShareUser", "xxxxxx", @"\\192.120.40.10\test", null);

    string [] files = Directory.GetFiles(@"\\192.120.40.56\test\fw");
    string s1 = GetFile(@"\\192.120.40.10\test\fw\test.doc");
    uint n =   WNetConnectionHelper.WNetCancelConnection("\\192.120.40.10\\test", 1, true);
}
catch (Exception ex)
{
    //编写异常处理代码
}
另外,比较常见的应用是在ASP.NET服务器端访问其他计算机的资源,比如文档服务器,然后通过Web页面输出。
主要示例代码如下:

protected void Page_Load(object sender, EventArgs e)

{

    uint r = WNetConnectionHelper.WNetAddConnection("192.120.40.10\\username", "password", @"\\192.120.40.10\test", "Z:");

    string filename = @"\\192.120.40.10\test\fw\1.doc";

    ReadFile(filename);

}
写文件到Web输出:
public void ReadFile(string filename)
{

    Response.Clear();

    Response.Charset = "utf-8";

    Response.Buffer = true;

    this.EnableViewState = false;

    Response.ContentType = "application/ms-word";

    Response.ContentEncoding = System.Text.Encoding.UTF8;

    Response.AppendHeader("Content-Disposition", "inline;filename=1.doc");

    try

    {

         Response.WriteFile(filename, true);

    }

    catch (Exception ex)

    {

        Response.Write(ex.ToString());

    }

    Response.Flush();

}
以上方法只是示例,对于大文件使用Response.WriteFile方法会有些问题。
补充说明
如果需要了解mpr.dll的功能还是建议大家看 MSDN文档,笔者在本文中的有些功能描述可能不够具体或妥当。对于WNetConnectionHelper的实现存在一些需要完善的地方,不过对于一 般的应用基本可以应付,在实际使用时还需要考虑多线程的情况。另外,在已经有同名驱动器映射的情况下需要做一些判断,而且弄清楚System Error Code的含义,对代码的调试i及正确执行还是有很大的帮助。
上一篇 :用C#编写ActiveX控件(一)
下一篇 :showModalDialog模式窗口提交打开新页面的解决办法