Vous êtes sur la page 1sur 18

How to present credentials in order to

open file?
up How do I specify the username and password in order for my program to open a file
vote6do for reading? The program that needs to access the file is running from an account
wn vote favorite
that does not have read access to the folder the file is in. Program is written in C#
2 and .NET 2, running under XP and file is on a Windows Server 2003 machine.

c# .net security filesystems

shareimprove this question asked Oct 30 '08 at 8:06


Will Sandenbergh
add a comment
3 Answers
activeoldest votes

up You want to impersonate a user who does have the rights to access the file.
vote11do
wn vote I recommend using a class like this
- http://www.codeproject.com/KB/cs/zetaimpersonator.aspx. It hides all the nasty
implementation of doing impersonation.
using (new Impersonator("myUsername", "myDomainname", "myPassword"))
{
string fileText = File.ReadAllText("c:\test.txt");
Console.WriteLine(fileText);
}
shareimprove this answer answered Oct 30 '08 at 8:41

James Newton-King
21.2k1378102
This works great. I found the file paths must be in UNC format if located on another machine. – Brian LeahyOct 30
at 11:12

1 Works for me too, took me a while get it to work because I missed the remark made on the referenced page:Please
note: The user context that initiates the impersonation (i.e. not the user context to which it is switched to) needs to h
the "Act as part of operating system" privilege set. – Lex Mar 7 '12 at 13:04

It's a shame that the codeproject's author never moved it into Nuget. My answer's commentary was only meant for f
for it is understood that you answered this 8 years ago before Nuget. – OmegaMan Feb 18 at 21:44
add a comment
up I have used the Nuget package NuGet Gallery | Simple Impersonation Library
vote1dow 1.1.0 but there are others; search on Impersonation for the others.
n vote Example usage using the interactive login to work with file structures:

using (Impersonation.LogonUser("{domain}",
"{UserName}",
"{Password}",
LogonType.Interactive))
{
var directory = @"\\MyCorpServer.net\alpha\cars";

Assert.IsTrue(Directory.Exists(directory));
}

This is kind of ironic that the person who has the most downloaded package on
Nuget does not even mention it. ;-)
shareimprove this answer edited Feb 19 at 22:07 answered Feb 18 at 21:41

OmegaMan
11.5k32442
Simple, thanks a lot! – Andrew Kilburn Feb 25 at 16:44
add a comment
up You can impersonate a user who has the necessary rights. There is an article on
vote0do MSDN that describes how to do this.
wn vote shareimprove this answer answered Oct 30 '08 at 8:21

http://stackoverflow.com/questions/249540/how-to-present-credentials-in-order-to-open-file

How to provide user name and password


when connecting to a network share
up When connecting to a network share for which the current user (in my case, a
vote91d network enabled service user) has no rights, name and password have to be
own vote favorite
provided.

53 I know how to do this with Win32 functions (the WNet* family from mpr.dll), but
would like to do it with .Net (2.0) functionality.
What options are available?

Maybe some more information helps:


 The use case is a windows service, not an Asp.Net application.

 The service is running under an account which has no rights on the share.
 The user account needed for the share is not known on the client side.
 Client and server are not members of the same domain.
c# .net winapi networking passwords

shareimprove this question edited Nov 17 '08 at 13:21 asked Nov 17 '08 at 13:07

gyrolf

1,19031619

4 While I'm not giving you a useful answer, I can supply an anti-answer.. Impersonation and spawning a process as
Marc posited will not work when the server and the client are not in the same domain, unless there is a trust
between the two domains. If there is a trust then I think it will work. I would have just replied as a comment to Ma
but I don't have enough rep to comment. :-/ – Moose Nov 17 '08 at 14:23

1 @Moose oh now you do. – AgentFire Dec 1 '14 at 15:01

add a comment

11 Answers
activeoldest votes

up You can either change the thread identity, or P/Invoke WNetAddConnection2. I


vote94do prefer the latter, as I sometimes need to maintain multiple credentials for different
wn vote a cce pted
locations. I wrap it into an IDisposable and call WNetCancelConnection2 to
remove the creds afterwards (avoiding the multiple usernames error):

using (new NetworkConnection(@"\\server\read", readCredentials))


