从具有凭据的远程不可信域访问共享文件(UNC)

我们遇到了一个有趣的情况需要解决,而我的搜索结果是零。因此,我呼吁社会福利署社区提供协助。

问题是: 我们需要通过编程方式访问一个不在我们的域中,也不在通过远程文件共享/UNC 的可信外部域中的共享文件。当然,我们需要向远程计算机提供凭据。

通常,解决这个问题的方法有两种:

  1. 将文件共享映射为驱动器并在此时提供凭据。这通常使用 NET USE命令或复制 NET USE的 Win32函数来完成。
  2. 使用 UNC 路径访问该文件,就像远程计算机在域上一样,并确保作为本地用户在远程计算机上复制运行程序的帐户(包括密码)。基本上,当用户试图访问共享文件时,Windows 将自动提供当前用户的凭据。
  3. 不要使用远程文件共享。使用 FTP (或其他一些方法)传输文件,在本地处理它,然后再传输回来。

由于各种各样的原因,我们的安全/网络架构师拒绝了前两种方法。第二种方法显然是一个安全漏洞; 如果远程计算机受到攻击,本地计算机现在就有危险。第一种方法不能令人满意,因为新挂载的驱动器是在程序访问文件期间本地计算机上其他程序可以使用的共享资源。尽管这很有可能是暂时的,但在他们看来,这仍然是一个漏洞。

他们对第三种选择持开放态度,但是远程网络管理员坚持使用 SFTP 而不是 FTPS,而且 FTpWebRequest 只支持 FTPS。SFTP 是更加友好的防火墙选项,我可以使用一些库来实现这种方法,但是如果可以的话,我更愿意减少依赖性。

我已经在 MSDN 中搜索了使用远程文件共享的托管方式或 win32方式,但是没有找到任何有用的东西。

所以我问: 还有别的办法吗?我是不是错过了一个超级机密的 win32函数?或者我必须采用选项3的某种变体?

219264 次浏览

大多数 SFTP 服务器也支持 SCP,这样可以更容易地为其找到库。您甚至可以从代码中调用现有的客户机,比如 腻子中包含的 pscp。

如果您正在处理的文件类型很简单,比如文本或 XML 文件,那么您甚至可以编写自己的客户机/服务器实现来使用类似于。NET Remoting 或 Web 服务。

AFAIK,您不需要 地图的 UNC 路径到驱动器号,以建立一个服务器的凭据。我经常使用批处理脚本,比如:

net use \\myserver /user:username password


:: do something with \\myserver\the\file\i\want.xml


net use /delete \\my.server.com

但是,任何与您的程序运行在同一帐户上的程序仍然能够访问 username:password所能访问的所有内容。一个可能的解决方案是将程序隔离在它自己的本地用户帐户中(UNC 访问是对称为 NET USE的帐户的本地访问)。

注: 跨域使用 SMB 技术并不是一个很好的技术应用,IMO。如果安全性如此重要,那么 SMB 缺乏加密这一事实本身就有点扫兴。

虽然我自己也不知道,但我当然希望 # 2是不正确的... ... 我希望 Windows 不会自动提供我的登录信息(至少不会提供我所有的密码!)任何机器,更不用说我不信任的机器了。

不管怎样,您研究过模拟体系结构吗:

using (System.Security.Principal.WindowsImpersonationContext context = System.Security.Principal.WindowsIdentity.Impersonate(token))
{
// Do network operations here


context.Undo();
}

在本例中,token变量是一个 IntPtr。为了获得此变量的值,必须调用非托管 LogonUserWindowsAPI 函数。快速浏览一下 Pinvoke.net,我们可以得到以下签名:

[System.Runtime.InteropServices.DllImport("advapi32.dll", SetLastError = true)]
public static extern bool LogonUser(
string lpszUsername,
string lpszDomain,
string lpszPassword,
int dwLogonType,
int dwLogonProvider,
out IntPtr phToken
);

用户名、域名和密码应该看起来相当明显。查看可以传递给 dwLogonType 和 dwLogonProvider 的各种值,以确定最适合您需要的值。

这段代码还没有经过测试,因为我在这里没有第二个可以验证的域,但是这应该可以帮助您找到正确的方向。

解决问题的方法是使用名为 WNetUseConnection的 Win32API。
使用此函数连接到具有身份验证的 UNC 路径,而不是映射驱动器

这将允许您连接到远程计算机,即使它不在同一个域上,即使它有不同的用户名和密码。

一旦您使用了 WNetUseConnection,您将能够通过 UNC 路径访问该文件,就像您在同一个域上一样。最好的办法可能是通过管理内置的股票。
示例: computer name c $program files Folder file.txt

下面是一些使用 WNetUseConnection 的示例 C # 代码。
注意,对于 NetResource,应该为 lpLocalName 和 lpProvider 传递 null。DwType 应该是 RESOURCETYPE _ DISK。LpRemoteName 应该是 ComputerName。

using System;
using System.Runtime.InteropServices ;
using System.Threading;


namespace ExtremeMirror
{
public class PinvokeWindowsNetworking
{
#region Consts
const int RESOURCE_CONNECTED = 0x00000001;
const int RESOURCE_GLOBALNET = 0x00000002;
const int RESOURCE_REMEMBERED = 0x00000003;


const int RESOURCETYPE_ANY = 0x00000000;
const int RESOURCETYPE_DISK = 0x00000001;
const int RESOURCETYPE_PRINT = 0x00000002;


const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000;
const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001;
const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002;
const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003;
const int RESOURCEDISPLAYTYPE_FILE = 0x00000004;
const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005;


const int RESOURCEUSAGE_CONNECTABLE = 0x00000001;
const int RESOURCEUSAGE_CONTAINER = 0x00000002;




const int CONNECT_INTERACTIVE = 0x00000008;
const int CONNECT_PROMPT = 0x00000010;
const int CONNECT_REDIRECT = 0x00000080;
const int CONNECT_UPDATE_PROFILE = 0x00000001;
const int CONNECT_COMMANDLINE = 0x00000800;
const int CONNECT_CMD_SAVECRED = 0x00001000;


const int CONNECT_LOCALDRIVE = 0x00000100;
#endregion


#region Errors
const int NO_ERROR = 0;


const int ERROR_ACCESS_DENIED = 5;
const int ERROR_ALREADY_ASSIGNED = 85;
const int ERROR_BAD_DEVICE = 1200;
const int ERROR_BAD_NET_NAME = 67;
const int ERROR_BAD_PROVIDER = 1204;
const int ERROR_CANCELLED = 1223;
const int ERROR_EXTENDED_ERROR = 1208;
const int ERROR_INVALID_ADDRESS = 487;
const int ERROR_INVALID_PARAMETER = 87;
const int ERROR_INVALID_PASSWORD = 1216;
const int ERROR_MORE_DATA = 234;
const int ERROR_NO_MORE_ITEMS = 259;
const int ERROR_NO_NET_OR_BAD_PATH = 1203;
const int ERROR_NO_NETWORK = 1222;


const int ERROR_BAD_PROFILE = 1206;
const int ERROR_CANNOT_OPEN_PROFILE = 1205;
const int ERROR_DEVICE_IN_USE = 2404;
const int ERROR_NOT_CONNECTED = 2250;
const int ERROR_OPEN_FILES  = 2401;


private struct ErrorClass
{
public int num;
public string message;
public ErrorClass(int num, string message)
{
this.num = num;
this.message = message;
}
}




// Created with excel formula:
// ="new ErrorClass("&A1&", """&PROPER(SUBSTITUTE(MID(A1,7,LEN(A1)-6), "_", " "))&"""), "
private static ErrorClass[] ERROR_LIST = new ErrorClass[] {
new ErrorClass(ERROR_ACCESS_DENIED, "Error: Access Denied"),
new ErrorClass(ERROR_ALREADY_ASSIGNED, "Error: Already Assigned"),
new ErrorClass(ERROR_BAD_DEVICE, "Error: Bad Device"),
new ErrorClass(ERROR_BAD_NET_NAME, "Error: Bad Net Name"),
new ErrorClass(ERROR_BAD_PROVIDER, "Error: Bad Provider"),
new ErrorClass(ERROR_CANCELLED, "Error: Cancelled"),
new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"),
new ErrorClass(ERROR_INVALID_ADDRESS, "Error: Invalid Address"),
new ErrorClass(ERROR_INVALID_PARAMETER, "Error: Invalid Parameter"),
new ErrorClass(ERROR_INVALID_PASSWORD, "Error: Invalid Password"),
new ErrorClass(ERROR_MORE_DATA, "Error: More Data"),
new ErrorClass(ERROR_NO_MORE_ITEMS, "Error: No More Items"),
new ErrorClass(ERROR_NO_NET_OR_BAD_PATH, "Error: No Net Or Bad Path"),
new ErrorClass(ERROR_NO_NETWORK, "Error: No Network"),
new ErrorClass(ERROR_BAD_PROFILE, "Error: Bad Profile"),
new ErrorClass(ERROR_CANNOT_OPEN_PROFILE, "Error: Cannot Open Profile"),
new ErrorClass(ERROR_DEVICE_IN_USE, "Error: Device In Use"),
new ErrorClass(ERROR_EXTENDED_ERROR, "Error: Extended Error"),
new ErrorClass(ERROR_NOT_CONNECTED, "Error: Not Connected"),
new ErrorClass(ERROR_OPEN_FILES, "Error: Open Files"),
};


private static string getErrorForNumber(int errNum)
{
foreach (ErrorClass er in ERROR_LIST)
{
if (er.num == errNum) return er.message;
}
return "Error: Unknown, " + errNum;
}
#endregion


[DllImport("Mpr.dll")] private static extern int WNetUseConnection(
IntPtr hwndOwner,
NETRESOURCE lpNetResource,
string lpPassword,
string lpUserID,
int dwFlags,
string lpAccessName,
string lpBufferSize,
string lpResult
);


[DllImport("Mpr.dll")] private static extern int WNetCancelConnection2(
string lpName,
int dwFlags,
bool fForce
);


[StructLayout(LayoutKind.Sequential)] private class NETRESOURCE
{
public int dwScope = 0;
public int dwType = 0;
public int dwDisplayType = 0;
public int dwUsage = 0;
public string lpLocalName = "";
public string lpRemoteName = "";
public string lpComment = "";
public string lpProvider = "";
}




public static string connectToRemote(string remoteUNC, string username, string password)
{
return connectToRemote(remoteUNC, username, password, false);
}


public static string connectToRemote(string remoteUNC, string username, string password, bool promptUser)
{
NETRESOURCE nr = new NETRESOURCE();
nr.dwType = RESOURCETYPE_DISK;
nr.lpRemoteName = remoteUNC;
//          nr.lpLocalName = "F:";


int ret;
if (promptUser)
ret = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null);
else
ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);


if (ret == NO_ERROR) return null;
return getErrorForNumber(ret);
}


