Pull to refresh
54.59
СЕРВЕР МОЛЛ
Серверное и сетевое оборудование

Набор отверток администратора 1С

Reading time13 min
Views67K

image alt text


За несколько лет сначала вынужденного, а потом и вполне занимательного администрирования 1С у меня накопился набор решений под большинство особенностей продукта. Предлагаю отложить в сторону высокие материи про кластеры и тюнинг SQL, и перетряхнуть запасы скриптов и механизмов, которые облегчают жизнь с 1С.


Будут как простые инструменты создания новых пользователей и мониторинга "все ли вышли из базы", так и более изощренные интерфейсы проверки целостности базы и ее перемещения.


Чистый кэш и крепкое здоровье


Как у большинства сложных приложений, у 1С через некоторое время работы вылезают странные ошибки, и возникает порой необъяснимое поведение. Специальные люди по 1С советуют в таких случаях почистить кэш.


Если запустить 1С с параметром /ClearCache, то будут очищены только клиент-серверные запросы. Локальные метаданные останутся и их нужно удалять отдельно на уровне файлов и папок. Эти данные хранятся в профиле пользователя, в папках с длинными названиями из GUID баз данных. Если баз на сервере немного, то такой кэш нетрудно удалить руками. Но если БД исчисляется десятками, то чистке вручную вы не обрадуетесь.


image alt text


В подобных ситуациях выручит скрипт на Powershell, который запускается каждый раз при выходе пользователя из системы:


Get-ChildItem "$env:USERPROFILE\AppData\Local\1C\1Cv8\*","$env:USERPROFILE\AppData\Roaming\1C\1Cv8\*" | Where {$_.Name -as [guid]} |Remove-Item -Force -Recurse

И никаких связанных со старым кэшем проблем.


Исправляем ошибки


Для исправления испорченной файловой базы в поставку 1С входит утилита chdbfl.exe, которая просто считывает содержимое базы во временный файл. Если какие-то данные считать не может — пропускает. При этом у нее нет ключей запуска для автоматизации, и проверку приходится запускать вручную.


Вообще, правильнее запускать проверку БД конфигуратором, но этот процесс проходит значительно дольше. Если же использовать только проверку физической целостности средствами chdbfl.exe, то не забывайте делать резервную копию из-за возможной потери данных.


Для баз 8.1 Андрей Скляров создал хороший инструмент Check1CD, с двумя параметрами запуска: "исправлять найденные ошибки" и “путь к базе”.


Но в Check1CD не хватает двух вещей:


  • Индикатора прогресса;


  • Уведомлений техподдержки о найденных ошибках.

Раз есть "хотелка" и немного свободного времени, то почему бы не попробовать решить вопрос самостоятельно?


Так появился мега-скрипт на AutoIt
#NoTrayIcon

#include <GUIConstantsEx.au3>

#include <File.au3>

#include <Date.au3>

#include <Inet.au3>

Global $oMyError = ObjEvent("AutoIt.Error","MyErrFunc")

Global $Basepath="D:\Base\” ;база везде в одном месте, можно хардкодить

;Проверим, не запущена ли 1С.

If ProcessExists("1cv8.exe") or ProcessExists("chdbfl.exe") Then 

    Msgbox(0,"ошибка!", "1c уже запущена, запуск второй невозможен")

Exit

EndIf

;Проверим, не была ли прервана проверка, по наличию временного файла

if FileExists ($Basepath&"_$NEW$_.1CD") Then

MSGbox(0,"ошибка!", "проверка базы была прервана! обратитесь в Отдел ИТ")

Exit

EndIf

;Ну и проверим, на месте ли сам файл базы

If not FileExists ($Basepath&"1Cv8.1CD") Then

Msgbox(0,"ошибка!", "файл БД не обнаружен!")

Exit

EndIf

;Конечно, спросим

$hgui = GUICreate("проверка БД" , 200, 200) 

GUICtrlCreateLabel(" будет запущена проверка базы",2,2)

GUICtrlCreateLabel("что может занять более 30 мин",2,22)

GUICtrlCreateLabel("     уверены?      ",2,42)

$yes = GUICtrlCreateButton("да", 5, 150, 70, 25)

$no= GUICtrlCreateButton("нет", 75, 150, 70, 25)

GUISetState(@SW_SHOW) 

While 1 

$msg = GUIGetMsg() 

 Select 

 Case $msg = $GUI_EVENT_CLOSE 

        GUIDelete()

Exit 

 Case $msg = $yes

 GUIDelete()

 _check ()

ExitLoop 

Case $msg = $no

GUIDelete()

Exit 

 EndSelect

WEnd

;Сама проверка базы

