I want to copy a file from computer A(with account myAccount#mydomain) to computer B(userB#computerB) over the network using c#.
I tried the standard
File.Copy(source,destination)
and tried starting a cmd process (from computer A) and call the copy method
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.UseShellExecute = false;
startInfo.Domain = "computerB"; //ofcourse it wont work since its outside the local domain of A
startInfo.FileName = "cmd.exe";
startInfo.Arguments = #"/C COPY \\computerA\Path\File1.txt \\computerB\Path$ ";
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
//It will exit the user name or password is incorrect
I tried also to use PSexec to impersonate computerB :
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new
System.Diagnostics.ProcessStartInfo();
startInfo.UseShellExecute = false;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = #"psexec \\computerB -u computerB\userB -p userBPassword cmd /c COPY \\computerA\Path\File1.txt \\computerB\Path$";
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
//it will exit that the source file is unknown
To sum it up,computer A is able to see the source(itself) but not the destination(since computer B has only authorized local user).
computer B is able to see the destination(itself) but not the source(since computer A is outside its domain and its not shared over the network).
Is there a workaround for this issue?
It sounds like this is a fairly simple authentication problem of the type that pops up whenever the current user doesn't have rights on a share, both in a domain and out. The problem also arises when running under the system user and trying to access shares on other devices.
The solution is to open an authenticated connection to the target device using the WNetUseConnection API, perform your file operations then close the connection with a call to WNetCancelConnection2.
Here's some code I have used in the past for this:
class ConnectSMB : IDisposable
{
public string URI { get; private set; }
public bool Connected { get; private set; } = false;
public ConnectSMB(string uri, string username, string encPassword)
{
string pass = StringEncryption.Decode(encPassword);
string connResult = Native.ConnectToRemote(uri, username, pass);
if (connResult != null)
throw new Exception(connResult);
URI = uri;
Connected = true;
}
public void Dispose()
{
Close();
}
public void Close()
{
if (Connected)
{
Native.DisconnectRemote(URI);
URI = null;
Connected = false;
}
}
}
public class Native
{
#region Consts
const int RESOURCETYPE_DISK = 1;
const int CONNECT_UPDATE_PROFILE = 0x00000001;
#endregion
#region Errors
public enum ENetUseError
{
NoError = 0,
AccessDenied = 5,
AlreadyAssigned = 85,
BadDevice = 1200,
BadNetName = 67,
BadProvider = 1204,
Cancelled = 1223,
ExtendedError = 1208,
InvalidAddress = 487,
InvalidParameter = 87,
InvalidPassword = 1216,
MoreData = 234,
NoMoreItems = 259,
NoNetOrBadPath = 1203,
NoNetwork = 1222,
BadProfile = 1206,
CannotOpenProfile = 1205,
DeviceInUse = 2404,
NotConnected = 2250,
OpenFiles = 2401
}
#endregion
#region API methods
[DllImport("Mpr.dll")]
private static extern ENetUseError WNetUseConnection(
IntPtr hwndOwner,
NETRESOURCE lpNetResource,
string lpPassword,
string lpUserID,
int dwFlags,
string lpAccessName,
string lpBufferSize,
string lpResult
);
[DllImport("Mpr.dll")]
private static extern ENetUseError WNetCancelConnection2(
string lpName,
int dwFlags,
bool fForce
);
[StructLayout(LayoutKind.Sequential)]
private class NETRESOURCE
{
// Not used
public int dwScope = 0;
// Resource Type - disk or printer
public int dwType = RESOURCETYPE_DISK;
// Not used
public int dwDisplayType = 0;
// Not used
public int dwUsage = 0;
// Local Name - name of local device (optional, not used here)
public string lpLocalName = "";
// Remote Name - full path to remote share
public string lpRemoteName = "";
// Not used
public string lpComment = "";
// Not used
public string lpProvider = "";
}
#endregion
public static string ConnectToRemote(string remoteUNC, string username, string password)
{
NETRESOURCE nr = new NETRESOURCE
{
lpRemoteName = remoteUNC
};
ENetUseError ret = WNetUseConnection(IntPtr.Zero, nr, password, username, 0, null, null, null);
if (ret == ENetUseError.NoError) return null;
return ret.ToString();
}
public static string DisconnectRemote(string remoteUNC)
{
ENetUseError ret = WNetCancelConnection2(remoteUNC, CONNECT_UPDATE_PROFILE, false);
if (ret == ENetUseError.NoError) return null;
return ret.ToString();
}
}
Create an instance of the ConnectSMB class before you do your remote file operations, then dispose (or close) it when you're done with the connection.
using (var smb = new ConnectSMB(#"\\computerB\Path", "userB", "userBPassword"))
{
File.Copy(source, destination);
}
Related
I have a C# api hosted on domain A. This api receives files as form-data (I extract the file using HttpContext.Current.Request.Files). In this api, I need to implement a method to save the extracted file to a folder or shared folder in a remote machine that is in another domain say domain B (My company domain). I have the access credentials of the machine running in domain B.
I am using VS2015 for development. My target framework is .NET 4.5.2.
I have tried using WNetUseConnection(Mpr.dll) method to connect. Using this method, I am able to successfully connect and save the files when connecting from the same domain as the remote machine. But when i tried to connect from outside domain B(the remote machine's domain), I am unable to do so. I am getting error 53. I researched about this error and found that it is "network path was not found".
I feel like I'll need all the help I can get. Need to get this done in 2 days.
Given below is my function call to connect to the remote machine in domain B.
RemoteConnect.connectToRemote("\\xxx.xxx.xxx.xxx\C$", #"domain-B\username", "password");
I have given below the code I have in my connection class (RemoteConnect.cs) for using WNetUseConnection.
[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);
}
Have you tried NetworkCredential? Maybe I'm thinking too simple and am not understanding your problem..
This has always worked for my needs:
NetworkCredential networkCredential = new NetworkCredential("username", "password", "domainname");
CredentialCache credentialCache = new CredentialCache();
credentialCache.Add(new Uri(#"\\networkshare\"), "Basic", networkCredential);
//Proceed with whatever file-io you need using the normal .NET file io. Example:
string[] folders = System.IO.Directory.GetDirectories(#"\\networkshare\Users\Userimages");
I start a Process by
Process app = new Process();
app.StartInfo.UseShellExecute = false;
app.StartInfo.FileName = path;
app.StartInfo.Domain = "Domain";
app.StartInfo.UserName = "userName";
string password = "Password";
System.Security.SecureString ssPwd = new System.Security.SecureString();
for (int x = 0; x < password.Length; x++)
{
ssPwd.AppendChar(password[x]);
}
password = "";
app.StartInfo.Password = ssPwd;
app.Start();
Then I confirm it is running by:
private bool IsRunning(string name)
{
Process[] processlist = Process.GetProcesses();
if (Process.GetProcessesByName(name).Length > 0)
{
string user = Process.GetProcessesByName(name)[0].StartInfo.UserName;
log.Debug("Process " + name + " is running by : " + user);
return true;
}
else
{
return false;
}
}
I get back true and find the process but the UserName is empty.
Why is that?
I also found some code to get the Owner of the Process but when I use this the Owner is also empty.
public string GetProcessOwner(int processId)
{
string query = "SELECT * FROM Win32_Process WHERE ProcessID = " + processId;
ManagementObjectSearcher searcher = new ManagementObjectSearcher(query);
ManagementObjectCollection processList = searcher.Get();
foreach (ManagementObject obj in processList)
{
string[] argList = new string[] { string.Empty, string.Empty };
int returnVal = Convert.ToInt32(obj.InvokeMethod("GetOwner", argList));
if (returnVal == 0)
{
// return DOMAIN\user
return argList[1] + "\\" + argList[0];
}
}
return "NO OWNER";
}
Please can you explain me why this is so?
This is by design; when requesting information about a Process via eg. GetProcessesByName the UserName doesn't get retrieved/resolved.
GetProcessByName internally retrieves its info via the code below, constructing Process instances from the obtained ProcesInfo data.
public static Process[] GetProcesses(string machineName)
{
bool isRemoteMachine = ProcessManager.IsRemoteMachine(machineName);
ProcessInfo[] processInfos = ProcessManager.GetProcessInfos(machineName);
Process[] processes = new Process[processInfos.Length];
for (int i = 0; i < processInfos.Length; i++) {
ProcessInfo processInfo = processInfos[i];
processes[i] = new Process(machineName, isRemoteMachine, processInfo.processId, processInfo);
}
return processes;
}
A ProcessInfo instance doesn't contain any user/owner information.
internal class ProcessInfo
{
public ArrayList threadInfoList = new ArrayList();
public int basePriority;
public string processName;
public int processId;
public int handleCount;
public long poolPagedBytes;
public long poolNonpagedBytes;
public long virtualBytes;
public long virtualBytesPeak;
public long workingSetPeak;
public long workingSet;
public long pageFileBytesPeak;
public long pageFileBytes;
public long privateBytes;
public int mainModuleId;
public int sessionId;
}
The private constructor of the Process class accepts this ProcessInfo.
Because no ProcessStartInfo has been set, it instantiates one on retrieval, having an empty UserName.
public string UserName {
get {
if( userName == null) {
return string.Empty;
}
else {
return userName;
}
}
set { userName = value; }
}
About the GetProcessOwner code; this results from the answer to the How do I determine the owner of a process in C#? question.
This is rather an other WMI related topic; not receiving any owner info might have to do with permissions as suggested in the comments.
For me, "it works on my machine" ...
Better start a separate question for this one.
I tried with solution using WinAPIs
You would get "NO OWNER" when the application you are trying to get the owner, is not run as admin
Please check by running your application as Administrator
I am trying to open and print files with the ProcessStartInfo class. (File can be anything but let`s assume it is a PDF File)
ProcessStartInfo pi = new ProcessStartInfo(file);
pi.Arguments = Path.GetFileName(file);
pi.WorkingDirectory = Path.GetDirectoryName(file);
pi.Verb = "OPEN";
Process.Start(pi);
this works well for the pi.Verb = "OPEN";. Some applications register themselves also with the verb "PRINT" but only some do. In my case (Windows PDF Viewer) I get an exception when trying to execute with the pi.Verb = "PRINT";
Is there a way to see all the verbs available for a specific type in C# at runtime?
Thx a lot
The ProcessStartInfo.Verbs property is somewhat broken as it does not consider the way how newer versions of Windows (Windows 8 and above afaik) retrieve the registered application. The property only checks the verbs that are registered for the ProgId defined under HKCR\.ext (as can be seen in the reference source) and does not consider other places such as below the Registry key HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Explorer\FileExts\.ext or some other places, e.g. defined via Policy.
Getting the registered verbs
The best way is to not rely on checking the Registry directly (as done by the ProcessStartInfo class), but to use the appropriate Windows API function AssocQueryString to retrieve the associated ProgId:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using Microsoft.Win32;
class Program
{
private static void Main(string[] args)
{
string fileName = #"E:\Pictures\Sample.jpg";
string progId = AssocQueryString(AssocStr.ASSOCSTR_PROGID, fileName);
var verbs = GetVerbsByProgId(progId);
if (!verbs.Contains("printto"))
{
throw new Exception("PrintTo is not supported!");
}
}
private static string[] GetVerbsByProgId(string progId)
{
var verbs = new List<string>();
if (!string.IsNullOrEmpty(progId))
{
using (var key = Registry.ClassesRoot.OpenSubKey(progId + "\\shell"))
{
if (key != null)
{
var names = key.GetSubKeyNames();
verbs.AddRange(
names.Where(
name =>
string.Compare(
name,
"new",
StringComparison.OrdinalIgnoreCase)
!= 0));
}
}
}
return verbs.ToArray();
}
private static string AssocQueryString(AssocStr association, string extension)
{
uint length = 0;
uint ret = AssocQueryString(
AssocF.ASSOCF_NONE, association, extension, "printto", null, ref length);
if (ret != 1) //expected S_FALSE
{
throw new Win32Exception();
}
var sb = new StringBuilder((int)length);
ret = AssocQueryString(
AssocF.ASSOCF_NONE, association, extension, null, sb, ref length);
if (ret != 0) //expected S_OK
{
throw new Win32Exception();
}
return sb.ToString();
}
[DllImport("Shlwapi.dll", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern uint AssocQueryString(
AssocF flags,
AssocStr str,
string pszAssoc,
string pszExtra,
[Out] StringBuilder pszOut,
ref uint pcchOut);
[Flags]
private enum AssocF : uint
{
ASSOCF_NONE = 0x00000000,
ASSOCF_INIT_NOREMAPCLSID = 0x00000001,
ASSOCF_INIT_BYEXENAME = 0x00000002,
ASSOCF_OPEN_BYEXENAME = 0x00000002,
ASSOCF_INIT_DEFAULTTOSTAR = 0x00000004,
ASSOCF_INIT_DEFAULTTOFOLDER = 0x00000008,
ASSOCF_NOUSERSETTINGS = 0x00000010,
ASSOCF_NOTRUNCATE = 0x00000020,
ASSOCF_VERIFY = 0x00000040,
ASSOCF_REMAPRUNDLL = 0x00000080,
ASSOCF_NOFIXUPS = 0x00000100,
ASSOCF_IGNOREBASECLASS = 0x00000200,
ASSOCF_INIT_IGNOREUNKNOWN = 0x00000400,
ASSOCF_INIT_FIXED_PROGID = 0x00000800,
ASSOCF_IS_PROTOCOL = 0x00001000,
ASSOCF_INIT_FOR_FILE = 0x00002000
}
private enum AssocStr
{
ASSOCSTR_COMMAND = 1,
ASSOCSTR_EXECUTABLE,
ASSOCSTR_FRIENDLYDOCNAME,
ASSOCSTR_FRIENDLYAPPNAME,
ASSOCSTR_NOOPEN,
ASSOCSTR_SHELLNEWVALUE,
ASSOCSTR_DDECOMMAND,
ASSOCSTR_DDEIFEXEC,
ASSOCSTR_DDEAPPLICATION,
ASSOCSTR_DDETOPIC,
ASSOCSTR_INFOTIP,
ASSOCSTR_QUICKTIP,
ASSOCSTR_TILEINFO,
ASSOCSTR_CONTENTTYPE,
ASSOCSTR_DEFAULTICON,
ASSOCSTR_SHELLEXTENSION,
ASSOCSTR_DROPTARGET,
ASSOCSTR_DELEGATEEXECUTE,
ASSOCSTR_SUPPORTED_URI_PROTOCOLS,
ASSOCSTR_PROGID,
ASSOCSTR_APPID,
ASSOCSTR_APPPUBLISHER,
ASSOCSTR_APPICONREFERENCE,
ASSOCSTR_MAX
}
}
From MSDN: https://msdn.microsoft.com/en-us/library/65kczb9y(v=vs.110).aspx?cs-save-lang=1&cs-lang=csharp#code-snippet-2
startInfo = new ProcessStartInfo(fileName);
if (File.Exists(fileName))
{
i = 0;
foreach (String verb in startInfo.Verbs)
{
// Display the possible verbs.
Console.WriteLine(" {0}. {1}", i.ToString(), verb);
i++;
}
}
I have a requirement witch is to execute a vbscript located in a shared network drive. ie: \SERVER1\shared$\path\script.vbs
To connect to this shared folder I need to pass credentials ie:
DOMAIN\AdminShare
1234
This script has to run with local admin credentials. ie:
.\Administrator
1234
The user witch will execute the exe also has it's own credentials, ie:
DOMAIN\User
1234
How can I manage this scenario?
I've successfully connected to the smb with proper credentials with this class:
using System;
using System.Runtime.InteropServices;
using BOOL = System.Boolean;
using DWORD = System.UInt32;
using LPWSTR = System.String;
using NET_API_STATUS = System.UInt32;
namespace blah
{
class UNCAccess
{
// FROM: https://ericwijaya.wordpress.com/2013/02/06/access-remote-file-share-with-username-and-password-in-c/
//
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
internal struct USE_INFO_2
{
internal LPWSTR ui2_local;
internal LPWSTR ui2_remote;
internal LPWSTR ui2_password;
internal DWORD ui2_status;
internal DWORD ui2_asg_type;
internal DWORD ui2_refcount;
internal DWORD ui2_usecount;
internal LPWSTR ui2_username;
internal LPWSTR ui2_domainname;
}
[DllImport("NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern NET_API_STATUS NetUseAdd(
LPWSTR UncServerName,
DWORD Level,
ref USE_INFO_2 Buf,
out DWORD ParmError);
[DllImport("NetApi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
internal static extern NET_API_STATUS NetUseDel(
LPWSTR UncServerName,
LPWSTR UseName,
DWORD ForceCond);
private string sUNCPath;
private string sUser;
private string sPassword;
private string sDomain;
private int iLastError;
public UNCAccess()
{
}
public UNCAccess(string UNCPath, string User, string Domain, string Password)
{
login(UNCPath, User, Domain, Password);
}
public int LastError
{
get { return iLastError; }
}
/// <summary>
/// Logs in to the shared network with the provided credentials
/// </summary>
/// <param name="UNCPath">unc</param>
/// <param name="User">user</param>
/// <param name="Domain">domain</param>
/// <param name="Password">password</param>
/// <returns>TRUE OK, ELSE FALSE</returns>
public bool login(string UNCPath, string User, string Domain, string Password)
{
sUNCPath = UNCPath;
sUser = User;
sPassword = Password;
sDomain = Domain;
return NetUseWithCredentials();
}
private bool NetUseWithCredentials()
{
uint returncode;
try
{
USE_INFO_2 useinfo = new USE_INFO_2();
useinfo.ui2_remote = sUNCPath;
useinfo.ui2_username = sUser;
useinfo.ui2_domainname = sDomain;
useinfo.ui2_password = sPassword;
useinfo.ui2_asg_type = 0;
useinfo.ui2_usecount = 1;
uint paramErrorIndex;
returncode = NetUseAdd(null, 2, ref useinfo, out paramErrorIndex);
iLastError = (int)returncode;
return returncode == 0;
}
catch
{
iLastError = Marshal.GetLastWin32Error();
return false;
}
}
///
/// Closes the UNC share
///
/// True if closing was successful
public bool NetUseDelete()
{
uint returncode;
try
{
returncode = NetUseDel(null, sUNCPath, 2);
iLastError = (int)returncode;
return (returncode == 0);
}
catch
{
iLastError = Marshal.GetLastWin32Error();
return false;
}
}
}
}
Then I did a process start to run the script as admin:
Process p = new Process();
p.StartInfo.FileName = "cscript.exe";
p.StartInfo.WorkingDirectory = #"c:\";
p.StartInfo.Arguments = "//B //Nologo " + script.FullName
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.Verb = "runas";
p.StartInfo.UserName = localAdminAccount;
System.Security.SecureString pwd = new System.Security.SecureString();
foreach (char c in localAdminPasswd) { pwd.AppendChar(c); }
p.StartInfo.Password = pwd;
The problem is that when the process starts with the new credentials (localAdmin) I'm not able to find the script (the user has changed so no access to the shared network).
I though it was because of the user, so I've also tried to create a launcher to elevate the privileges of the execution of the main application without user interaction (another process.start from the launcher), which works fine, but then the same thing happens (not found).
Any help on this? Thanks
I've solved it with two launchers to get the privileges and a runas in the process start, so now i can run the script with the necesary credentials =)
I have an ASP .NET web application on a 64-bit machine that needs to run a legacy 32-bit reporting application.
When I run the program with UseShellExecute = false, the program exits with exit code:
-1073741502
I can't use Shell Execute because I have to run the process as a different user. Yet, when Shell Execute is true, the process will run fine (although I have to change the user that ASP .NET is executing under).
How can I start this 32-bit program using C# without use shell execute?
Here's the code I have right now:
var pxs = new ProcessStartInfo
{
Arguments = arguments,
CreateNoWindow = true,
Domain = ConfigurationManager.AppSettings["reportUserDomain"],
UserName = ConfigurationManager.AppSettings["reportUserName"],
Password = GetSecureString(ConfigurationManager.AppSettings["reportUserPassword"]),
LoadUserProfile = true,
FileName = ConfigurationManager.AppSettings["reportRuntime"],
UseShellExecute = false
};
var px = new Process
{
StartInfo = pxs
};
px.Start();
px.WaitForExit();
What if you surrounded your code, including UseShellExecute = true, with the windows native "LogonUser" method? I've used this successfully in a few projects to do something similar.
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern bool LogonUser(String lpszUserName, String lpszDomain,
String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken
Fresh Click Media did an article about this and wrote a sample Impersonate class: --> http://www.freshclickmedia.com/blog/2008/11/programmatic-impersonation-in-c/
But for completeness, here's my version of it:
public class Impersonator : IDisposable
{
private WindowsImpersonationContext _impersonatedUser = null;
private IntPtr _userHandle;
// constructor for a local account. username and password are arguments.
public Impersonator(string username, string passwd)
{
_userHandle = new IntPtr(0);
string user = username;
string userDomain = "."; // The domain for a local user is by default "."
string password = passwd;
bool returnValue = LogonUser(user, userDomain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref _userHandle);
if (!returnValue)
throw new ApplicationException("Could not impersonate user");
WindowsIdentity newId = new WindowsIdentity(_userHandle);
_impersonatedUser = newId.Impersonate();
}
// constructor where username, password and domain are passed as parameters
public Impersonator(string username, string passwd, string domain)
{
_userHandle = new IntPtr(0);
string user = username;
string userDomain = domain;
string password = passwd;
bool returnValue = LogonUser(user, userDomain, password, LOGON32_LOGON_INTERACTIVE, LOGON32_PROVIDER_DEFAULT, ref _userHandle);
if (!returnValue)
throw new ApplicationException("Could not impersonate user");
WindowsIdentity newId = new WindowsIdentity(_userHandle);
_impersonatedUser = newId.Impersonate();
}
public void Dispose()
{
if (_impersonatedUser != null)
{
_impersonatedUser.Undo();
CloseHandle(_userHandle);
}
}
public const int LOGON32_LOGON_INTERACTIVE = 2;
public const int LOGON32_LOGON_SERVICE = 3;
public const int LOGON32_PROVIDER_DEFAULT = 0;
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern bool LogonUser(String lpszUserName, String lpszDomain, String lpszPassword, int dwLogonType, int dwLogonProvider, ref IntPtr phToken);
[DllImport("kernel32.dll", CharSet = CharSet.Auto)]
public extern static bool CloseHandle(IntPtr handle);
}
Using it in your case would be:
var domain = ConfigurationManager.AppSettings["reportUserDomain"];
var username = ConfigurationManager.AppSettings["reportUserName"];
var password = ConfigurationManager.AppSettings["reportUserPassword"];
using (Impersonator impersonator = new Impersonator(username, password, domain))
{
var pxs = new ProcessStartInfo
{
Arguments = arguments,
CreateNoWindow = true,
LoadUserProfile = true,
FileName = ConfigurationManager.AppSettings["reportRuntime"],
UseShellExecute = true
};
var px = new Process
{
StartInfo = pxs
};
px.Start();
px.WaitForExit();
}