public static string disconnectRemote(string remoteUNC)
{
int ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false);
if (ret == NO_ERROR) return null;
return getErrorForNumber(ret);
}
}
}

我建议使用 NetUseAdd而不是 WNetUseConnection。WNetUseConnection 是一个遗留的功能,已经被 WNetUseConnection2和 WNetUseConnection3所取代,但是所有这些功能都创建了一个可以在文件资源管理器中看到的网络设备。NetUseAdd 相当于在 DOS 提示符中调用 net use 以在远程计算机上进行身份验证。

如果调用 NetUseAdd,则后续访问该目录的尝试应该会成功。

对于寻找快速解决方案的人,您可以使用我最近写的 NetworkShareAccesser(基于 这个答案(非常感谢!)):

用法:

using (NetworkShareAccesser.Access(REMOTE_COMPUTER_NAME, DOMAIN, USER_NAME, PASSWORD))
{
File.Copy(@"C:\Some\File\To\copy.txt", @"\\REMOTE-COMPUTER\My\Shared\Target\file.txt");
}

警告: 请绝对确认,NetworkShareAccesserDispose被调用了(即使你的应用程序崩溃了!)否则,Windows 上将保留打开的连接。通过打开 cmd提示符并输入 net use,您可以看到所有打开的连接。

守则:

