Долгое время пишу 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
Из оставшихся задумок:
Указывать в конфигурационном файле список копируемых файлов и создаваемых директорий (придумать как десериализовать массив).Обработать оставшиеся исключения и правильно их причесать.Вынести наиболее обширные части кода в модули.
В комментариях приветствуются замечания и предложения))
Комментариев нет:
Отправить комментарий
Уважаемый комментатор, пишите грамотно.
С благодарностью, автор блога.