Долгое время пишу BAT скриптики, но его возможности не безграничны, да и не всегда есть возможность нормально и правильно обработать исключения. Поэтому, по рекомендации нашего админа, решил установить Visual Studio 2010 C# Express. Бесплатная, а мне больше и не надо))
Собственно, первым делом решил переписать скриптик, который ставил 1С:Предприятие на машины пользователей в тихом режиме, копировал файл настройки hasp-ключа и файл со списком ИБ. Ставится с использованием программы Русиновича PSExec. Собственно, вот что у меня получилось. Корявенько, правда, хотелось бы переписать как должно быть, но оно работает, а мне больше и не надо.
Исходник:
using System;
using System.Net.NetworkInformation;
using System.Text;
using System.Diagnostics;
using System.IO;
using System.Management;
using System.Xml;
using System.Xml.Serialization;
using Microsoft.Win32;
namespace Install_Program
{
public class WMI
{
string WMI_UN;
int WMI_FS;
public string WMI_UserName(String CompName)
{
// Создаём новое соединение к WMI удалённого компьютера
ConnectionOptions connection = new ConnectionOptions();
// Соединяемся с WMI целевого компьютера...
ManagementScope scope = new ManagementScope(@"\\" + CompName + @"\root\CIMV2", connection);
scope.Connect();
// ...и читаем информацию о компьютере
ObjectQuery UserName = new ObjectQuery("SELECT UserName FROM Win32_ComputerSystem");
ManagementObjectSearcher sUserName = new ManagementObjectSearcher(scope, UserName);
// Находим в этой информации текущего пользователя
foreach (ManagementObject queryObj in sUserName.Get())
{
String wmi_un = queryObj["UserName"].ToString();
// Выделяем в ней часть хоста и часть с именем пользователя
String[] un = wmi_un.Split(new char[] { '\\' }, StringSplitOptions.RemoveEmptyEntries);
WMI_UN = un[1];
}
return WMI_UN;
}
public int WMI_FreeSpace(String CompName)
{
// Создаём новое соединение к WMI удалённого компьютера
ConnectionOptions connection = new ConnectionOptions();
// Соединяемся с WMI целевого компьютера...
ManagementScope scope = new ManagementScope(@"\\" + CompName + @"\root\CIMV2", connection);
scope.Connect();
// ...и читаем информацию о компьютере
ObjectQuery FreeSpace = new ObjectQuery("SELECT FreeSpace, Name FROM Win32_LogicalDisk WHERE Name = 'C:'");
ManagementObjectSearcher sFreeSpace = new ManagementObjectSearcher(scope, FreeSpace);
foreach (ManagementObject queryObj in sFreeSpace.Get())
{
// Получаем свободное пространство на диске C:, в ГБ
long wmi_fs = long.Parse(queryObj["FreeSpace"].ToString());
WMI_FS = (int)(wmi_fs / 1024 / 1024 / 1024);
}
return WMI_FS;
}
}
public class Conf
{
public string User = @" ";
public string Password = @" ";
public string KeyInstall = @"-d";
public string FileComp = @"1C.txt";
public string ProgConf = @"D:\TT";
[XmlArrayAttribute("Items")]
public Item[] Items;
}
public class Item
{
public string InstallProgram;
public string Key;
public string From;
public string To;
}
public class Program
{
public static void WriteFile()
{
int i;
// Читаем из файла списка ИБ 1С все строки
string[] str = File.ReadAllLines(@"copy\ibases.v8i", Encoding.Default);
// И для каждой строки, с нулевой до последней...
for (i = 0; i < str.Length; i++)
{
// ...ищем начинающуюся с "ID=" и заменяем её на
if (str[i].StartsWith("ID="))
{
str[i] = ("ID=" + Guid.NewGuid().ToString());
break;
}
}
// Всё сделали, записываем изменения обратно
File.WriteAllLines(@"copy\ibases.v8i", str, Encoding.Default);
}
public static void Logger(String LogText)
{
File.AppendAllText("log.ini", DateTime.Now.ToString() + "\t" + LogText, Encoding.Default);
}
public static void Main(string[] args)
{
// Определяем переменные
String S, command;
// Для файла конфигурации
String User, Password, KeyInstall, FileComp, ProgConf;
//Подключаем класс, который получает данные о компьютере
WMI wmi_info = new WMI();
// Десериализуем файл конфигурации
String[] XMLFile = Directory.GetFiles(@".\", "*.xml");
XmlSerializer serializer = new XmlSerializer(typeof(Conf));
Stream stream = new FileStream(XMLFile[0], FileMode.Open);
Conf conf = (Conf)serializer.Deserialize(stream);
//Чтобы было удобнее работать с десериализованными данными
User = conf.User;
Password = conf.Password;
KeyInstall = conf.KeyInstall;
FileComp = conf.FileComp;
ProgConf = conf.ProgConf;
Item[] items = conf.Items;
// Создаём поток для файла откуда читать номера компьютеров
StreamReader sr = new StreamReader(FileComp, Encoding.Default);
try
{
// Пока не достигнем конца файла...
while (!sr.EndOfStream)
{
// ... читаем строки
S = sr.ReadLine();
// Парсим список компьютеров и выделяем оттуда имя компьютера
String[] A = S.Split(new char[] { '_' }, StringSplitOptions.RemoveEmptyEntries);
// Пингуем
Ping pingSender = new Ping();
// Если строка не начинается с "#", пинг успешен и хватает места на установку программы то работаем
if (A[0][0] != '#')
{
PingReply reply = pingSender.Send(A[0], 100);
if (reply.Status == IPStatus.Success)
{
//Получаем имя текущего пользователя с удалённого компьютера
String wmi_un = wmi_info.WMI_UserName(A[0]);
Logger("=====================================================\n");
Logger("Установка Программы" + ProgConf + "\n");
Logger("=====================================================\n");
//Ну а если места недостаточно, то пишем в лог, сообщаем в консоль и переходим к следующего компьютеру
if (wmi_info.WMI_FreeSpace(A[0]) <= 2)
{
Logger(A[0] + "\t" + wmi_un + "\tНедостаточной места на диске C: для установки программы" + "\n");
Console.WriteLine(A[0] + "\t" + wmi_un + "\tНедостаточной места на диске C: для установки программы");
continue;
}
//Редактируем файл списка ИБ 1С
WriteFile();
for (int i = 0; i < items.Length; i++)
{
String CopyTo = items[i].To.Replace("A[0]", A[0]).Replace("A[1]", A[1]).Replace("B[0]", wmi_un);
command = "-u " + User + " -p " + Password + @" \\" + A[0] + " " + KeyInstall + " " + items[i].InstallProgram;
switch (items[i].Key)
{
//Ключ "с" обозначает копирование файлов (от Copy)
case "c":
try
{
File.Copy(items[i].From, CopyTo, true);
}
catch (UnauthorizedAccessException)
{
Logger(A[0] + "\t" + wmi_un + "\tОтсутствют права на копирование файла " + items[i].From + "\n");
Console.WriteLine(A[0] + "\t" + wmi_un + "\tОтсутствют права на копирование файла " + items[i].From);
}
catch (IOException)
{
Logger(A[0] + "\t" + wmi_un + "\tФайл " + items[i].From + " уже существует или его перезапись невозможна\n");
Console.WriteLine(A[0] + "\t" + wmi_un + "\tФайл " + items[i].From + " уже существует или его перезапись невозможна");
}
catch
{
Logger(A[0] + "\t" + wmi_un + "\tВозникла какая-то ошибка при копировании файла " + items[i].From + "\n");
Console.WriteLine(A[0] + "\t" + wmi_un + "\tВозникла какая-то ошибка при копировании файла " + items[i].From);
}
break;
//Ключ "md" обозначает создание дирректории (от Make Directory)
case "md":
try
{
Directory.CreateDirectory(CopyTo);
}
catch (UnauthorizedAccessException)
{
Logger(A[0] + "\t" + wmi_un + "\tОтсутствют права на создание папки " + items[i].To + "\n");
Console.WriteLine(A[0] + "\t" + wmi_un + "\tОтсутствют права на создание папки " + items[i].To);
}
break;
//Ключ "rd" обозначает удаление дирректории (от Remove Directory)
case "rd":
try
{
Directory.Delete(CopyTo, true);
}
catch (UnauthorizedAccessException)
{
Logger(A[0] + "\t" + wmi_un + "\tОтсутствют права на создание папки " + items[i].To + "\n");
Console.WriteLine(A[0] + "\t" + wmi_un + "\tОтсутствют права на создание папки " + items[i].To);
}
catch (DirectoryNotFoundException)
{
Logger(A[0] + "\t" + wmi_un + "\tОтсутствует или не может быть найдена папка " + items[i].To + "\n");
Console.WriteLine(A[0] + "\t" + wmi_un + "\tОтсутствует или не может быть найдена папка " + items[i].To);
}
catch (IOException)
{
Logger(A[0] + "\t" + wmi_un + "\tКаталог только для чтения или занят " + items[i].To + "\n");
Console.WriteLine(A[0] + "\t" + wmi_un + "\tКаталог только для чтения или занят " + items[i].To);
}
break;
//Ключ "rf" обозначает удаление файла (от Remove File)
case "rf":
try
{
File.Delete(CopyTo);
}
catch (UnauthorizedAccessException)
{
Logger(A[0] + "\t" + wmi_un + "\tОтсутствют права на удаление файла или файл только для чтения " + items[i].To + "\n");
Console.WriteLine(A[0] + "\t" + wmi_un + "\tОтсутствют права на удаление файла или файл только для чтения " + items[i].To);
}
catch (DirectoryNotFoundException)
{
Logger(A[0] + "\t" + wmi_un + "\tУказанный путь недопустим " + items[i].To + "\n");
Console.WriteLine(A[0] + "\t" + wmi_un + "\tУказанный путь недопустим " + items[i].To);
}
catch (IOException)
{
Logger(A[0] + "\t" + wmi_un + "\tЗаданный файл кем-то используется " + items[i].To + "\n");
Console.WriteLine(A[0] + "\t" + wmi_un + "\tЗаданный файл кем-то используется " + items[i].To);
}
break;
//Ключ "i" обозначает установку (от Install)
case "i":
Process.Start("psexec.exe", command);
System.Threading.Thread.Sleep(30000);
break;
}
}
RegistryKey key = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, A[0])
.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Session Manager\Environment", true);
key.SetValue("SAP_CODEPAGE", "1504");
//Сообщаем, что что-то было сделано
Logger(A[0] + "\t" + wmi_un + "\tНа компьютер установлено и скопировано необходимое ПО\n");
Console.WriteLine(A[0] + "\t" + wmi_un + "\tНа компьютер установлено и скопировано необходимое ПО");
}
else
{
// Ну а если компьютер выключен или не пингуется, дадим знать об этом в логе
Logger(A[0] + " Компьютер выключен или не пингуется\n");
Console.WriteLine(A[0] + " Компьютер выключен или не пингуется");
}
}
}//while
}//try
catch (IOException exc)
{
Console.WriteLine("Ошибка ввода-вывода:\n" + exc.Message);
}
finally
{
sr.Close();
}
}
}
}
Файл "comps.txt" имеет такой вид:ИмяКомпьютера_ИмяПользователяИмя пользователя, правда, теперь берётся с помощью WMI, но переписывать лень, да и вдруг пригодится))
ИмяКомпьютера_ИмяПользователя
Вот как это работает. Есть файл "comps.txt", в котором содержится имя компьютера, на который необходимо установить 1С (можно и другой продукт, с небольшими изменениями в коде). Парсим строку, берём из неё имя компьютера и пингуем. Если пинг проходит, то запускаем установку приложения и ждём 20 секунд, пока приложение поставится. Через 20 секунд получаем имя текущего пользователя, в профиль которого нам необходимо поместить файл содержащий список Информационных Баз (ИБ), редактируем в нём поле ID, в которое генерируем GUID, необходимый для идентификации в 1С:Предприятие. Далее копируем этот файл в профиль. Ну и копируем файл "nethasp.ini", который содержит адрес сетевого ключа.
После недолгих раздумий прикрутил ещё и конфигурационный файл, чтобы каждый раз не править код под установку разных программ. Файл называется config.xml. Вот как он выглядит:
Ключи в тэгах "Items" располагать по логической структуре: сначала ставим (ключи "i"), потом создаём директории (ключ "md") и копирование в уже созданные папки (ключ "c").домен\админ-пользователь pswd -d SAP.TXT SAP i \\InstallServer\program i \\InstallServer\program md \\A[0]\C$\Documents and Settings\B[0]\Application Data\... c copy\File \\A[0]\C$\Documents and Settings\B[0]\Application Data\...\File
Из оставшихся задумок:
Указывать в конфигурационном файле список копируемых файлов и создаваемых директорий (придумать как десериализовать массив).Обработать оставшиеся исключения и правильно их причесать.Вынести наиболее обширные части кода в модули.
В комментариях приветствуются замечания и предложения))
Комментариев нет:
Отправить комментарий
Уважаемый комментатор, пишите грамотно.
С благодарностью, автор блога.