/// <summary>
/// Provides access to a network share.
/// </summary>
public class NetworkShareAccesser : IDisposable
{
private string _remoteUncName;
private string _remoteComputerName;


public string RemoteComputerName
{
get
{
return this._remoteComputerName;
}
set
{
this._remoteComputerName = value;
this._remoteUncName = @"\\" + this._remoteComputerName;
}
}


public string UserName
{
get;
set;
}
public string Password
{
get;
set;
}


#region Consts


private const int RESOURCE_CONNECTED = 0x00000001;
private const int RESOURCE_GLOBALNET = 0x00000002;
private const int RESOURCE_REMEMBERED = 0x00000003;


private const int RESOURCETYPE_ANY = 0x00000000;
private const int RESOURCETYPE_DISK = 0x00000001;
private const int RESOURCETYPE_PRINT = 0x00000002;


private const int RESOURCEDISPLAYTYPE_GENERIC = 0x00000000;
private const int RESOURCEDISPLAYTYPE_DOMAIN = 0x00000001;
private const int RESOURCEDISPLAYTYPE_SERVER = 0x00000002;
private const int RESOURCEDISPLAYTYPE_SHARE = 0x00000003;
private const int RESOURCEDISPLAYTYPE_FILE = 0x00000004;
private const int RESOURCEDISPLAYTYPE_GROUP = 0x00000005;


private const int RESOURCEUSAGE_CONNECTABLE = 0x00000001;
private const int RESOURCEUSAGE_CONTAINER = 0x00000002;




private const int CONNECT_INTERACTIVE = 0x00000008;
private const int CONNECT_PROMPT = 0x00000010;
private const int CONNECT_REDIRECT = 0x00000080;
private const int CONNECT_UPDATE_PROFILE = 0x00000001;
private const int CONNECT_COMMANDLINE = 0x00000800;
private const int CONNECT_CMD_SAVECRED = 0x00001000;


private const int CONNECT_LOCALDRIVE = 0x00000100;


#endregion


#region Errors


private const int NO_ERROR = 0;


private const int ERROR_ACCESS_DENIED = 5;
private const int ERROR_ALREADY_ASSIGNED = 85;
private const int ERROR_BAD_DEVICE = 1200;
private const int ERROR_BAD_NET_NAME = 67;
private const int ERROR_BAD_PROVIDER = 1204;
private const int ERROR_CANCELLED = 1223;
private const int ERROR_EXTENDED_ERROR = 1208;
private const int ERROR_INVALID_ADDRESS = 487;
private const int ERROR_INVALID_PARAMETER = 87;
private const int ERROR_INVALID_PASSWORD = 1216;
private const int ERROR_MORE_DATA = 234;
private const int ERROR_NO_MORE_ITEMS = 259;
private const int ERROR_NO_NET_OR_BAD_PATH = 1203;
private const int ERROR_NO_NETWORK = 1222;


private const int ERROR_BAD_PROFILE = 1206;
private const int ERROR_CANNOT_OPEN_PROFILE = 1205;
private const int ERROR_DEVICE_IN_USE = 2404;
private const int ERROR_NOT_CONNECTED = 2250;
private const int ERROR_OPEN_FILES = 2401;


#endregion


#region PInvoke Signatures


[DllImport("Mpr.dll")]
private static extern int WNetUseConnection(
IntPtr hwndOwner,
NETRESOURCE lpNetResource,
string lpPassword,
string lpUserID,
int dwFlags,
string lpAccessName,
string lpBufferSize,
string lpResult
);


[DllImport("Mpr.dll")]
private static extern int WNetCancelConnection2(
string lpName,
int dwFlags,
bool fForce
);


[StructLayout(LayoutKind.Sequential)]
private class NETRESOURCE
{
public int dwScope = 0;
public int dwType = 0;
public int dwDisplayType = 0;
public int dwUsage = 0;
public string lpLocalName = "";
public string lpRemoteName = "";
public string lpComment = "";
public string lpProvider = "";
}


#endregion


/// <summary>
/// Creates a NetworkShareAccesser for the given computer name. The user will be promted to enter credentials
/// </summary>
/// <param name="remoteComputerName"></param>
/// <returns></returns>
public static NetworkShareAccesser Access(string remoteComputerName)
{
return new NetworkShareAccesser(remoteComputerName);
}


/// <summary>
/// Creates a NetworkShareAccesser for the given computer name using the given domain/computer name, username and password
/// </summary>
/// <param name="remoteComputerName"></param>
/// <param name="domainOrComuterName"></param>
/// <param name="userName"></param>
/// <param name="password"></param>
public static NetworkShareAccesser Access(string remoteComputerName, string domainOrComuterName, string userName, string password)
{
return new NetworkShareAccesser(remoteComputerName,
domainOrComuterName + @"\" + userName,
password);
}


/// <summary>
/// Creates a NetworkShareAccesser for the given computer name using the given username (format: domainOrComputername\Username) and password
/// </summary>
/// <param name="remoteComputerName"></param>
/// <param name="userName"></param>
/// <param name="password"></param>
public static NetworkShareAccesser Access(string remoteComputerName, string userName, string password)
{
return new NetworkShareAccesser(remoteComputerName,
userName,
password);
}


private NetworkShareAccesser(string remoteComputerName)
{
RemoteComputerName = remoteComputerName;


this.ConnectToShare(this._remoteUncName, null, null, true);
}


private NetworkShareAccesser(string remoteComputerName, string userName, string password)
{
RemoteComputerName = remoteComputerName;
UserName = userName;
Password = password;


this.ConnectToShare(this._remoteUncName, this.UserName, this.Password, false);
}


private void ConnectToShare(string remoteUnc, string username, string password, bool promptUser)
{
NETRESOURCE nr = new NETRESOURCE
{
dwType = RESOURCETYPE_DISK,
lpRemoteName = remoteUnc
};


int result;
if (promptUser)
{
result = WNetUseConnection(IntPtr.Zero, nr, "", "", CONNECT_INTERACTIVE | CONNECT_PROMPT, null, null, null);
}
else
{
result = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);
}


if (result != NO_ERROR)
{
throw new Win32Exception(result);
}
}


