Delphi подключение к базе 1с
Насколько мне известно, многие 1С-ники хотели бы изучить написание внешних компонент, чтобы поднять свое магическое искусство 1С на качественно иную ступень.
Что этому может помешать? Во-первых, известный синдром компонентофобии (который исторически берет свое начало от криво написанных внешних компонент). Во-вторых –синдром клинически запутанного кода. OLE-программирование – это не самая простая штука, и, как говорится, «не всякая птица долетит до середины Днепра» (особенно, если эта «птица» – программист 1С).
Я предлагаю вашему вниманию шаблон внешней компоненты, который, как я надеюсь, достаточно прост для понимания (я постарался его значительно упростить по сравнению с типовым примером из «Технологии создания внешних компонент») и стабилен (везде, где это возможно, я использую обработку исключительных ситуаций).
Сборка проекта
Для компиляции примера потребуется среда разработки Delphi 6 или 7.
Файл проекта - TestVK.dpr.
Откройте этот файл (например, двойным щелчком мыши из Проводника).
Нажмите сочетание клавиш Ctrl-F9 (или пункт меню Project-Compile). Если все прошло нормально, в этой же папке образуется готовая внешняя компонента TestVK.dll (для проверки, а все ли хорошо, ее можно удалить, и получить готовую TestVK.dll еще раз).
Проверка работоспособности DLL
В комплект примера входит тестовая конфигурация 1С:Предприятие 7.7.
Переименование DLL
Первое, что я делаю при создании новой внешней компоненты – переименовываю уже существующий образец.
Переименуйте TestVK.dpr так, как вы хотите (например, MyVK.dpr).
Произведите замену всех вхождений подстроки TestVK в файлах проекта на нужное вам имя внешней компоненты.
Подсказка: чтобы открыть другие модули проекта, используйте пункт меню View-Units…
Программный код 1С, разумеется, также нужно не забыть изменить так, чтобы заменить все подстроки «TestVK».
Важно: замените значение CLSID внешней компоненты, чтобы новая DLL, с точки зрения Windows, стала действительно новой.
Чтобы сгенерировать новый CLSID, нажмите сочетание клавиш Ctrl-Shift-G.
Попробуйте скомпилировать новый проект – 1С должна «увидеть» вашу новую внешнюю компоненту, которая создана на основе другой ВК, но содержит полный набор ее свойств и методов.
Что такое свойства и методы?
Новички могут задаться вопросом, а что такое свойства и что такое методы?
В коде 1С свойства выглядят как, своего рода, «переменные», объекта, доступные через точку, например
Здесь объект – имеет имя vk (посмотрите, как он объявляется и инициализируется в глобальном модуле 1С).. Этот объект поддерживает свойства и методы.
В этом коде я установил свойству "Заголовок" текстовое значение (посмотрите, как будет работать пример, если установить этому свойству другое значение заголовка, например, «Здесь был romix», или не устанавливать его вовсе).
Метод объекта – это, своего рода, «функция» объекта, доступная «через точку».
Изменение списка свойств и методов ВК
В модуле AddinObj.pas за количество свойств отвечают участки кода, которые я пометил (*2*), (*5*), (*6*), (*8*), (*11*) а за количество методов - (*3*), (*7*), (*9*), (*10*), (*12*).
Я завел в шаблон по 5 свойств и методов, но что нужно сделать, чтобы их стало 6, например, в приведенном ниже фрагменте кода, - я надеюсь, понятно без объяснений.
Вы можете завести свойства и методы «с запасом» - лишние заготовки не повредят (их можно будет оставить пустыми).
Программирование функциональности свойств
Откройте (через меню View – Units…) модуль vk_object.pas
Для свойств вы увидите примерно такой код, продублированный, с небольшими отличиями, несколько раз: Это самая важная часть наших действий, которую важно постараться понять.
Что я здесь делаю?
Программирование функциональности методов
Для методов код похожий:
Этот абзац можно воспроизвести несколько раз (что и сделано в примере), заменив meth1 на meth2, meth3 и т.д.
Приведенные ниже строчки устанавливают русское и английское имя метода.
Попробуйте изменить то или другое, перекомпилировать проект и посмотреть, что получится.
я устанавливаю количество параметров метода. Попробуйте изменять это количество (например, установить значение 3) и посмотреть, что получится.
Подробнее код я опишу ниже – но сначала полезно потренироваться со вставкой в него отладочной печати (именно так я исследую код).
Возвращаемое значение функции
Чтобы вернуть значение из функции, надо установить переменную g_Value, например:
Отладочная печать
Вы можете в качестве теста вписать в функциональность метода что-то свое, например,
Этот вызов покажет стандартное окно предупреждения с кнопкой ОК. Или вот так:
Во втором случае, если Delphi будет ругаться при компиляции, добавьте Dialogs в раздел uses модуля.
Получение параметров функции
В своей функции я завел две переменные для хранения параметров:
Сейчас мы их заполним значениями, полученными из 1С.
Параметры нумеруются, начиная с 0 (давняя традиция программистов на языке С).
Чем отличаются AsString и AsInteger, надеюсь, понятно.
Этот код можно было бы написать и так:
Отладочная печать параметров
При отладке полученные из 1С значения полезно выводить на экран. Но как это сделать?
Я обычно использую следующий прием:
Чтобы показать строковые переменные, я пишу так:
А числовые значения и значение типа "дата-время" я отображаю примерно так:
Функциональность методов
Получив все значения из 1С, можно приступать к собственно написанию полезного кода.
Узнавать, как работают те или иные функции API, можно в поисковых машинах или в сборниках Delphi FAQ, которые в изобилии выложены в сети Интернет.
Delphi site: daily Delphi-news, documentation, articles, review, interview, computer humor.
Прямой доступ к базе данных 1С
Мне удалось подключиться к 1С, получить информацию о полях и данных справочников. Однако, этот способ оказался слишком не стабильным и медленно-работающим. Система не хотела работать, когда 1С завершилась аварийно, кроме того у заказчика пришлось чистить реестр, чтобы OLE срабатывал правильно. И самое, пожалуй главное, так это то, что команды не всегда хотели срабатывать. Приходилось много времени тратить на доступ к элементарным операциям.
В завершение всего, данные, получаемые посредством OLE не очень полезны - часто показываются только те данные, которые действительны на данное время. Так, например, курс валют будет показываться на дату системы, а не выводиться таблица изменений курсов валют.
Поэтому, изрядно повозившись с OLE-механизмом я всё таки решил реализовать прямой доступ к 1С.
В данной статье я описываю где и как получить информацию из таблиц 1С.
Прямой доступ к таблицам в формате dbf-файлов
Прежде всего, необходимо было выбрать компоненты для доступа к таблицам 1С. К dbf-файлам можно получить доступ с помощью BDE, ODBC или компонент прямого доступа. Первые два способа я сразу же отмёл, остался - третий.
Хранение информации в 1С
Теперь рассмотрим где же взять информацию о таблицах 1С.
1С для каждого справочника создаёт отдельную таблицу. Таких таблиц получается достаточно большое количество и догадаться о назначении каждой из них довольно тяжело.
Прежде всего следует отметить, что 1С создаёт пары файлов: .DBF-файл (данные) и одноимённый .CDX-файл (индексный файл).
Мы можем CDX файлы удалить - потери информации не будет, так как это индексные файлы. При запуске 1С в монопольном режиме автоматически создадутся необходимые индексные файлы.
Информацию о любом файле базы данных и любом поле Вы можете почерпнуть из текстового файла 1Сv7.DD.
В этом файле написано рядом с английским названием таблицы - русское название, рядом с английским названием поля - его назначение, тип данных и размер. Этой информации вполне достаточно, чтобы понять назначение каждой таблицы и поля базы данных. Я не буду дублировать информацию из этого файла в данной статье.
Пароли пользователей хранятся в папке usrdef. Если удалить этот каталог, то можно входить в систему 1С без пароля с наибольшими правами.
Последнее, что необходимо для шлюза - это история изменений данных пользователями. Эту информацию Вы можете найти в текстовом файле каталога SYSLOG.
Заключение
Если Вы собираетесь делать шлюз, то предусматривайте в программе возможность программирования, т.к. многие поля придётся декодировать и адаптировать под данные Вашей системы. Но это уже тема отдельной статьи.
Delphi site: daily Delphi-news, documentation, articles, review, interview, computer humor.
1C + Delphi 5: Простой пример подключения к Базе Данных 1C
(через OLE; в данном случае рассматривается подключение к "1C-предприятие версии 7.7")
Создадим простое приложение, которое считывает список из справочника "ФизическиеЛица".
Пример (в формате MS Word) можно скачать здесь (792 Кб)
Шаг N 1. (скачать, 177 Кб)
Создадим проект с именем Project1
Положим на форму 2-а компонента типа TPanel (у Panel1 свойство Align установим равным AlTop, а у Panel2 - AlClient)
На Panel1 положим кнопку Button1 (TButton) и установим свойство Caption = "Соединить"
Положим на Panel1 еще 4 компонента :
Label1, Edit1, Label2, Edit2
Установим :
- свойство Caption :
Label1.Caption - "Имя 1C-сервера Строка инициализации V77.Application"
Edit2.Text - "/DF:\1CV77\ATCDEMO"
Должно получиться что-то вроде этого:
Некоторые пояснения.
Edit1.Text содержит имя 1C, как OLE - сервера автоматизации.
В нашем случае мы указали : V77.Application
Возможные значения (согласно документации по 1C) :
- V1СEnterprise.Application - версия независимый ключ;
- V77.Application - версия зависимый ключ;
- V77S.Application - версия зависимый ключ, SQL версия;
- V77L.Application - версия зависимый ключ, локальная версия;
- V77M.Application - версия зависимый ключ, сетевая версия.
Edit2.Text содержит строку инициализации 1C
В нашем случае мы указали : /D F:\1CV77\ATCDEMO
Т.е., указали только местонахождение Базы Данных, к которой желаем подключиться
(в этом примере : указан каталог F:\1CV77\ATCDEMO)
Возможные значения ключей (согласно документации по 1C) :
/D - Имя каталога (папки), где располагается База Данных
/N - Имя Пользователя
/P - Пароль доступа
Пример : /DC:\1CV77\ATCDEMO /NАдмин /P123
В исходном тексте программы добавим следующее (выделено жирным красным цветом) :
uses
ComObj,
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
ExtCtrls, StdCtrls;
type
TForm1 = class(TForm)
Panel1: TPanel;
Panel2: TPanel;
Button1: TButton;
Label1: TLabel;
Label2: TLabel;
Edit1: TEdit;
Edit2: TEdit;
procedure Button1Click(Sender: TObject);
private
< Private declarations >
Ole1C : OleVariant;
public
< Public declarations >
end;
Переменная Ole1C (типа OleVariant) будет использована в дальнейшем, как указатель на объект 1C (OLE - сервер автоматизации).
Сделаем двойной щелчок по компоненту Button1 и изменим стандартный обработчик события - нажатие на кнопку - следующим образом :
procedure TForm1.Button1Click(Sender: TObject);
Var
ResOpen : integer;
begin
Ole1C := CreateOleObject(Edit1.Text);
ResOpen:=Ole1C.Initialize(Ole1C.RMTrade, Edit2.Text, '');
end;
Строка :
Ole1C := CreateOleObject(Edit1.Text);
собственно, "запускает" программу 1C, как OLE - сервер автоматизации (имя указано в Edit1.Text)
Строка :
ResOpen:=Ole1C.Initialize(Ole1C.RMTrade, Edit2.Text, '');
инициализирует 1C, согласно нашим "указаниям" (Edit2.Text)
Более подробно можно почитать соотв. документацию по 1C
Откомпилируем и запустим приложение Project1.exe
Нажмите на кнопку " Соединить ".
Если все указано корректно (содержание Edit1.Text и Edit2.Text ),
программа 1C должна запуститься (как OLE сервер автоматизации).
Если в строке инициализации НЕ указаны имя пользователя и(или) пароль доступа, то следует запрос :
Если не корректно указан каталог Базы Данных, то следует запрос :
После загрузки и инициализации программы 1C, ссылка на 1C хранится в переменной Ole1C.
Следует помнить, что программа 1С:Предприятие (как и все создаваемые объекты OLE Automation), выступающая в качестве объекта OLE Automation в другой программе, будет находиться в памяти компьютера до удаления или изменения значения переменной, содержащей ее в качестве объекта.
Именно поэтому, мы вынесли объявление переменной Ole1C "за пределы" обработчика : TForm1.Button1Click
Явных (видимых) признаков того, что мы запустили 1C - нет.
Для того, что убедиться в этом, нужно запустить диспетчер задач (по комбинации Ctrl+Alt+Del).
В списке задач можно увидеть :
Следует отметить, что после того, как Project1.exe будет закрыт - автоматически выгрузится из памяти и 1C.
Шаг N 2. (скачать, 180Кб)
Продолжим наш проект.
На Panel2 положим компонент ListBox1 (TListBox). Свойство Align установим равным AlClient.
Рядом с кнопкой Button1 ("Соединить") положим на Panel1 кнопку Button2 (установим свойство Caption = ФизЛица)
Получится что-то вроде этого :
В исходном тексте программы добавим описание переменной OleFizLica
private
< Private declarations >
Ole1C : OleVariant;
OleFizLica : OleVariant;
public
< Public declarations >
end;
Сделаем двойной щелчок по компоненту Button2 и изменим стандартный обработчик события - нажатие на кнопку - следующим образом :
procedure TForm1.Button2Click(Sender: TObject);
begin
OleFizLica := Unassigned;
if not VarIsEmpty(Ole1C) then begin
OleFizLica:=Ole1C.CreateObject('Справочник.ФизическиеЛица');
end;
end;
Строка :
OleFizLica := Unassigned;
Здесь "обнуляется" значение переменной OleFizLica.
Тем самым, если ранее был создан какой-либо OLE-объект, на который ссылалась эта переменная, он будет уничтожен.
Строка :
if not VarIsEmpty(Ole1C) then begin
Здесь проверяется, чтобы значение переменной Ole1C не было "пустым".
Т.е., если Ole1C имеет "не пустое" значение, то это значит (в контексте нашего проекта), что программа 1C запущена и связь с ней установлена.
Строка :
OleFizLica:=Ole1C.CreateObject('Справочник.ФизическиеЛица');
Здесь создается объект, который ссылается на справочник "ФизическиеЛица".
Если такой справочник существует, то в дальнейшем, переменная OleFizLica будет содержать ссылку на него.
Шаг N 3. (скачать, 184 Кб)
Сделаем двойной щелчок по компоненту Button2 и еще раз изменим обработчик события нажатия на кнопку следующим образом :
Строки N 3,4,5,6 теперь понятны.
Строка N 7 :
if not VarIsEmpty(OleFizLica) then begin
Здесь проверяется, что объект, ссылающийся на справочник "ФизическиеЛица" действительно создан.
Строки N 8..13
Здесь реализован стандартный алгоритм на языке 1C по перебору элементов справочника.
Строка N 11
ListBox1.Items.Add(OleFizLica.Код+' '+OleFizLica.Наименование);
Здесь продемонстрирован один из вариантов обращения к реквизитам справочника по именам.
Строка N 16
OleFizLica := Unassigned;
Здесь "обнуляется" значение переменной OleFizLica.
Тем самым, мы уничтожаем созданный нами объект, чтобы зря не занимать ресурсы.
Следует отметить, что поскольку (в нашем случае) мы используем переменную OleFizLica только в "пределах" обработчика
procedure TForm1.Button2Click(Sender: TObject), то мы могли бы и объявить эту переменную в этом обработчике.
Откомпилируем и запустим приложение Project1.exe
Нажмите на кнопку " Соединить ". После того, как загрузится программа 1C, нажмите на кнопку " ФизЛица ".
Если все указано корректно , то в ListBox1 будет содержание справочника "ФизическиеЛица".
Т.е., будет что-то вроде этого :
Delphi site: daily Delphi-news, documentation, articles, review, interview, computer humor.
Подключение к Базе данных
При работе с базой данных первое, что должна уметь делать программа, это подключаться к базе данных. Через это подключение к БД программа будет отправлять запросы и принимать результаты его выполнения.
- Уже выбран сервер БД и мы знаем его имя.
- Тип выбранного сервера поддерживается компонентами доступа, которые вы используете (в примере используется erDA).
- База данных уже создана соединение с ней уже проверенно стандартными средствами сервера и все работает.
- В Delphi уже создана форма (или DataModule), в которой мы будем создавать это подключение.
Для описания типа сервера и параметров подключения к нему как правило используетсяобъект в своем названии содержащий DataBase или Connection.(В erDA это TerDataBase, в ADO это TADOConnection, в BDE это TDataBase). Обычно рекомендуется на все приложение создается один такой объект, и вся последующая работа идет через него. В случае BDE и ADO можно обойтись и без этого объекта, но тогда сложно организовать красивый диалог для входа в систему, трудно сделать кнопку отключения от БД. и практически невозможно контролировать количество активных подключений (сессий) - которые на данный момент установила программа с сервером.
Этот объект создается как правило либо в главной форме (если приложение небольшое), либо в специально созданном DataModule. Для его создания достаточно выбрать нужный компонент в палитре компонентов и указать мышкой - в какое место его положить. Как правило этот объект снабжен редактором свойств, который вызывается двойным нажатием на иконку компонента. Либо, если редактора нет, то свойства необходимо задать при помощи ObjectInspector.
После создания компонентов желательно присвоить ему читаемое имя. Обзовем его DB.
Как правило при запуске программа должна спросить у пользователя имя и пароль, для подключения к базе данных. Так же при этом желательно дать пользователю выбора базы данных с которой он будет работать. Эта возможность как правило реализуется в событиях onLogin. BeforeConnect, или другие в зависимости от компонентов, где показывается форма для ввода пароля, затем полученные данные передаются в свойства компонента.
В связи с тем, что BDE и ADO автоматически создают подключения к БД когда это необходимо дальнейшая настройка не требуется.
В erDA есть возможность контролировать количество активных сессий, и то, через какое именно подключение будет выполнен запрос. Для этого используется объект TerTransaction там же описаны методы для работы с транзакцией. В связи с тем, что MSSQL не позволяет одновременно держать несколько открытых и не "вытащенных" запросов на одну сессию, в TerTransaction реализован механизм, который при открытии следующего запроса либо останавливает, либо выкачивает весь предыдущий запрос на клиента (вариант поведения можно выбрать при помощи свойства FreeAction компонента TerTransaction).
Обычно просто достаточно положить TerTransaction на форму вместе с TerDataBase и прописать свойство DataBase. Если вы не хотите добиться какого нибудь другого эффекта. Я например разделяю запросы на 2 части 1-я запросы к небольшим справочникам и 2-я запросы которые могут возвращать большое количество записей. Для 1-й части создаю свою Transaction для второй свою со свойством FreeAction=erStopFetch. В результате - если пользователь откроет 2 "длинных" запроса, то вытаскивание первого просто остановится на том месте, где он был на момент открытия 2-го запроса.
Дело в том, что если на сервере оставлять не вытащенные данные - как это делает BDE, то на каждый "не вытащенный" запрос создается свое подключение. В случае если пользователь забыл закрыть этот запрос, то он так и остается висеть и занимать ресурсы сервера, которые все-таки не безграничные. В ADO по умолчанию все результаты запроса сразу вытаскиваются на клиента, но это иногда приводит к долгому ожиданию окончания процесса кэширования.
При этом каждый активный запрос автоматически накладывает ряд блокировок. Хотя при небольшом количестве пользователей это не принципиально, но при увеличении количества пользователей с этим приходится считаться. К сожалению, обычно, когда до этого доходит дело (после написания программы), решать эту проблему очень даже не просто. Гораздо проще, когда задача работы программы с большим количеством клиентов ставилась изначально, и сразу были приняты меры по уменьшению количества блокировок.
В демонстрационной программе используется сервер БД MSSQL имя сервера Test, база данных TestDB.
Для подключения к нему необходимо прописать следующие свойства TerDataBase:
dllName |   | er_mssql.dll |
Params |   | Server Name=Test Database Name=TestDB |
примечание: для данного драйвера, при данных Params (если не указаны имя пользователя и пароль) будет использоваться Windows авторизация.
Теперь остается установить свойство (у компонента Trn) DataBase=DB и протестировать настройки установив свойство Active=True.
Если вы все правильно сделали, то никаких ошибок не произойдет и свойство Active обоих компонентов послушно установится в True и переходим к следующему пункту.
При создании Ole1C := CreateOleObject('V8.Application');
выдает ошибку Недопустимая срока с указанием класса
Может есть вариант средствами ADO? (тоже пытался - не получилось)
В заранее благодарю за помощь!
Вот как проверку подключения пытаюсь сделать:
---------------------------------
Ну да, не установлена. Значит надо установить?
База находится на удаленном сервере, доступ к которому я себе расширил..
Сейчас попытаюсь установить 1С8.2 к себе на компьютер.
Подскажите тогда пожалуйста как и где вводить имя пользователя и пароль для доступа?
Перетащил EXE скомпилированный на сервер с 1С, данная ошибка перестала быть.
Теперь ругается на RMTrade: Method 'RMTrade' not supported automation object
На что поменять надо?
Спасибо, попробую!
if not VarIsEmpty(Connection) then
begin
begin
if BaseType=1 then
Result:='File="'+BasePath+'";';
if BaseType=2 then
Сложновато:(
А как использовать эти функции?
даа.. большая:(
Ну ладно, придется все свои действия по шагам описывать:
создал глобальные функции от H A D G E H O G s:
public
немного подвисает секунд на 5 и отходит.
Поэтому пытаюсь: Connection.CreateObject('Справочники.Сотрудники');
и получаю ошибка при тесте на сервере: Method 'CreateObject' not supported by automation object
str:=Connection.string(Connection.Справочники.Сотрудники);
выдало ошибку Method 'Сотрудники' not supported by automation object
Я код копирую, к сожалению не знаю как выложить скриншоты сюда.
Если необходимо выложу сюда все что у меня на данный момент получилось
Selection:=Connection.Справочники.Сотрудники.Выбрать();
While Selection.Следующий()=True do begin
Выдает ошибку Method 'Сотрудники' not supported by automation object
Справочник этот есть и права у пользователя полные.
Сейчас попробовал загрузить Организации, получилось.
Всем огромное спасибо, в частности H A D G E H O G s, за помощь в настройки.
Если позволите буду писать тут по возникшим вопросам, дабы не плодить темы, новичкам вышеописанное подойдет для практики:)
по моему в инете давно написали кучу статей с примерами для такого скрещивания ужа с ежом, я еще лет 5 назад тягал данные из 1С чисто ради прикола попробовать.
Вроде даже в инете была готовая библиотека-обертка для делфи чтоб удобнее было обращатся к 1С
[27] да было, но к сожалению 7,7 . Да и непонятные ошибки возникали:(
А так мне H A D G E H O G s помог, теперь данные тягаю спокойно, выяснить как бы можно было записать.
устанавливать значение присвоением вроде не прокатит, надо что то типа SetProperty("наименование","77777")
[29] мне на дельфи как то удобнее,превычнее, а на Си давно уже не делаю(
[30] не получается с SetProperty, (ошибка)пишет что не знает эту штуку
(31) ты че хочешь присвоить то? И куда? выполнил запрос и пытаешься в результат что то записать? Ничего не смущает??
(34) смущает:)
Вот допустим я загрузил справочник контр агентов и мне нужно переименовать одного контр агента, как это сделать не пойму?
что сделать? отладчиком не умеешь пользоваться что ли
я не помню как там правильно с 1С из делфи обращаться, найди примеры в инете и все
(39)Думал создать свой справочник по планированию и вносить некоторые данные через свою программку, без входа в 1С.
pfffff
Че там?
Загрузить справочник?, типа списка, как в рассово верной 8.2?
1) Загрузить весь справочник в Tstringgrid и по таймеру обновлять (бред)
2) Написать самому класс - рассово верный аналог динамического списка в 1С - со всеми движухами onscroll-ами, и прочей мутотней. Жуть - ниасилил.
3) Юзать рассово верный ADO от самой дельфи - но только в случае ms sql базы от 1С.
Требовать и эффективности, и гибкости от одной и той же программы — все равно, что искать очаровательную и скромную жену. по-видимому, нам следует остановиться на чем-то одном из двух.
Фредерик Брукс-младший
Читайте также: