#c# #cmd #copy #psexec
#c# #cmd #Копировать #psexec
Вопрос:
Я хочу скопировать файл с компьютера A (с учетной записью MyAccount@mydomain) на компьютер B (UserB@ComputerB) по сети, используя c #. Я попробовал стандартный
File.Copy(source,destination)
и попытался запустить процесс cmd (с компьютера A) и вызвать метод копирования
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 \computerAPathFile1.txt \computerBPath$ ";
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
//It will exit the user name or password is incorrect
Я пытался также использовать PsExec для олицетворения 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 computerBuserB -p userBPassword cmd /c COPY \computerAPathFile1.txt \computerBPath$";
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
//it will exit that the source file is unknown
Подводя итог, компьютер A может видеть источник (сам), но не пункт назначения (поскольку компьютер B имеет только авторизованного локального пользователя).
компьютер B может видеть пункт назначения (сам), но не источник (поскольку компьютер A находится за пределами своего домена и не является общим по сети).
Есть ли обходной путь для этой проблемы?
Комментарии:
1. «Есть ли обходной путь для этой проблемы?» — Многие. Некоторые из них: 1. Создайте общедоступный и анонимный общий ресурс на
B
(не такой безопасный). 1b. Создайте выделенного пользователя наB
, который имеет права на назначение. 2. Переключитесь с «push» на «pull»: что-то вроде http-сервера наA
иB
запросите документ / файл. 3. Запустите общедоступный FTP-серверB
4. Используйте SSH / SCP…2. Исключите C # из уравнения. Вам нужно заставить это работать без C #, затем включить C # в проблему (поскольку C # не может волшебным образом чего-либо достичь, если целевая машина недоступна).
3. @Fildor спасибо, как бы я мог выполнить это ваше второе предложение «Переключиться с «push» на «pull»
4. @MhamadTabikh Я не знаю вашего системного ландшафта и требований. Вполне может быть, что это невозможно, поскольку
B
пришлось бы «знать», что извлекать, и если и когда вообще.5. @Fildor Если бы я подключил диск компьютера A к компьютеру A и запустил .copy(), это сработало бы?
Ответ №1:
Похоже, что это довольно простая проблема аутентификации того типа, которая возникает всякий раз, когда у текущего пользователя нет прав на общий ресурс, как в домене, так и вне его. Проблема также возникает при запуске под системным пользователем и попытке получить доступ к общим ресурсам на других устройствах.
Решение состоит в том, чтобы открыть аутентифицированное соединение с целевым устройством с помощью WNetUseConnection
API, выполнить свои файловые операции, а затем закрыть соединение с помощью вызова WNetCancelConnection2
.
Вот некоторый код, который я использовал в прошлом для этого:
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();
}
}
Создайте экземпляр ConnectSMB
класса перед выполнением операций с удаленными файлами, затем удалите (или закройте) его, когда вы закончите с подключением.
using (var smb = new ConnectSMB(@"\computerBPath", "userB", "userBPassword"))
{
File.Copy(source, destination);
}
Комментарии:
1. спасибо, но у меня есть вопрос. Если бы я установил это соединение и после копирования файла, не выдало бы ли это исключение, поскольку UserB, похоже, не может найти исходный путь?
2. @MhamadTabikh По сути, это то же самое, что происходит, когда вы переходите к удаленному пути в проводнике Windows и получаете приглашение для входа на удаленный компьютер. Это не изменяет текущего пользователя, это добавляет токен аутентификации к текущему пользователю, который позволяет им получать доступ к ресурсам на удаленном компьютере.