private void DisconnectFromShare(string remoteUnc)
{
int result = WNetCancelConnection2(remoteUnc, CONNECT_UPDATE_PROFILE, false);
if (result != NO_ERROR)
{
throw new Win32Exception(result);
}
}


/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
/// </summary>
/// <filterpriority>2</filterpriority>
public void Dispose()
{
this.DisconnectFromShare(this._remoteUncName);
}
}

我向 MS 寻求答案。第一个解决方案假定运行应用程序进程的用户帐户可以访问共享文件夹或驱动器(同一域)。请确保解析了 DNS 或尝试使用 IP 地址。只需做以下几件事:

 DirectoryInfo di = new DirectoryInfo(PATH);
var files = di.EnumerateFiles("*.*", SearchOption.AllDirectories);

如果您希望跨不同的域. NET 2.0使用凭证,请遵循以下模式:

WebRequest req = FileWebRequest.Create(new Uri(@"\\<server Name>\Dir\test.txt"));


req.Credentials = new NetworkCredential(@"<Domain>\<User>", "<Password>");
req.PreAuthenticate = true;


WebResponse d = req.GetResponse();
FileStream fs = File.Create("test.txt");


// here you can check that the cast was successful if you want.
fs = d.GetResponseStream() as FileStream;
fs.Close();

在这里一个最小的 POC 类/所有的剥离

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;


public class UncShareWithCredentials : IDisposable
{
private string _uncShare;


public UncShareWithCredentials(string uncShare, string userName, string password)
{
var nr = new Native.NETRESOURCE
{
dwType = Native.RESOURCETYPE_DISK,
lpRemoteName = uncShare
};


int result = Native.WNetUseConnection(IntPtr.Zero, nr, password, userName, 0, null, null, null);
if (result != Native.NO_ERROR)
{
throw new Win32Exception(result);
}
_uncShare = uncShare;
}


public void Dispose()
{
if (!string.IsNullOrEmpty(_uncShare))
{
Native.WNetCancelConnection2(_uncShare, Native.CONNECT_UPDATE_PROFILE, false);
_uncShare = null;
}
}


private class Native
{
public const int RESOURCETYPE_DISK = 0x00000001;
public const int CONNECT_UPDATE_PROFILE = 0x00000001;
public const int NO_ERROR = 0;


[DllImport("mpr.dll")]
public static extern int WNetUseConnection(IntPtr hwndOwner, NETRESOURCE lpNetResource, string lpPassword, string lpUserID,
int dwFlags, string lpAccessName, string lpBufferSize, string lpResult);


[DllImport("mpr.dll")]
public static extern int WNetCancelConnection2(string lpName, int 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;
}
}
}

您可以直接使用 \\server\share\folderw/WNetUseConnection,无需事先将其剥离到 \\server部分。

我附上我的 网络代码基于 Brian的参考

Imports System.ComponentModel


Imports System.Runtime.InteropServices


Public Class PinvokeWindowsNetworking


Const NO_ERROR As Integer = 0






Private Structure ErrorClass


Public num As Integer


Public message As String






Public Sub New(ByVal num As Integer, ByVal message As String)


Me.num = num


Me.message = message


End Sub


End Structure






