最近要实现访问远程主机的共享目录中的一个文件。遇到了权限问题。google了一下,找到了几种解决方法,记录如下:

一、调用Net use命令

// 使用方法:
        //if (Connect("192.168.1.48", "用户名", "密码"))   
        //{
        //    File.Copy(@"\\192.168.1.48\共享目录\test.txt",   @"e:\\test.txt",   true);   
        //}
        public bool Connect(string remoteHost, string userName, string passWord)
        {
            bool Flag = true;
            Process proc = new Process();
            proc.StartInfo.FileName = "cmd.exe";
            proc.StartInfo.UseShellExecute = false;
            proc.StartInfo.RedirectStandardInput = true;
            proc.StartInfo.RedirectStandardOutput = true;
            proc.StartInfo.RedirectStandardError = true;
            proc.StartInfo.CreateNoWindow = true;
            try
            {
                proc.Start();
                string command = @"net  use  \\" + remoteHost + "  " + passWord + "  " + "  /user:" + userName + ">NUL";
                proc.StandardInput.WriteLine(command);
                command = "exit";
                proc.StandardInput.WriteLine(command);
                while (proc.HasExited == false)
                {
                    proc.WaitForExit(1000);
                }
                string errormsg = proc.StandardError.ReadToEnd();
                if (errormsg != "")
                    Flag = false;
                proc.StandardError.Close();
            }
            catch (Exception ex)
            {
                Flag = false;
            }
            finally
            {
                proc.Close();
                proc.Dispose();
            }
            return Flag;
        }

二、调用WNetAddConnection2、WNetAddConnection3或者NetUseAdd函数,进行磁盘映射。

using System;
using System.Collections.Generic;
using System.Text;       
using System.Runtime.InteropServices;

namespace WindowsApplication1
{
    public class MyMap
    {
        [DllImport("mpr.dll", EntryPoint = "WNetAddConnection2")]
        public static extern uint WNetAddConnection2(
            [In] NETRESOURCE lpNetResource,
            string lpPassword,
            string lpUsername,
            uint dwFlags);
        
        [DllImport("Mpr.dll")]
        public 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 LocalName;
            public string RemoteName;
            public string Comment;
            public string Provider;
        }

// remoteNetworkPath format:  @"\\192.168.1.48\sharefolder"
        // localDriveName format:     @"E:"
        public static bool CreateMap(string userName, string password, string remoteNetworkPath, string localDriveName)
        {            
            NETRESOURCE myNetResource = new NETRESOURCE();
            myNetResource.dwScope = 2;       //2:RESOURCE_GLOBALNET
            myNetResource.dwType = 1;        //1:RESOURCETYPE_ANY
            myNetResource.dwDisplayType = 3; //3:RESOURCEDISPLAYTYPE_GENERIC
            myNetResource.dwUsage = 1;       //1: RESOURCEUSAGE_CONNECTABLE
            myNetResource.LocalName = localDriveName;
            myNetResource.RemoteName = remoteNetworkPath;
            myNetResource.Provider = null;

uint nret = WNetAddConnection2(myNetResource, password, userName, 0);

if (nret == 0)
                return true;
            else
                return false;
        }

// localDriveName format:     @"E:"
        public static bool DeleteMap(string localDriveName)
        {
            uint nret = WNetCancelConnection2(localDriveName, 1, true);

if (nret == 0)
                return true;
            else
                return false;
        }

public void test()
        {
            // 注意:
            // remote、local、username的格式一定要正确,否则可能出现错误
            string remote = @"\\192.168.1.48\generals";
            string local = @"P:";
            string username = @"Domain\UserName";
            string password = @"Password";
            bool ret = MyMap.CreateMap(username, password, remote, local);
            if (ret)
            {
                //do what you want:
                // ...
                //File.Copy("q:\\test.htm", "c:\\test.htm");

MyMap.DeleteMap(local);
            }
        }
    }
}

三、使用WebClient类

由于WebClient类可以上传下载文件,并且支持以http:、https:和file:开头的URI,所以可以用WebClient类来传输文件。
添加System.Net命名空间后使用如下代码下载文件:

private void Test1()
        {
            try
            {
                WebClient client = new WebClient();
                NetworkCredential cred = new NetworkCredential("username", "password", "172.16.0.222");
                client.Credentials = cred;
                client.DownloadFile("file://172.16.0.222/test/111.txt", "111.txt");
            }
            catch (Exception ex)
            {
                // 如果网络很慢,而文件又很大,这时可能有超时异常(Time out)。
            }
        }

public void Test2()
        {
            try
            {
                WebClient client = new WebClient();
                NetworkCredential cred = new NetworkCredential("username", "password", "domain");
                client.Credentials = cred;
                client.DownloadFile("file://172.16.0.222/test/111.txt", "111.txt");
            }
            catch (Exception ex)
            {
                // 如果网络很慢,而文件又很大,这时可能有超时异常(Time out)。
            }
        }

类似的还可以试试WebRequest、FileWebRequest等:

WebRequest req = WebRequest.Create("file://138.12.12.14/generals/test.htm");
                NetworkCredential cred = new NetworkCredential("username", "password", "IP");
                req.Credentials = cred;
                WebResponse response = req.GetResponse();
                Stream strm = response.GetResponseStream();
                StreamReader r = new StreamReader(strm);
                ... ...

四、角色模拟

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Security.Principal;
using System.IO;

namespace Test
{
    public class Test
    {
        // logon types
        const int LOGON32_LOGON_INTERACTIVE = 2;
        const int LOGON32_LOGON_NETWORK = 3;
        const int LOGON32_LOGON_NEW_CREDENTIALS = 9;
        // logon providers
        const int LOGON32_PROVIDER_DEFAULT = 0;
        const int LOGON32_PROVIDER_WINNT50 = 3;
        const int LOGON32_PROVIDER_WINNT40 = 2;
        const int LOGON32_PROVIDER_WINNT35 = 1;

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern int LogonUser(String lpszUserName,
            String lpszDomain,
            String lpszPassword,
            int dwLogonType,
            int dwLogonProvider,
            ref IntPtr phToken);

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern int DuplicateToken(IntPtr hToken,
            int impersonationLevel,
            ref IntPtr hNewToken);

[DllImport("advapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern bool RevertToSelf();

[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public static extern bool CloseHandle(IntPtr handle);

private WindowsImpersonationContext impersonationContext;

public bool impersonateValidUser(String userName, String domain, String password)
        {
            WindowsIdentity tempWindowsIdentity;
            IntPtr token = IntPtr.Zero;
            IntPtr tokenDuplicate = IntPtr.Zero;

if (RevertToSelf())
            {
                // 这里使用LOGON32_LOGON_NEW_CREDENTIALS来访问远程资源。
                // 如果要(通过模拟用户获得权限)实现服务器程序,访问本地授权数据库可
                // 以用LOGON32_LOGON_INTERACTIVE
                if (LogonUser(userName, domain, password, LOGON32_LOGON_NEW_CREDENTIALS,
                    LOGON32_PROVIDER_DEFAULT, ref token) != 0)
                {
                    if (DuplicateToken(token, 2, ref tokenDuplicate) != 0)
                    {
                        tempWindowsIdentity = new WindowsIdentity(tokenDuplicate);
                        impersonationContext = tempWindowsIdentity.Impersonate();
                        if (impersonationContext != null)
                        {
                            System.AppDomain.CurrentDomain.SetPrincipalPolicy(PrincipalPolicy.WindowsPrincipal);
                            IPrincipal pr = System.Threading.Thread.CurrentPrincipal;
                            IIdentity id = pr.Identity;
                            CloseHandle(token);
                            CloseHandle(tokenDuplicate);
                            return true;
                        }
                    }
                }
            }

if (token != IntPtr.Zero)
                CloseHandle(token);

if (tokenDuplicate != IntPtr.Zero)
                CloseHandle(tokenDuplicate);

return false;
        }

public void undoImpersonation()
        {
            impersonationContext.Undo();
        }

public void TestFunc()
        {
            bool isImpersonated = false;
            try
            {
                if (impersonateValidUser("UserName", "Domain", "Password"))
                {
                    isImpersonated = true;
                    //do what you want now, as the special user
                    // ...
                    File.Copy(@"\\192.168.1.48\generals\now.htm", "c:\\now.htm", true);
                }
            }
            finally
            {
                if (isImpersonated)
                    undoImpersonation();
            }
        }
    }
}

五、比较

方法一通过调用Shell命令Net Use实现,有点笨拙。
方法二和方法一有些相似之处。映射远程资源,然后访问。
方法三由于会有超时异常出现,所以在网络速度快、传输小文件时是可以的。
方法四通过身份模拟实现远程资源访问。一些服务器进程就是通过这种方式运行的。这种方法也是我的最爱。

六、要注意的地方

关于这几种方法,google后都可以找到一些文章。但是等到自己实际测试时,有时会出现各种小错误,
这些错误基本来源于两方面:

1、函数的参数选择有问题,和自己的环境不相符。
比如        
public static extern int LogonUser(String lpszUserName,
            String lpszDomain,
            String lpszPassword,
            int dwLogonType,
            int dwLogonProvider,
            ref IntPtr phToken);
中的dwLogonType,要访问远程资源就要用LOGON32_LOGON_NEW_CREDENTIALS,
要模拟本机用户就要用LOGON32_LOGON_INTERACTIVE。

2、函数的参数格式有问题。

a、比如
    public static extern int LogonUser(String lpszUserName,
            String lpszDomain,
            String lpszPassword,
            int dwLogonType,
            int dwLogonProvider,
            ref IntPtr phToken);
    中的lpszUserName、lpszDomain、lpszPassword就要写清楚。

我就在这遇到过问题,第一次测试时,远程服务器就是一台独立的文件服务器,这是我的调用方式:
        LogonUser("myname", "192.168.1.48", "password", LOGON32_LOGON_NEW_CREDENTIALS,
                    LOGON32_PROVIDER_DEFAULT, ref token);

第二次测试时,远程服务器是域MyDomain中的一个成员服务器,提供文件服务。这时代码就应该是:
        LogonUser("myname", "MyDomain", "password", LOGON32_LOGON_NEW_CREDENTIALS,
                    LOGON32_PROVIDER_DEFAULT, ref token);

注意,代码中是MyDomain而不是IP地址。

b、再如:

参考上面代码
            string remote = @"\\192.168.1.48\generals";
            string local = @"P:";
            string username = @"Domain\UserName";
            string password = @"Password";

如果@"\\192.168.1.48\generals"变成@"\\192.168.1.48\generals\”就会出错;
    如果是域中的用户,那么把@"Domain\UserName"变成@"UserName"就会出错。

C#访问远程主机资源的方法,多种方式的更多相关文章

  1. C#访问远程主机资源的方法

    实现访问远程主机的共享目录中的一个文件的解决方法: 一.调用Net use命令 // 使用方法:        //if (Connect("192.168.1.48", &quo ...

  2. SpringMVC访问静态资源的三种方式(转)

    本文转自:http://www.iigrowing.cn/springmvc_fang_wen_jing_tai_zi_yuan_de_san_zhong_fang_shi.html 如何你的Disp ...

  3. 转转转!SpringMVC访问静态资源的三种方式

    如果你的DispatcherServlet拦截 *.do这样的URL,就不存在访问不到静态资源的问题.如果你的DispatcherServlet拦截“/”,拦截了所有的请求,同时对*.js,*.jpg ...

  4. 当配置 DispatcherServlet拦截“/”,SpringMVC访问静态资源的三种方式

    如何你的DispatcherServlet拦截 *.do这样的URL,就不存在访问不到静态资源的问题.如果你的DispatcherServlet拦截“/”,拦截了所有的请求,同时对*.js,*.jpg ...

  5. 【转】SpringMVC访问静态资源的三种方式

    如何你的DispatcherServlet拦截 *.do这样的URL,就不存在访问不到静态资源的问题.如果你的DispatcherServlet拦截“/”,拦截了所有的请求,同时对*.js,*.jpg ...

  6. SpringMVC访问静态资源的三种方式

    如何你的DispatcherServlet拦截 *.do这样的URL,就不存在访问不到静态资源的问题.如果你的DispatcherServlet拦截“/”,拦截了所有的请求,同时对*.js,*.jpg ...

  7. springMVC访问静态资源的两种方式

    1<mvc:default-servlet-handler/> 2 <mvc:resources mapping="/images/**" location=&q ...

  8. Spring Boot整合Servlet,Filter,Listener,访问静态资源

    目录 Spring Boot整合Servlet(两种方式) 第一种方式(通过注解扫描方式完成Servlet组件的注册): 第二种方式(通过方法完成Servlet组件的注册) Springboot整合F ...

  9. [原创]java WEB学习笔记55:Struts2学习之路---详解struts2 中 Action,如何访问web 资源,解耦方式(使用 ActionContext,实现 XxxAware 接口),耦合方式(通过ServletActionContext,通过实现 ServletRequestAware, ServletContextAware 等接口的方式)

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

随机推荐

  1. Java内存模型-final域的内存语义

    一 引言 说到final你肯定知道它是Java中的关键字,那么它所在Java中的作用你知道吗?不知道的话,请前往这篇了解下https://www.cnblogs.com/yuanfy008/p/802 ...

  2. [转]OpenMP 入门指南

    简介 这门课作为 ECE 中少有的跟计算机科学相关的课,自然是必上不可.不过无论是 OpenMP 还是 CUDA,对于平时极少接触并行编程的我来说,都是十分吃力的,第一次作业的 OpenMP 编程已经 ...

  3. python之celery使用详解(二)

    前言 前面我们了解了celery的基本使用后,现在对其常用的对象和方法进行分析. Celery对象 核心的对象就是Celery了,初始化方法: class Celery(object): def __ ...

  4. 关于Mysql5.6半同步主从复制的开启方法【转】

    介绍 先了解一下mysql的主从复制是什么回事,我们都知道,mysql主从复制是基于binlog的复制方式,而mysql默认的主从复制方式,其实是异步复制. 主库实际上并不关心从库是否把数据拉完没有, ...

  5. 一个无锁消息队列引发的血案(六)——RingQueue(中) 休眠的艺术 [续]

    目录 (一)起因 (二)混合自旋锁 (三)q3.h 与 RingBuffer (四)RingQueue(上) 自旋锁 (五)RingQueue(中) 休眠的艺术 (六)RingQueue(中) 休眠的 ...

  6. 输出联系变化的数字seq

    主要作用:输出联系变化的数字格式:Seq 分割符号 开始 间隔 结束开始默认是1,间隔默认是1,分隔符默认回车一位是结束,两位首末,三位首间隔末,没有四位,开始可以是负数主要参数:-f 指定格式打印- ...

  7. Reactor模型-单线程版

    Reactor模型是典型的事件驱动模型.在网络编程中,所谓的事件当然就是read.write.bind.connect.close等这些动作了.Reactor模型的实现有很多种,下面介绍最基本的三种: ...

  8. CF1063A 【Oh Those Palindromes】

    考虑在一个部分串中加入字符使得最终构造的串回文子串最多的方案 考虑简单情况,对于只含一种元素的串,我们要插入其他元素 记原有元素为$a$,新加元素为$b$ 考虑$b$的最优插入位置 原串$aaaa.. ...

  9. HttpUrlConnection的setDoOutput与setDoInput的区别

    httpUrlConnection.setDoOutput(true) httpUrlConnection.setDoInput(true) 这两个方法在develope的httpUrlConnect ...

  10. SqlServer行转列(PIVOT),列转行(UNPIVOT)总结

    PIVOT用于将列值旋转为列名(即行转列) 语法: table_source PIVOT( 聚合函数(value_column) FOR pivot_column IN(<column_list ...