func _check ()

$sTitle="Проверка физической целостности файла БД"

ProgressOn("проверка БД", "выполняется проверка базы данных", "0%",-1,-1,18)

run ("C:\Program Files\1cv81\bin\chdbfl.exe","", @SW_HIDE,7)

WinWait($sTitle)

local $size = filegetsize($Basepath&"1Cv8.1CD")

;Автокликер!

ControlSend($sTitle,"", "V8FormElement9",$Basepath&"1Cv8.1CD")

ControlClick($sTitle,"", "V8FormElement8")

ControlClick($sTitle,"", "V8FormElement7","left",2)

$filename = $Basepath&"\1Cv8.1CD"

local $size1 =0

;Считаем процент размера временного файла и заполняем прогрессбар

While 1

$size1 = filegetsize($Basepath&"_$NEW$_.1CD")

    $perc=round(($size1/$size)*100)

ProgressSet($perc, "пожалуйста, ожидайте. прошло "& $perc & "%")

sleep (1500)

;Проверяем, что проверка еще идет по наличию временного файла

;и заблокированности файла БД

If not FileInUse($filename) and not FileExists ($Basepath&"\_$NEW$_.1CD") Then

        ExitLoop

 EndIf  

WEnd

sleep(5000)

;К сожалению, получить информацию из объектов окна нормально не получилось

;Поэтому - автокликер!

WinSetState ( $sTitle, "", @SW_SHOW )

WinSetOnTop($sTitle, "", 1)

WinActivate($sTitle, "")

WinWaitActive($sTitle)

ControlClick($sTitle,"", "V8FormElement4")

sleep(50)

ControlSend($sTitle,"", "V8FormElement4","^{home}")

sleep(50)

ControlSend($sTitle,"", "V8FormElement4","+^{end}")

sleep(50)

ControlSend($sTitle,"", "V8FormElement4","^{ins}")

sleep(50)

$text = ClipGet()

ControlClick($sTitle,"", "V8FormElement6","left",2)

ProgressSet(100, "100%", "проверка завершена")

sleep (500)

ProgressOff()

;Создаем лог-файл, и пишем туда результат проверки, если он есть.

;И отправляем e-mail

;В организации используется Outlook Express. Берём отправителя из реестра