using (new NetworkConnection(@"\\server2\write", writeCredentials)) {
File.Copy(@"\\server\read\file", @"\\server2\write\file");
}
shareimprove this answer answered Nov 17 '08 at 14:39

Mark Brackett

61.8k1075124

2 The service isn't member of the target domain - impersonation cannot work since you wouldn't be able to create
security token locally and impersonate with it. PInvoke is the only way. – stephbu Nov 17 '08 at 17:31
any sample with full source code ? thx – Kiquenet Mar 13 '12 at 12:12

3 @Kiquenet - see Luke's answer for source: stackoverflow.com/a/1197430/2199 – Mark Brackett Mar 13 '12 at 13

@MarkBrackett I know this is an old answer, but maybe you still know... will the access be granted to the progra
only or also to the logged in user via explorer? – Breeze Mar 21 at 10:06

@Breeze - I haven't tested it, but I'd expect it authenticate for the logon session; so if your program is running as
logged on user, they'd have access as well (at least for the duration of the operation). – Mark Brackett Mar 21 at
15:45

show 1 more comment

up I liked Mark Brackett's answer so much that I did my own quick implementation.
vote183d Here it is if anyone else needs it in a hurry:
own vote public class NetworkConnection : IDisposable
{
string _networkName;

public NetworkConnection(string networkName,


NetworkCredential credentials)
{
_networkName = networkName;

var netResource = new NetResource()


{
Scope = ResourceScope.GlobalNetwork,
ResourceType = ResourceType.Disk,
DisplayType = ResourceDisplaytype.Share,
RemoteName = networkName
};

var userName = string.IsNullOrEmpty(credentials.Domain)


? credentials.UserName
: string.Format(@"{0}\{1}", credentials.Domain,
credentials.UserName);

var result = WNetAddConnection2(


netResource,
credentials.Password,
userName,
0);

if (result != 0)
{
throw new Win32Exception(result, "Error connecting to
remote share");
}
}

~NetworkConnection()
{
Dispose(false);
}

public void Dispose()


{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool disposing)


{
WNetCancelConnection2(_networkName, 0, true);
}

[DllImport("mpr.dll")]
private static extern int WNetAddConnection2(NetResource
netResource,
string password, string username, int flags);

[DllImport("mpr.dll")]
private static extern int WNetCancelConnection2(string name, int
flags,
bool force);
}

[StructLayout(LayoutKind.Sequential)]
public class NetResource
{
public ResourceScope Scope;
public ResourceType ResourceType;
public ResourceDisplaytype DisplayType;
public int Usage;
public string LocalName;
public string RemoteName;
public string Comment;
public string Provider;
}

public enum ResourceScope : int


{
Connected = 1,
GlobalNetwork,
Remembered,
Recent,
Context
};

public enum ResourceType : int


{
Any = 0,
Disk = 1,
Print = 2,
Reserved = 8,
}
public enum ResourceDisplaytype : int
{
Generic = 0x0,
Domain = 0x01,
Server = 0x02,
Share = 0x03,
File = 0x04,
Group = 0x05,
Network = 0x06,
Root = 0x07,
Shareadmin = 0x08,
Directory = 0x09,
Tree = 0x0a,
Ndscontainer = 0x0b
}
shareimprove this answer edited Aug 14 '12 at 23:15 answered Jul 29 '09 at 0:10

Luke Quinane

10.7k74778

Thanks Luke it is greate. it helped me +1 – AEMLoviji Feb 10 '11 at 14:37

7 It really should be throw new Win32Exception(result);, since WNetAddConnection2 returns win32 erro
codes (ERROR_XXX) – torvin May 16 '11 at 8:22

1 @AngryHacker Thanks, I've updated the code to add that in. – Luke Quinane Aug 14 '12 at 23:16

1 +1 This is really awesome. I 'was in a hurry' and quickly used it with great results! Will be refactoring this and mo
it to our company framework soon. – julealgon Jul 4 '13 at 19:55

1 This is a brilliant little piece of code. Needed to logon to a UNIX system to get a directory listing for printing to an
MVC5 web application and this did the trick. +1!!! – Tay Jan 27 '14 at 9:07

show 9 more comments

up I searched lots of methods and i did it my own way. You have to open a connection
vote16d between two machine via command prompt NET USE command and after finishing
own vote your work clear the connection with command prompt NET USE "myconnection"
/delete.

You must use Command Prompt process from code behind like this:

var savePath = @"\\servername\foldername\myfilename.jpg";


var filePath = @"C:\\temp\myfileTosave.jpg";
Usage is simple:

SaveACopyfileToServer(filePath, savePath);
Here is functions:

using System.IO
using System.Diagnostics;

public static void SaveACopyfileToServer(string filePath, string


savePath)
{
var directory = Path.GetDirectoryName(savePath).Trim();
var username = "loginusername";
var password = "loginpassword";
var filenameToSave = Path.GetFileName(savePath);

if (!directory.EndsWith("\\"))
filenameToSave = "\\" + filenameToSave;

var command = "NET USE " + directory + " /delete";


ExecuteCommand(command, 5000);

command = "NET USE " + directory + " /user:" + username + " "
+ password;
ExecuteCommand(command, 5000);

command = " copy \"" + filePath + "\" \"" + directory +


filenameToSave + "\"";

ExecuteCommand(command, 5000);

command = "NET USE " + directory + " /delete";


ExecuteCommand(command, 5000);
}
And also ExecuteCommand function is:

public static int ExecuteCommand(string command, int timeout)


{
var processInfo = new ProcessStartInfo("cmd.exe", "/C " +
command)
{
CreateNoWindow = true,
UseShellExecute = false,
WorkingDirectory = "C:\\",
};

var process = Process.Start(processInfo);


process.WaitForExit(timeout);
var exitCode = process.ExitCode;
process.Close();
return exitCode;
}
This functions worked very fast and stable for me.

shareimprove this answer edited Jul 17 '13 at 13:09 answered Nov 25 '11 at 16:20

Hakan KOSE

463513

+1 this solution worked great for me - i needed to copy a remote file on a network share to my local machine. The
network share required a user and pass. – Simon Jul 10 '12 at 7:37

Actually i jumped the gun, it does work, however it leaves cmd.exe processes running!! Couldnt shut down at the
of the day. I tried wrapping it up in using blocks but still didnt work :( – Simon Jul 11 '12 at 2:58

I updated the code now. It works better now. – Hakan KOSE Jul 12 '12 at 10:49

Would be better if people included namespaces with their code... – Liquid Core Jul 15 '13 at 16:37

I added namespaces to code, thanks. – Hakan KOSE Jul 17 '13 at 13:10

add a comment

up OK... I can resond..


vote3dow
n vote Disclaimer: I just had an 18+ hour day (again).. I'm old and forgetfull.. I can't spell..
I have a short attention span so I better respond fast.. :-)

Question:

Is it possible to change the thread principal to an user with no account on the local
machine?

Answer:

Yes, you can change a thread principal even if the credentials you are using are not
defined locally or are outside the "forest".

I just ran into this problem when trying to connect to an SQL server with NTLM
authentication from a service. This call uses the credentials associated with the
process meaning that you need either a local account or a domain account to
authenticate before you can impersonate. Blah, blah...

But...
Calling LogonUser(..) with the attribute of ????_NEW_CREDENTIALS will return
a security token without trying to authenticate the credentials. Kewl.. Don't have to
define the account within the "forest". Once you have the token you might have to
call DuplicateToken() with the option to enable impersonation resulting in a new
token. Now call SetThreadToken( NULL, token ); (It might be &token?).. A call to
ImpersonateLoggedonUser( token ); might be required, but I don't think so. Look it
up..

Do what you need to do..

Call RevertToSelf() if you called ImpersonateLoggedonUser() then


SetThreadToken( NULL, NULL ); (I think... look it up), and then CloseHandle() on
the created handles..

No promises but this worked for me... This is off the top of my head (like my hair)
and I can't spell!!!

shareimprove this answer answered Jan 30 '10 at 8:12

Craig Armstrong

311

add a comment

up The Luke Quinane solution looks good, but did work only partially in my ASP.NET
vote3do MVC application. Having two shares on the same server with different credentials I
wn vote could use the impersonation only for the first one.

The problem with WNetAddConnection2 is also that it behaves differently on


different windows versions. That is why I looked for alternatives and found
the LogonUser function. Here is my code which also works in ASP.NET:
public sealed class WrappedImpersonationContext
{
public enum LogonType : int
{
Interactive = 2,
Network = 3,
Batch = 4,
Service = 5,
Unlock = 7,
NetworkClearText = 8,
NewCredentials = 9
}

public enum LogonProvider : int


{
Default = 0, // LOGON32_PROVIDER_DEFAULT
WinNT35 = 1,
WinNT40 = 2, // Use the NTLM logon provider.
WinNT50 = 3 // Use the negotiate logon provider.
}

[DllImport("advapi32.dll", EntryPoint = "LogonUserW", SetLastError


= true, CharSet = CharSet.Unicode)]
public static extern bool LogonUser(String lpszUsername, String
lpszDomain,
String lpszPassword, LogonType dwLogonType, LogonProvider
dwLogonProvider, ref IntPtr phToken);

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

private string _domain, _password, _username;


private IntPtr _token;
private WindowsImpersonationContext _context;

private bool IsInContext


{
get { return _context != null; }
}

public WrappedImpersonationContext(string domain, string username,


string password)
{
_domain = String.IsNullOrEmpty(domain) ? "." : domain;
_username = username;
_password = password;
}

// Changes the Windows identity of this thread. Make sure to always


call Leave() at the end.
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Enter()
{
if (IsInContext)
return;

_token = IntPtr.Zero;
bool logonSuccessfull = LogonUser(_username, _domain,
_password, LogonType.NewCredentials, LogonProvider.WinNT50, ref
_token);
if (!logonSuccessfull)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
WindowsIdentity identity = new WindowsIdentity(_token);
_context = identity.Impersonate();

Debug.WriteLine(WindowsIdentity.GetCurrent().Name);
}
[PermissionSetAttribute(SecurityAction.Demand, Name = "FullTrust")]
public void Leave()
{
if (!IsInContext)
return;

_context.Undo();

if (_token != IntPtr.Zero)
{
CloseHandle(_token);
}
_context = null;
}
}
Usage:
var impersonationContext = new WrappedImpersonationContext(Domain,
Username, Password);
impersonationContext.Enter();

//do your stuff here

impersonationContext.Leave();
shareimprove this answer edited Mar 18 '14 at 14:49 answered Mar 18 '14 at 13:55

VladL

7,75693054

this approach worked well for me, but noticed in my testing that when using a bad password with a domain user
account, that user is immediately throw into locked status. our domain policy calls for a 3 failed login attempts befo
that happens, but via this approach one bad attempt and you're locked. So, use with caution... – kellyb Oct 2 '15 at
17:46

add a comment

up One option that might work is using WindowsIdentity.Impersonate (and change


vote2do the thread principal) to become the desired user, like so. Back to p/invoke, though,
wn vote I'm afraid...
Another cheeky (and equally far from ideal) option might be to spawn a process to
do the work... ProcessStartInfo accepts a .UserName, .Password and .Domain.
Finally - perhaps run the service in a dedicated account that has access? (removed as
you have clarified that this isn't an option).
shareimprove this answer edited Nov 17 '08 at 13:23 answered Nov 17 '08 at 13:18
Marc Gravell♦

608k13917032175

i don't think the process thing is such a bad idea. google put out some whitepapers about the benefits of
multiprocessing in chrome. – Dustin Getz Nov 17 '08 at 13:26

Is it possible to change the thread principal to an user with no account on the local machine? – gyrolf Nov 17 '08 at
13:52

To be honest, I simply don't know... You'd have to try LogonUser with a different domain to find out. – Marc
Gravell♦ Nov 17 '08 at 14:00

add a comment

up You should be looking at adding a like like this:


vote1do
<identity impersonate="true" userName="domain\user" password="****" />
wn vote
Into your web.config.

More Information.
shareimprove this answer answered Nov 17 '08 at 13:12

GEOCHET

15.9k155883

add a comment

up If you can't create an locally valid security token, it seems like you've ruled all out
vote1do every option bar Win32 API and WNetAddConnection*.
wn vote
Tons of information on MSDN about WNet - PInvoke information and sample code
that connects to a UNC path here:

http://www.pinvoke.net/default.aspx/mpr/WNetAddConnection2.html#
MSDN Reference here:

http://msdn.microsoft.com/en-us/library/aa385391(VS.85).aspx
shareimprove this answer answered Nov 17 '08 at 14:35
stephbu

4,6291838

add a comment

up May not be possible with .NET 2.0, but certainly with .NET 4.5 (maybe with 3.0 and
vote1do 4.0 too). Refer tohttp://stackoverflow.com/a/22378883/828062 for answer.
shareimprove this answer answered Mar 13 '14 at 12:40
wn vote

zendu

11113

add a comment

up Also ported to F# to use with FAKE


vote1do module NetworkShare
wn vote
open System
open System.ComponentModel
open System.IO
open System.Net
open System.Runtime.InteropServices

type ResourceScope =
| Connected = 1
| GlobalNetwork = 2
| Remembered = 3
| Recent = 4
type ResourceType =
| Any = 0
| Disk = 1
| Print = 2
| Reserved = 8
type ResourceDisplayType =
| Generic = 0x0
| Domain = 0x01
| Server = 0x02
| Share = 0x03
| File = 0x04
| Group = 0x05
| Network = 0x06
| Root = 0x07
| Shareadmin = 0x08
| Directory = 0x09
| Tree = 0x0a
| Ndscontainer = 0x0b

//Uses of this construct may result in the generation of unverifiable


.NET IL code.
#nowarn "9"
[<StructLayout(LayoutKind.Sequential)>]
type NetResource =
struct
val mutable Scope : ResourceScope
val mutable ResourceType : ResourceType
val mutable DisplayType : ResourceDisplayType
val mutable Usage : int
val mutable LocalName : string
val mutable RemoteName : string
val mutable Comment : string
val mutable Provider : string
new(name) = {
// lets preset needed fields
NetResource.Scope = ResourceScope.GlobalNetwork
ResourceType = ResourceType.Disk
DisplayType = ResourceDisplayType.Share
Usage = 0
LocalName = null
RemoteName = name
Comment = null
Provider = null
}
end

type WNetConnection(networkName : string, credential :


NetworkCredential) =
[<Literal>]
static let Mpr = "mpr.dll"
[<DllImport(Mpr, EntryPoint = "WNetAddConnection2")>]
static extern int connect(NetResource netResource, string password,
string username, int flags)
[<DllImport(Mpr, EntryPoint = "WNetCancelConnection2")>]
static extern int disconnect(string name, int flags, bool force)

let mutable disposed = false;

do
let userName = if String.IsNullOrWhiteSpace credential.Domain
then credential.UserName
else credential.Domain + "\\" + credential.UserName
let resource = new NetResource(networkName)

let result = connect(resource, credential.Password, userName, 0)

if result <> 0 then


let msg = "Error connecting to remote share " + networkName
new Win32Exception(result, msg)
|> raise

let cleanup(disposing:bool) =
if not disposed then
disposed <- true
if disposing then () // TODO dispose managed resources here
disconnect(networkName, 0, true) |> ignore

interface IDisposable with


member __.Dispose() =
disconnect(networkName, 0, true) |> ignore
GC.SuppressFinalize(__)

override __.Finalize() = cleanup(false)

type CopyPath =
| RemotePath of string * NetworkCredential
| LocalPath of string

let createDisposable() =
{
new IDisposable with
member __.Dispose() = ()
}

let copyFile overwrite destPath srcPath : unit =


use _srcConn =
match srcPath with
| RemotePath(path, credential) -> new WNetConnection(path,
credential) :> IDisposable
| LocalPath(_) -> createDisposable()
use _destConn =
match destPath with
| RemotePath(path, credential) -> new WNetConnection(path,
credential) :> IDisposable
| LocalPath(_) -> createDisposable()
match srcPath, destPath with
| RemotePath(src, _), RemotePath(dest, _)
| LocalPath(src), RemotePath(dest, _)
| RemotePath(src, _), LocalPath(dest)
| LocalPath(src), LocalPath(dest) ->
if FileInfo(src).Exists |> not then
failwith ("Source file not found: " + src)
let destFilePath =
if DirectoryInfo(dest).Exists then Path.Combine(dest,
Path.GetFileName src)
else dest
File.Copy(src, destFilePath, overwrite)

let rec copyDir copySubDirs filePattern destPath srcPath =


use _srcConn =
match srcPath with
| RemotePath(path, credential) -> new WNetConnection(path,
credential) :> IDisposable
| LocalPath(_) -> createDisposable()
use _destConn =
match destPath with
| RemotePath(path, credential) -> new WNetConnection(path,
credential) :> IDisposable
| LocalPath(_) -> createDisposable()
match srcPath, destPath with
| RemotePath(src, _), RemotePath(dest, _)
| LocalPath(src), RemotePath(dest, _)
| RemotePath(src, _), LocalPath(dest)
| LocalPath(src), LocalPath(dest) ->
let dir = DirectoryInfo(src)
if dir.Exists |> not then
failwith ("Source directory not found: " + src)

let dirs = dir.GetDirectories()


if Directory.Exists(dest) |> not then
Directory.CreateDirectory(dest) |> ignore

let files = dir.GetFiles(filePattern)


for file in files do
let tempPath = Path.Combine(dest, file.Name)
file.CopyTo(tempPath, false) |> ignore

if copySubDirs then
for subdir in dirs do
let subdirSrc =
match srcPath with
| RemotePath(_, credential) -> RemotePath(Path.Combine(dest,
subdir.Name), credential)
| LocalPath(_) -> LocalPath(Path.Combine(dest, subdir.Name))
let subdirDest =
match destPath with
| RemotePath(_, credential) -> RemotePath(subdir.FullName,
credential)
| LocalPath(_) -> LocalPath(subdir.FullName)
copyDir copySubDirs filePattern subdirDest subdirSrc
shareimprove this answer answered Nov 10 '15 at 16:22

python_kaa

439312

add a comment

up For VB.lovers the VB.NET equivalent of Luke Quinane's code (thanks Luke!)
vote0do
Imports System
wn vote
Imports System.Net
Imports System.Runtime.InteropServices
Imports System.ComponentModel

Public Class NetworkConnection


Implements IDisposable

Private _networkName As String

Public Sub New(networkName As String, credentials As


NetworkCredential)
_networkName = networkName

Dim netResource = New NetResource() With {


.Scope = ResourceScope.GlobalNetwork,
.ResourceType = ResourceType.Disk,
.DisplayType = ResourceDisplaytype.Share,
.RemoteName = networkName
}

Dim userName = If(String.IsNullOrEmpty(credentials.Domain),


credentials.UserName, String.Format("{0}\{1}", credentials.Domain,
credentials.UserName))

Dim result = WNetAddConnection2(NetResource,


credentials.Password, userName, 0)

If result <> 0 Then


Throw New Win32Exception(result, "Error connecting to
remote share")
End If
End Sub

Protected Overrides Sub Finalize()


Try
Dispose (False)
Finally
MyBase.Finalize()
End Try
End Sub

Public Sub Dispose() Implements IDisposable.Dispose


Dispose (True)
GC.SuppressFinalize (Me)
End Sub

Protected Overridable Sub Dispose(disposing As Boolean)


WNetCancelConnection2(_networkName, 0, True)
End Sub

<DllImport("mpr.dll")> _
Private Shared Function WNetAddConnection2(netResource As
NetResource, password As String, username As String, flags As Integer)
As Integer
End Function
<DllImport("mpr.dll")> _
Private Shared Function WNetCancelConnection2(name As String, flags
As Integer, force As Boolean) As Integer
End Function

End Class

<StructLayout(LayoutKind.Sequential)> _
Public Class NetResource
Public Scope As ResourceScope
Public ResourceType As ResourceType
Public DisplayType As ResourceDisplaytype
Public Usage As Integer
Public LocalName As String
Public RemoteName As String
Public Comment As String
Public Provider As String
End Class

Public Enum ResourceScope As Integer


Connected = 1
GlobalNetwork
Remembered
Recent
Context
End Enum

Public Enum ResourceType As Integer


Any = 0
Disk = 1
Print = 2
Reserved = 8
End Enum

Public Enum ResourceDisplaytype As Integer


Generic = &H0
Domain = &H1
Server = &H2
Share = &H3
File = &H4
Group = &H5
Network = &H6
Root = &H7
Shareadmin = &H8
Directory = &H9
Tree = &HA
Ndscontainer = &HB
End Enum
shareimprove this answer answered Nov 5 '15 at 15:42

http://stackoverflow.com/questions/295538/how-to-provide-user-name-and-password-when-
connecting-to-a-network-share

Vous aimerez peut-être aussi