最近要实现访问远程主机的共享目录中的一个文件。遇到了权限问题。google了一下,找到了几种解决方法,记录如下:
一、调用net use命令
// 使用方法:
//if (Connect("192.168.1.48", "用户名", "密码"))
//{
// File.Copy(@"\192.168.1.48共享目录 est.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.48sharefolder" // 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.48generals"; string local = @"P:"; string username = @"DomainUserName"; 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);
... ...四、角色模拟
/// <summary>
/// 身份模拟实现远程资源访问
/// </summary>
public class FileImpersonation
{
// 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;
/// <summary>
/// 身份模拟
/// </summary>
/// <param name="userName">独立服务器用IP,域环境就用域名</param>
/// <param name="domain"></param>
/// <param name="password"></param>
/// <returns></returns>
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)
{
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;
}
/// <summary>
/// 取消模拟身份
/// </summary>
public void undoImpersonation()
{
impersonationContext.Undo();
}
/// <summary>
///
/// </summary>
/// <param name="domain">独立服务器用IP,域环境就用域名</param>
/// <param name="username"></param>
/// <param name="password"></param>
/// <param name="act">需要执行的操作</param>
public void ImpersonateFunc(string username, string domain, string password, Action act)
{
bool isImpersonated = false;
try
{
if (impersonateValidUser(username, domain, password))
{
isImpersonated = true;
//File.Copy(@"\192.168.1.48generals
ow.htm", "c:\now.htm", true);
act();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
finally
{
if (isImpersonated)
undoImpersonation();
}
}
//
}
FileImpersonation imp = new FileImpersonation();
imp.ImpersonateFunc("Administrator", "192.168.0.125", "xxxx", () =>
{
var source = new DirectoryInfo(@"D:HR");
string targetFolder = @"\192.168.0.125d$HR";
if (!Directory.Exists(targetFolder))
{
Directory.CreateDirectory(targetFolder);
}
foreach (FileInfo item in source.GetFiles())
{
string targertFile = Path.Combine(targetFolder, item.Name);
File.Copy(item.FullName, targertFile, true);
}
});五、比较
方法一通过调用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.48generals";
string local = @"P:";
string username = @"DomainUserName";
string password = @"Password"; 如果@"\192.168.1.48generals"变成@"\192.168.1.48generals”就会出错;
如果是域中的用户,那么把@"DomainUserName"变成@"UserName"就会出错。
以上就是C#访问远程主机资源的多种方法分享的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号