$sender = RegRead("HKEY_CURRENT_USER\Software\Microsoft\ _

Internet Account Manager\Accounts\00000001","SMTP Email Address")

$sendername = RegRead("HKEY_CURRENT_USER\Software\Microsoft\ _

Internet Account Manager\Accounts\00000001","SMTP Display Name")

If not FileExists($Basepath&"check.Log") then _FileCreate($Basepath&"\check.Log")

FileWrite ($Basepath&"check.Log", _NowCalc()&@CRLF )

If $text <> "" Then

FileWrite ( $Basepath&"check.Log", "база бита:"&@CRLF)

FileWrite ( $Basepath&"check.Log", $text&@CRLF)

FileWrite ( $Basepath&"check.Log", "отправляем имейл.."&@CRLF)

;Проверяем доступность нашего smtp. Да-да, опять хардкод

If Ping("smtp-server") <> 0 then 

  if $sender = "" or $sendername="" Then

     MSGbox(0,"ошибка!", "не настроена почта, обратитесь в отдел ИТ!") 

  Else

  email($sender, $sendername,$text)

EndIf

endif 

;а если все хорошо, то и писать не будем

Else

 FileWrite ( $Basepath&"check.Log", "всё чисто"&@CRLF)  

EndIf

$hgui = GUICreate("проверка БД" , 200, 200) 

GUICtrlCreateLabel("проверка БД завершена",2,2)

$ok = GUICtrlCreateButton("да", 5, 150, 70, 25)

GUISetState(@SW_SHOW) 

While 1 

$msg = GUIGetMsg() 

 Select 

 Case $msg = $GUI_EVENT_CLOSE or $msg=$1

        GUIDelete()

Exit 

EndSelect

WEnd

EndFunc

Func FileInUse($filename)

$handle = FileOpen($filename, 1)

$result = False

if $handle = -1 then $result = True

FileClose($handle)

return $result

 EndFunc

func email($sender, $sendername,$text)

local $array[1]

$array[0]=$text

$t=_INetSmtpMail("smtpserver", $sendername,$sender, "[helpdesk@domain.com](mailto:helpdesk@domain.com)", _

"базка побилась" ,$array,"EHLO " & @ComputerName,-1 )

$error=@error

if $t <> 1 Then

FileWrite ( $Basepath&"check.Log", "ошибка отправки e-mail от "&$sendername& " "&$sender&@CRLF) 

FileWrite ( $Basepath&"check.Log", "ошибка протокола "&$error&@CRLF)

Else

FileWrite ( $Basepath&"check.Log", "email отправлен"&@CRLF)

    EndIf

EndFunc

;----------------------------------------------------------------------------------------------------------

 ; Com Error Handler

 ;----------------------------------------------------------------------------------------------------------

 Func MyErrFunc() 

 Local $HexNumber

 Local $strMsg

 $HexNumber = Hex($oMyError.Number, 8) 

 $strMsg = " " & $HexNumber & @CRLF 

 $strMsg &= "описание " & $oMyError.WinDescription & @CRLF 

 $strMsg &= "строка: " & $oMyError.ScriptLine & @CRLF 

 FileWrite ( $Basepath&"check.Log", "ошибка ком-объекта: "&$strMsg)

     FileWrite ( $Basepath&"check.Log", "e-mail не отправлен"& @CRLF)

 SetError(1)

 Endfunc

Доработать код для передачи параметров через ключи командной строки — дело техники.


image alt text


Так было


image alt text


Так стало


С выходом 1С 8.2 возникла проблема — путь к chdbfl менялся с установкой нового релиза. Что ж, дополним скрипт:


;Соберём в массив все пути к chdbfl в отсортированный массив

$path=_FileListToArrayRec(@ProgramFilesDir&"\1cv8\","chdbfl.exe",1,1,0,2)

If @error then

    Msgbox("","","1c не установлена, или установлена не туда"

Exit

Endif

;и запустим первый

Run ($path[1],"", @SW_HIDE,7)

Надо сказать, недавно был опубликован исходный код Check1CD. Да, тоже на AutoIT.


Аналогичный механизм можно применять и для автоматического запуска различных регламентных механизмов, где нужно запускать 1С и ждать завершения операции.


Выкидываем из базы с выдумкой


При различных регламентных операциях с 1С (ночное обновление конфигурации или бэкап в .dt) важно обеспечить отсутствие подключенных к ней пользователей. Можно конечно запускать 1С: Предприятие с ключом /C ЗавершитьРаботуПользователей, но это не всегда удобно, да и хочется же потом написать личное письмо с рекомендациями по устранению склероза.


Можно использовать штатную возможность подключения к 1С через COMConnector и скрипт на AutoIT. Код написан под 1С 8.1 и позволяет выкинуть пользователей из базы с записью в журнал.


global $obj = ObjCreate("v81.comconnector")

$AgentConnection = $obj.ConnectAgent("tcp://servername:1540")

$Cluster = $AgentConnection.GetClusters()[0]

$AgentConnection.Authenticate($Cluster, "", "")

$WorkingProcess = $AgentConnection.GetWorkingProcesses($Cluster)[0]

$ConnectString = $WorkingProcess.HostName & ":" & $WorkingProcess.MainPort

$WorkingProcessConnection = $obj.ConnectWorkingProcess($ConnectString)

$WorkingProcessConnection.AddAuthentication("логин","пароль")

$ibDesc = $WorkingProcessConnection.CreateInfoBaseInfo()

$ibDesc.Name = "имя базы"

$connections = $WorkingProcessConnection.GetInfoBaseConnections($ibDesc)

for $temp in $connections

if $temp.username <> "логин" and ($temp.AppID <> "COMConsole" _

or "COM-соединение") then

        FileWriteLine ( "файл лога", @MDAY&"."&@MON&"."&@YEAR&" _

"&@HOUR&":"&@MIN&" в базе сидит "&$temp.username &" _

тип подключения "&$temp.AppID)

$WorkingProcessConnection.Disconnect($temp)

endif

Next

$AgentConnection=""

$connect=""

Но операцию иногда нужно проворачивать по просьбе самого пользователя, который запустил "тяжелый" отчет и повесил 1С. Если не хотите решать эти вопросы самостоятельно, то просто выведите любителям “тяжелых” отчётов ярлык на скомпилированный скрипт:


$a=Msgbox(4,"Вы уверены?","Вы точно хотите выкинуться из базы?")

If $a<>6 then

exit

Else

global $obj = ObjCreate("v83.comconnector")

$AgentConnection = $obj.ConnectAgent("tcp://servername:1540")

$Cluster = $AgentConnection.GetClusters()[0]

$AgentConnection.Authenticate($Cluster, "", "")

$infobases=$AgentConnection.GetInfoBases($Cluster)

for $base in $infobases

if $base.name="basename" Then

$connections=$AgentConnection.GetInfoBaseSessions($Cluster, $base)

        for $temp in $connections

;Пользователи в базе именуются так же, как и доменный пользователь

            If $temp.username = @username then

                $AgentConnection.terminatesession($cluster,$temp)

            EndIf

         next

         Exit

    EndIf

next

Endif

Еще COMConnector помогает проверить наличие обновлений конфигурации, получить какую-то информацию из базы, и автоматизировать заведение пользователей в 1С.


Технологичное создание новых пользователей


На мой взгляд, создавать новых пользователей 1С должен системный администратор, а не программист 1С. Но последнему нужно сделать процесс максимально простым. Чтобы администратору не приходилось "заглядывать" в каждую базу отдельно, можно использовать еще один скрипт.


Для примера приведу функцию создания пользователей в типовой Бухгалтерии 2.0.
;передаем функции имя пользователя в домене, фамилию, имя и отчество

func _1cBuh($username, $surname,$name,$fathername)

FileWriteLine($logfile, "добавляем в 1с 8.3 бухгалтерия "& $Username)

$obj = ObjCreate("v83.comconnector")

$connect=$obj.Connect("Srvr=""servername"";Ref=""basename"";Usr=""login"";Pwd=""password"";")

Local $avArray[1], $i=0

;Спрашиваем про роли в базе

$hgui = GUICreate("вопрос" , 200, 200)

GUICtrlCreateLabel("какие права в бухгалтерии?",2,2)

GUICtrlCreateLabel("полные или пользовательские?",2,22)

$1 = GUICtrlCreateButton("полные", 5, 150, 70, 25)

$2= GUICtrlCreateButton("пользователь", 75, 150, 70, 25)

GUISetState(@SW_SHOW)

While 1

$msg = GUIGetMsg()

Select

Case $msg = $1

GUIDelete()

$rght="ПолныеПрава"

ExitLoop

Case $msg = $2

GUIDelete()

$rght="Бухгалтер"

ExitLoop

 EndSelect

 WEnd

$mt=$connect.Metadata.Roles.find($rght)

;создаем пользователя ИБ

$newuser= $connect.ПользователиИнформационнойБазы.createuser()

$newuser.name = $surname&" "&$name

$newuser.fullname = $surname&" "&$name&" "&$fathername

$newuser.StandardAuthentication = "False"

$newuser.OSAuthentication="True"

$newuser.OSuser="\\DOMAINNAME\"&$Username

$newuser.Roles.add($mt)

$newuser.write()

;создаем пользователя в справочнике

$bject=$connect.NewObject("СправочникМенеджер.Пользователи")

$newuserS=$bject.CreateItem()

$newuserS.code=$surname&" "&$name

$newuserS.Description=$surname&" "&$name&" "&$fathername

$newuserS.write()

;Выбираем группы (юрлица)

$query=$connect.Catalogs.ГруппыПользователей.Выбрать()

while $query.Следующий()

if StringInStr ( $query.Description, "Группа") then

_ArrayAdd($avArray, $query.Description)

$i+=1

endif

wend

$avArray[0]=$i

GUICreate("Группы Бухгалтерии", 200,$i*20+70)

for $i=1 to $avArray[0]

assign ("var"&$i, GUICtrlCreateCheckbox( $avArray[$i], 2, 0+20*$i));, 90, 20))

next

$OK_Btn = GUICtrlCreateButton("добавить", 50,$i*20+20, 60)

GUISetState()

While 1

$msg = GUIGetMsg()

Select

Case $msg = $OK_Btn

For $i=1 to $avArray[0]

if BitAND(GUICtrlRead(eval("var"&$i)), $GUI_CHECKED) = _

$GUI_CHECKED Then

$grp=$connect.Справочники.ГруппыПользователей. _

НайтиПоНаименованию($avArray[$i]).ПолучитьОбъект()

$t=$grp.ПользователиГруппы.add()

$t.Пользователь = $newuserS.ссылка

$grp.write()

endif

next

GUIDelete()

ExitLoop

EndSelect

 WEnd

$connect=""

endfunc

image alt text


Юрлиц развелось слишком много — нужно разбивать на столбцы.


Занятно, но после смены нескольких поколений администраторов в одной компании из далекого прошлого новенькие уже не знали как создать пользователя вручную.


Автоматическое перемещение баз


Если нужно перенести базу 1С: Предприятия вместе с ее данными в SQL на другой сервер, то делать это вручную целесообразно только для 1-2 БД.


При большем их числе рутина станет утомительной - лучше воспользоваться скриптом.
#include <GUIConstantsEx.au3>
local $name
GUICreate("перенос", 200, 180)
GUICtrlCreateLabel("название базы по английки",2,35)
$name1 = GUICtrlCreateInput($name, 2, 50, 300, 20)
$OK_Btn = GUICtrlCreateButton("погнали", 70, 180, 60)
GUISetState()
While 1
  $msg = GUIGetMsg()
  Select
     Case $msg = $GUI_EVENT_CLOSE
         Exit
     Case $msg = $OK_Btn
     $name = GUICtrlRead($name1)
     exitloop
  endselect
WEnd
GUIDelete()
GUICreate("перенос", 200, 180)
GUICtrlCreateLabel("отключаем базу",2,2)
GUISetState()
$constrim="DRIVER={SQL Server};SERVER=старый_сервер;DATABASE=master;uid=sa;pwd=пароль;"
$adCN = ObjCreate ("ADODB.Connection")
$adCN.Open ($constrim)
$sQuery = "sp_detach_db @dbname = N'"&$name&"'"
$adCN.Execute($sQuery)
$adCN.Close
GUICtrlCreateLabel("перемещаем базу",2,15)
$u=FileCopy ("\\старый_сервер\путь к папке с SQL-базами\"&$name&".mdf","\\новый_сервер\путь к папке с SQL-базами\"&$name&".mdf")
if $u = 0 Then
  GUICtrlCreateLabel("ОШИБКА! ПЕРЕМЕЩЕНИЕ НЕУСПЕШНО!!!!",2,15)
  $OK_Btn = GUICtrlCreateButton("закрыть", 336,40, 60)
  GUISetState()
  While 1
     $msg = GUIGetMsg()
     Select
         Case $msg = $GUI_EVENT_CLOSE
            exit
         Case $msg = $OK_Btn
            exit
     endselect
  WEnd
  GUIDelete()
EndIf

$u=FileCopy ("\\старый_сервер\путь к папке с _

SQL-базами\"&$name&".ldf","\\новый_сервер\путь к папке с SQL-базами\"&$name&".ldf")
if $u = 0 Then
  GUICtrlCreateLabel("ОШИБКА! ПЕРЕМЕЩЕНИЕ НЕУСПЕШНО!!!!",2,15)
  $OK_Btn = GUICtrlCreateButton("закрыть", 336,40, 60)
  GUISetState()
  While 1
     $msg = GUIGetMsg()
     Select
         Case $msg = $GUI_EVENT_CLOSE
            exit
         Case $msg = $OK_Btn
            exit
     endselect
  WEnd
  GUIDelete()
EndIf

GUICtrlCreateLabel("создаем скулевую базу "&$name,2,28)
GUISetState()
$constrim="DRIVER={SQL Server};SERVER=новый_сервер;DATABASE=master;uid=sa;pwd=пароль;"
$adCN = ObjCreate ("ADODB.Connection")
$adCN.Open ($constrim)
$sQuery = "CREATE DATABASE "&$name&" ON (FILENAME = 'путь к папке с _

базами\"&$name&".mdf'), (FILENAME = 'путь к папке с базами\"&$name&".ldf') FOR ATTACH"
$adCN.Execute($sQuery)
$adCN.Close
GUICtrlCreateLabel("создаем 1c-ную базу "&$name,2,41)
GUISetState()
run ("""C:\Program Files (x86)\1cv8\common\1cestart.exe"" createinfobase_

""Srvr=localhost;Ref="&$name&";DB="&$name&";DBMS=MSSQLServer;DBSrvr=localhost;DBUID=sa;DBPwd=пароль""")
GUICtrlCreateLabel("закончили, проверяйте ",2,54)
$OK_Btn = GUICtrlCreateButton("закрыть", 336,40, 60)
GUISetState()
While 1
 $msg = GUIGetMsg()
    Select
    Case $msg = $GUI_EVENT_CLOSE
             exitloop
     Case $msg = $OK_Btn
            exitloop
    endselect
WEnd
GUIDelete()

image alt text


Список баз для миграции можно брать и из файла, а лог выводить в текстовый файл. Аналогичным образом можно конвертировать несколько десятков баз из файловых в SQL простой выгрузкой-загрузкой в .dt


На этом набор заканчивается


Конечно, это далеко не все, что можно автоматизировать в связке с 1С. Но разного рода обмены, получение real-time информации из 1С в других приложениях и прочие сценарии не попали в этот обзор из-за узкой направленности и специфики.


Наверняка у вас тоже есть свой набор "ноу хау" для администрирования 1С — будет здорово если поделитесь с коллегами в комментариях.


Скрипты и ноу-хау предоставлены avelor, за что ему огромное спасибо!

Tags:
Hubs:
Total votes 14: ↑13 and ↓1+12
Comments39

Articles

Information

Website
servermall.ru
Registered
Founded
Employees
101–200 employees
Location
Россия