Private Shared ERROR_LIST As ErrorClass() = New ErrorClass() {


New ErrorClass(5, "Error: Access Denied"),


New ErrorClass(85, "Error: Already Assigned"),


New ErrorClass(1200, "Error: Bad Device"),


New ErrorClass(67, "Error: Bad Net Name"),


New ErrorClass(1204, "Error: Bad Provider"),


New ErrorClass(1223, "Error: Cancelled"),


New ErrorClass(1208, "Error: Extended Error"),


New ErrorClass(487, "Error: Invalid Address"),


New ErrorClass(87, "Error: Invalid Parameter"),


New ErrorClass(1216, "Error: Invalid Password"),


New ErrorClass(234, "Error: More Data"),


New ErrorClass(259, "Error: No More Items"),


New ErrorClass(1203, "Error: No Net Or Bad Path"),


New ErrorClass(1222, "Error: No Network"),


New ErrorClass(1206, "Error: Bad Profile"),


New ErrorClass(1205, "Error: Cannot Open Profile"),


New ErrorClass(2404, "Error: Device In Use"),


New ErrorClass(2250, "Error: Not Connected"),


New ErrorClass(2401, "Error: Open Files")}






Private Shared Function getErrorForNumber(ByVal errNum As Integer) As String


For Each er As ErrorClass In ERROR_LIST


If er.num = errNum Then Return er.message


Next






Try


Throw New Win32Exception(errNum)


Catch ex As Exception


Return "Error: Unknown, " & errNum & " " & ex.Message


End Try






Return "Error: Unknown, " & errNum


End Function






<DllImport("Mpr.dll")>


Private Shared Function WNetUseConnection(ByVal hwndOwner As IntPtr, ByVal lpNetResource As NETRESOURCE, ByVal lpPassword As String, ByVal lpUserID As String, ByVal dwFlags As Integer, ByVal lpAccessName As String, ByVal lpBufferSize As String, ByVal lpResult As String) As Integer


End Function






<DllImport("Mpr.dll")>


Private Shared Function WNetCancelConnection2(ByVal lpName As String, ByVal dwFlags As Integer, ByVal fForce As Boolean) As Integer


End Function






<StructLayout(LayoutKind.Sequential)>


Private Class NETRESOURCE


Public dwScope As Integer = 0


Public dwType As Integer = 0


Public dwDisplayType As Integer = 0


Public dwUsage As Integer = 0


Public lpLocalName As String = ""


Public lpRemoteName As String = ""


Public lpComment As String = ""


Public lpProvider As String = ""


End Class






Public Shared Function connectToRemote(ByVal remoteUNC As String, ByVal username As String, ByVal password As String) As String


Return connectToRemote(remoteUNC, username, password, False)


End Function






Public Shared Function connectToRemote(ByVal remoteUNC As String, ByVal username As String, ByVal password As String, ByVal promptUser As Boolean) As String


Dim nr As NETRESOURCE = New NETRESOURCE()


nr.dwType = ResourceTypes.Disk


nr.lpRemoteName = remoteUNC


Dim ret As Integer






If promptUser Then


ret = WNetUseConnection(IntPtr.Zero, nr, "", "", Connects.Interactive Or Connects.Prompt, Nothing, Nothing, Nothing)


Else


ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, Nothing, Nothing, Nothing)


End If






If ret = NO_ERROR Then Return Nothing


Return getErrorForNumber(ret)


End Function






Public Shared Function disconnectRemote(ByVal remoteUNC As String) As String


Dim ret As Integer = WNetCancelConnection2(remoteUNC, Connects.UpdateProfile, False)


If ret = NO_ERROR Then Return Nothing


Return getErrorForNumber(ret)


End Function




Enum Resources As Integer


Connected = &H1


GlobalNet = &H2


Remembered = &H3


End Enum




Enum ResourceTypes As Integer


Any = &H0


Disk = &H1


Print = &H2


End Enum




Enum ResourceDisplayTypes As Integer


Generic = &H0


Domain = &H1


Server = &H2


Share = &H3


File = &H4


Group = &H5


End Enum




Enum ResourceUsages As Integer


Connectable = &H1


Container = &H2


End Enum




Enum Connects As Integer


Interactive = &H8


Prompt = &H10


Redirect = &H80


UpdateProfile = &H1


CommandLine = &H800


CmdSaveCred = &H1000


LocalDrive = &H100


End Enum




End Class

怎么用

Dim login = PinvokeWindowsNetworking.connectToRemote("\\ComputerName", "ComputerName\UserName", "Password")


If IsNothing(login) Then






'do your thing on the shared folder






PinvokeWindowsNetworking.disconnectRemote("\\ComputerName")


End If