Программная блокировка таблиц 1С: Предприятие 7.7


В статье рассматривается пример программной блокировки файла таблицы журнала 1С: Предприятие 7.7. Обращение к Win-API выполняется связыванием через ActivX-компоненту DynamicWrapperX.

В сети уже довольно много написано о блокировках таблиц программы 1С: Предприятие 7.7 и о том, как их избежать путем оптимизации железа и обработок проведения документов. Данная статья, наоборот, описывает, как программно вызвать блокировку одной или нескольких ее таблиц. Зачем это нужно? — возникает закономерный вопрос. Какая польза от того, что в таблицу нельзя будет ничего записать ни методами 1С: Предприятие 7.7, ни прямым запросом, при помощи OLEDB и ODBC — драйвера? Но, это не так. Оказывается, уже лет десять, как есть исправленный vfpoledb-драйвер, в котором вырезаны все функции блокировки файла таблицы таким образом, что можно выполнять прямые запросы, в том числе и на запись данных в таблицу, не запрашивая информацию о наложенных на файл блокировках и без попыток самому их наложить.

Итак, приготовились. Запустили дополнительный сеанс 1С: Предприятие для тестирования блокировки проведения.

Необходимый для работы набор констант:

Скрипт = СоздатьОбъект("MSScriptControl.ScriptControl");
Скрипт.Language = "JScript"; 
Скрипт.AddCode("
        |GENERIC_READ = 0x80000000;
        |GENERIC_WRITE = 0x40000000;
        |GENERIC_READ_WRITE = GENERIC_READ|GENERIC_WRITE;
        |FILE_SHARE_READ = 1;
        |FILE_SHARE_WRITE = 2;
        |FILE_SHARE_READ_WRITE = FILE_SHARE_READ|FILE_SHARE_WRITE;
        |OPEN_EXISTING = 3;
        |OPEN_ALWAYS = 4;
        |FILE_ATTRIBUTE_NORMAL = 128");
 
Модуль = Скрипт.Modules("Global").CodeObject;

Подключение функций Win-API

WinAPI= СоздатьОбъект("DynamicWrapperX.2");

Открытие таблицы журнала 1С: Предприятие 7.7 (1sjourn.dbf). Подключение необходимых функций библиотеки:

WinAPI.Register("KERNEL32.DLL", "CreateFile", "i=sllllll", "r=l");
WinAPI.Register("KERNEL32.DLL", "LockFile", "i=hllll", "r=l");
WinAPI.Register("KERNEL32.DLL", "UnlockFile", "i=hllll", "r=l");

Открытие таблицы в режиме GENERIC_READ_WRITE, FILE_SHARE_READ_WRITE, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL:

Хендл = WinAPI.CreateFile(СтрЗаменить(СтрЗаменить(КаталогИБ() + "1sjourn.dbf", "\", "\\"), "\\\\", "\\"), 
        Модуль.GENERIC_READ_WRITE, Модуль.FILE_SHARE_READ_WRITE, 0, Модуль.OPEN_EXISTING, Модуль.FILE_ATTRIBUTE_NORMAL, 0);

Установка блокировки на запись в журнал

Если Хендл <> -1 Тогда
        Рез = WinAPI.LockFile(Хендл, 1073741823, 0, 1, 0);
        Если Рез <> - 1 Тогда
                Рез = WinAPI.LockFile(Хендл, 1073741824, 0, 1073741823, 0);
        Иначе
                Сообщить("Не удалось заблокировать таблицу");
        КонецЕсли;
Иначе
        Сообщить("Не удалось заблокировать таблицу");
КонецЕсли;

Подключим функцию ожидания

WinAPI.Register("KERNEL32.DLL", "Sleep", "i=l", "f=s");

Подержим блокировку 15 секунд

WinAPI.Sleep(15000);

При попытки проведения в другом сеансе, в строке состояния на весь период блокировки будет высвечиваться «Ожидание захвата таблицы ‘Журналы’ для начала транзакции». Если в параметрах программы в сеансе для проведения настроить параметр «время ожидания захвата таблиц базы данных» меньше отведенных в обработке 15-ти секунд, тогда по истечении времени ожидания система выдаст сообщение: При выполнении транзакции произошла ошибка! Таблица: 1SJOURN Ошибка обращения к данным при транзакции, выполняемой другим пользователем. Повторить попытку выполнить транзакцию? Таким образом, видим, механизм работает. Без снятия блокировки, записать данные в таблицу невозможно. К слову сказать, мы тоже не можем ничего писать в эту таблицу, даже если заменим режим ожидания на попытки программно выполнить запись или проведение документа Разблокируем таблицу:

Если Хендл <> -1 Тогда
        Рез = WinAPI.UnlockFile(Хендл, 1073741823, 0, 1, 0);
        Если Рез <> - 1 Тогда
                Рез = WinAPI.UnlockFile(Хендл, 1073741824, 0, 1073741823, 0);
        Иначе
                Сообщить("Не удалось снять блокировку таблицы");
        КонецЕсли;
Иначе
        Сообщить("Не удалось снять блокировку таблицы");
КонецЕсли;

Установка блокировки на чтение в журнал. Блокировка на запись в таблице 1С не так чревата последствиями как, вполне ожидаемо, блокировка на чтение. Блокировка на чтение в таблицу:

Если Хендл <> -1 Тогда
        Рез = WinAPI.LockFile(Хендл, 1073741772, 0, 1, 0);
        Если Рез <> - 1 Тогда
                Рез = WinAPI.LockFile(Хендл, 1073741773, 0, 50, 0);        
        Иначе
                Сообщить("Не удалось заблокировать таблицу");
        КонецЕсли;
Иначе
        Сообщить("Не удалось заблокировать таблицу");
КонецЕсли;

Подержим блокировку 15 секунд:

WinAPI.Sleep(15000);

При попытке просмотра записей таблицы журнала документов, будет отображаться в строке состояния надпись «Ожидание захвата таблицы ‘Журналы’ для чтения». В случае, если приложение на сможет получить доступ к таблице базы данных достаточно продолжительное время, оно завершит свою работу. Разблокируем таблицу:

Если Хендл <> -1 Тогда
        Рез = WinAPI.UnlockFile(Хендл, 1073741772, 0, 1, 0);
        Если Рез <> - 1 Тогда
                Рез = WinAPI.UnlockFile(Хендл, 1073741773, 0, 50, 0);
        Иначе
                Сообщить("Не удалось снять блокировку таблицы");
        КонецЕсли;
Иначе
        Сообщить("Не удалось снять блокировку таблицы");
КонецЕсли;

Закроем хендл таблицы:

WinAPI.Register("KERNEL32.DLL", "CloseHandle", "i=h", "r=l");
Рез = WinAPI.CloseHandle(Хендл);