Форум: "Базы";
Текущий архив: 2002.12.16;
Скачать: [xml.tar.bz2];
ВнизКомпоненты ADO в сервисе Win32 Найти похожие ветки
← →
iZEN (2002-11-15 15:23) [0]Компоненты ADO в консольном приложении. :[
Возможно ли такое в принципе?
Пробую создать сервисное приложение под Windows2000/XP, где в run-time инстанцируются компоненты TADOConnection и TADOQuery.
Создаются нити на основе TThread, в которые передаются экземпляры этих компонентов и они начинают работу (должны начать - в оконном тестовом приложении всё работает).
...
{$APPTYPE CONSOLE}
uses
ActiveX,...;
...
var
Connection: TADOConnection;
Query: TADOQuery;
Writer: TWriterTask;//Дочерняя нить
...
procedure TConverter.ServiceStart(Sender: TService; var Started: Boolean);
begin
OLEInitialize(nil);
Connection := TADOConnection.Create(nil);
Query := TADOQuery.Create(nil);
Query.Connection := Connection;
Writer := TWriterTask.Create();
Writer.FreeOnTerminate := True;
Writer.SetConnection(Connection);
...
//---Стартуем нить
Writer.Resume();
end;
procedure TConverter.ServiceStop(Sender: TService; var Stopped: Boolean);
begin
//Очистка и завершение работы сервиса
...
OLEUninitialize();
end.
Компиляция происходит нормально.
Инсталляции сервиса происходит нормально (среди сервисов присутствует).
Как только пытаюсь запустить, выдаётся сообщение: "сервис был запущен и остановлен. Некоторые службы автоматически останавливаются, если им нечего делать".
Но как раз делать-то есть чего!!!
Как это побороть?
← →
iZEN (2002-11-15 15:24) [1]>>>iZEN © (15.11.02 15:23)
>>>Компоненты ADO в консольном приложении. :[
>>>Возможно ли такое в принципе?
оопс...
Следует читать так:
"Компоненты ADO в сервисном приложении. :[
Возможно ли такое в принципе?"
← →
BlackTiger (2002-11-15 15:57) [2]Сам с этим бился, то ли я чего-то не понял, то ли - ОБЛОМ.
Кое что работало, когда я в сервис пихал форму, и уже на форму кидал адошные контролы, не создавая их налету.
Но все равно нифига не работало - благо был под рукой интерфейс прямого доступа к базе.
У меня была проблема, что именно в сервисе ADO-компоненты просто отказывались инициализироваться (писал свой лог-файл перед вызовом каждой строки кода - только так и нашел), чего то им не хватало.
← →
asmith (2002-11-16 15:22) [3]Да, там какая-то проблема. Мы вышли из тупика, отказавшись от ADO-компонентов и перешли на интерфейсы ADO. Импортировали библиотеку типов, почистили ее от ненужных вещей (без этого размер исполняемого модуля был слишком велик, все писалось на API для достижения минимального размера)и далее юзали прямо интерфейса _Connection, _Command и другие.
← →
sniknik (2002-11-17 16:53) [4]у тебя OLEInitialize(nil); делается а CoInitialize(nil); нет. Может в этом причина?
Вообще есть проги сервисы на Delphi писаные и со стандартными ADO компонентами, как минимум 1 такую знаю (правда исходников нет :( ), т.е. в принципе возможно.
← →
asmith (2002-11-17 20:40) [5]Нашел старые сорсы - делали такое (в сервисе ADO-компоненты).
1. Сервис на основе класса TService
2. В обработчике события ServiceExecute создавали поток, порождннный от класса TThread
3. В методе Execute потокового класса пишем, как обычно:
CoInitialize(nil);
...
try
FServer := ReadStringSetting(strServerSetting);
FDB := ReadStringSetting(strDBSetting);
FConn := TADOConnection.Create(nil);
FConn.LoginPrompt := False;
FConn.ConnectionString := Format(ыяConnString, [FDB, FServer]);
FConn.Open();
except
on E: Exception do
Log.Error(Format(errDBConnect, [FServer, E.Message]), False);
end;
...
// тело потока
...
CoUninitialize;
Это у нас работает.
← →
Fantasist (2002-11-18 00:25) [6]Интересная проблема. В принципе, на использование COM серверов ограничений не накладывалось, разве только что правами доступа. Но и компоненты, на первый взгляд, делают всего лишь простую оболочку над ADO серверами, какой в них интересно можно глюк запихнуть? Эх, жаль NT нет, не могу посмотреть!
Попробуй создавать компоненты в том же потоке, что и работает с ними. Все-таки CoCreateInstance(точнее CoGetClassObject) делает больше, чем просто подгружает dll в память и вызывает DllGetClassObject. И в каждом потоке надо не забывать вызывать CoInitialize. И еще может эта утилитка поможет разобраться:
http://www.develop.com/hp/jasonw/comspy/default.htm
← →
iZEN (2002-11-19 10:56) [7]Бесполезно, ребята.
По-всякому пробовал:
тасовал OLEInitialize(nil); CoInitialize(nil); и парные процедуры, вызывал одно без другого, в секции инициализации модуля/нити и т.д. - бесполезно.
Сервис не хочет стартовать. Что ему не хватает? Мозгов чьих-то, очевидно. :(
← →
sniknik (2002-11-19 11:14) [8]вышли мне то что написал, посмотреть, хотя сервисы и не писал (потому и интересно) но может что нибудь в голову придет.
одна голова хорошо,... а с мозгами лутше. :о))
← →
iZEN (2002-11-19 11:16) [9]Для sniknik © (19.11.02 11:14).
Извините, не могу.
Это специфический сервис конвертации для промышленного приложения.
Думаю, суть обрисовал вполне полно (см. выше).
Возникнут вопросы - задавайте.
← →
sniknik (2002-11-19 11:21) [10]очень специфический, не работает - основа секретности! :-)
ну нет так нет, если напишу когданибудь рабочий сервис сообщу.
← →
iZEN (2002-11-19 11:48) [11]Для sniknik © (19.11.02 11:21).
Суть проблемы такова.
1. Имеется архив с данными (не важно какой), на подобие базы данных.
2. Этот архив изменяется в реальном времени через каждые 30 минут.
3. Доступ к архиву осуществляется через собственный API на Object Pascal-е.
4. Я пишу сервис Win32 реального времени конвертации данных из этого архива в современную РСУБД (MS SQL Server 2000, IBPhoenix Firebird и т.д.)
5. Используются компоненты доступа ADO (стандартный доступ к РСУБД). Используются только обычные операторы SQL без специфических мелочей: delete from <table> where (...), insert into <table> values (...).
6. Данные из архива читаются, но не могут быть занесены в РСУБД по причине неинициализации ADO в сервисе - в обычных консольных и оконных приложениях это проходит нормально.
7. Задействуется механизм многопоточности Delphi, стэк, и критическая секция: одна нить читает данные, заносит их в стэк, другая нить читает данные из стэка и конвертирует в РСУБД с помощью SQL-запросов, стэк охраняется критической секцией.
8. Нигде не используются прямые вызовы WinAPI. Всё построено на стандартных классах VCL: TThread, TCriticalSection, TStack и т.д.
← →
Polevi (2002-11-19 11:55) [12]1 поток - ServiceStart
инициировали COM, создали экземпляр Connection и передаем его в другой поток, как я понимаю ????
Writer.SetConnection(Connection);
надо передавать строку инициализации соеджинения в Writer, в его потоке делать CoIni.. и после этого создавать Connection
← →
DOOMin (2002-11-19 12:06) [13]Я только не понял зачем все это делать в потоках, если данные изменяются раз в 30 минут? Почему нельзя сделать обячную обработку по таймеру? У меня уже больше года работает 2 сервиса, использующие ADO. В одном используется Datamodule, а в другом компоненты живут прямо в контейнере TService. Единственная специфика это вызов Coinitialize(nil) и то что я использую windows security for MSSQL и поэтому сервис должен быть запущен не от localsystem, а от какой-то учетной записи с правами доступа. Больше проблем не замечено. Скорее всего проблемы именно в работе ADO в потоке. Насколько я понял из задачи ее можно решить без потоков.
← →
iZEN (2002-11-19 12:44) [14]Для DOOMin (19.11.02 12:06).
Насколько я понял из задачи ее можно решить без потоков.
Увы, нельзя. Это - промышленное приложение. Данные должны быть гарантировано обработаны с допустимым уровнем устойчивости. Например, нет доступа к архиву на время его обновления сторонним приложением, что делать? Кидать? Нет! Будем ждать и получать доступ: на это 5 попыток. То, что предлагаете Вы, - просто "на любителя". Это я уже прошёл и поимел шишек.
Кстати, я не использую TTimer. Причина: не нужно - нити могут приостанавливаться (Suspend) по внутренней логике, есть привязка к машинному времени через функции Now() и Sleep(...).
← →
iZEN (2002-11-19 12:47) [15]Для Polevi © (19.11.02 11:55).
Дело в том, что OLEInitialize(nil); CoInitialize(nil) и т.д. я делаю в секцииinitialize
модуля (пробовал разные варианты) - это должно срабатывать перед инициализацией любого приложения, но почему-то для ADO нет.
Хотя попробую Ваш способ. О результатах сообщу.
← →
iZEN (2002-11-19 13:06) [16]Для Polevi © (19.11.02 11:55).
Увы. Ах и ох. Ничего такого не вышло. Перепробовал различные способы инициализации - ноль.
← →
iZEN (2002-11-19 13:12) [17]Видимо, придётся поднимать как сервис любимую Java Virtual Machine и работать через JDBC и JNI.
← →
Fantasist (2002-11-19 22:02) [18]Не, iZEN, так нельзя, нам ведь жутко интересно, что же за глюк там сидит. :) А сами мы посмотреть не можем - он у вас.
То есть точно известно, что проблема в ADO в потоках? Утилитку пользовал, на которую я ссылку кинул? По ней можно отследить создаются ли сервера и в какой последовательности.
← →
sniknik (2002-11-19 22:45) [19]Fantasist © (19.11.02 22:02)
> проблема в ADO в потоках?
не, уже давно в потоках пользую, проблем не замечал.
← →
Fantasist (2002-11-20 08:33) [20]
> не, уже давно в потоках пользую, проблем не замечал.
Ну так я же пытаюсь выяснить хотя бы необходимый набор для этой проблемы. Может тут, конечно, потоки вообще непричем. Или ADO. Вот и хочется очертить минимальную границу при которой возникают неполадки.
← →
Fantasist (2002-11-21 03:09) [21]Что-то темы вниз быстро убегают.
← →
iZEN (2002-11-21 08:25) [22]Для Fantasist © (19.11.02 22:02).
Насчёт утилитки. Не пробовал. Времени нет. Проект уже сдавать надо.
Чужие глюки (VCL&etc.) ловить как-то не хочется. :(
Ещё раз повторяю: со своей стороны я использую только стандартные компоненты и стандартные механизмы функционирования. В приложениях всё работает, в сервисе - нет. Доступ к MS SQL Server 2000 через сквозную Windows-аутентификацию и UDL-файл. Знаю, что в этом может быть и дело - сервис всё-таки не от имени пользователя работает, но такова политика.
Выкрою время, обязательно разберусь в чём дело. За ссылку - спасибо.
← →
Fantasist (2002-11-21 11:48) [23]iZEN, хорошо, спасибо и на этом. Доберусь до NT/2000 обязательно попробую.
← →
iZEN (2002-11-25 08:26) [24]Это снова я.
Сервис так и не работает!!!
Пишет то же самое: "Сервис был запущен и остановлен по причине "нечего делать". :))
Дело не в безопасности: с локальным сервером Firebird тоже не работает. Авторизация идёт не сквозная, как в случае с MSSQL2000, а через пару значений "пользователь/пароль" - всё в UDL-файле, сервис имеет доступ к этому файлу.
Проблему придётся кидать? :(
← →
Zelius (2002-11-25 11:05) [25]Сейчас пишу программу, которая может работать как сервис, она использует стандартные компоненты для работы с АДО, все без проблем! И это с учетом того, что прога может работать и под Вин98 и под НТ, и как сервис и как консоль... Могу посоветовать вставить лог для всех шагов, что бы каждый шаг программы записывался в файл, вызовы, параметры и прочее... Может у тебя ошибка вылезает, а ты ее не знаешь! Хорошо бы все в эксепшены обернуть и логи создавать. А может проге не хватает очереди выборки сообщений, что бы маршалинг работал по человечески, правда в этом я не уверен...
← →
BlackTiger (2002-11-25 13:59) [26]Zelius! Если не трудно, приведи пример инициализации сервиса (все проблемы видимо где-то на этом этапе) для работы с АДО-компонентами. Никто тырить ничего не будет, а вот жизнь облегчишь многим. Мне интересно ЧТО нужно сделать еще для корректной инициализации. Чую, что что-то там не совсем стандартное и специфичное, но не могу понять - что.
У меня тоже не работало ничего в сервисе, а просто вываливалось с "фиг-его-знает-каким исключением", сколько я не бился.
← →
Zelius (2002-11-25 17:06) [27]У меня для того, что бы все работало в различных режимах (консоль, сервис, Вин9х приложение), есть отдельный поток, который выполняет основную задачу сервиса. Перед началом работы потока идет вызов OleCheck(CoInitialize(nil)), в конце CoUninitialize() - тут все стандартно. Есть одно важное отличие моего потока, работающего с АДО от потока консольного приложения или сервиса - у моего есть очередь сообщений!!! Я думаю, что в этом все дело! Вот выдержка из статьи MSDN "The Rules of the Component Object Model": Each apartment/thread with objects in it must have a message queue in order to handle calls from other processes and apartments within the same process. This means simply that the thread"s work function must have a GetMessage/DispatchMessage loop. If other synchronization primitives are being used to communicate between threads, the Microsoft Win32® function MsgWaitForMultipleObjects can be used to wait for both messages and thread synchronization events. Перевожу самые главные слова: поток должен иметь цикл выборки сообщений из очереди - GetMessage/DispatchMessage.
← →
Hiqwer (2002-11-25 19:04) [28]Писать надо без компонент. Забыть про TADOConnection.
Делей так...
uses
ComObj;
...
var
Conn:Variant;
begin
Conn:=CreateOleObject("ADODB.Connection");
Conn.ConnectionString:="... твое описание подключения ...";
Conn.Open;
...
Conn.ExecSql(...); // для примера
...
Conn.Close;
Conn:=Null;
Работать будет везде и всегда. Код при этом не сколько не усложняеться.
Полоное описание объектов (Connection,RecordSet,Command и др.) можно посмотреть например в инспекторе обектов VBA запустив любое приложение MSOffice.
Где-то была статья на эту тему.
Нужны подробности, обращайся.
← →
BlackTiger (2002-11-26 14:10) [29]Интересно, а совместимы ли форматы работы с данными (в смысле, можно ли это соединение использовать для работы с дельфийскими таблицами/запросами)? Или ВСЕ надо делать НОРМАЛЬНЫМ путем, по всем правилам АДО без дельфийских наворотов?
Например, у меня интерактивный сервис: пользователь видит иконку в трэйе и может вызвать форму, в которой есть грид с данными. Получится? (в данный момент нет никакой возможности проверить самому)
← →
Hiqwer (2002-11-26 16:42) [30]Все должно получиться, но придется использовать предков (или свойства) привычных компонентов.
Например тот же TADOConnection имеет свойство ConnectionObject оно как раз эквивалентно описанному выше созданному Conn.
Вообще, для визуализации данных полученых таким образом удобнее использовать не DB компоненты, а обычные (например TStringGrid).
Напомню, что в теме речь о консольном приложении.
Однако, такой способ подключения в приложениях с графическим интерфейсом хотя и усложняет код, но снимает все проблемы возникающие при использовании DLL, BPL, COM - обектов, обектов автоматизации и др. если из них осуществляеться доступ к базам.
Для простых приложений (одна форма и Grid) может и не стоит...
← →
iZEN (2002-11-27 10:00) [31]Немного не в тему.
Очень странное поведение объекта TStringList: в оконном приложении код (набираю по-памяти) работает:
var
s: String;
list: TStringList;
begin
try
list := TStringList.Create();
...
list.Add(s);//s - строка символов
finally
if (Assigned(list)) then begin list.Free(); list := nil; end;
end;
end;
В консольном приложении после:
list := TStringList.Create();
В отладчике:
list == nil
Это что? У меня глюки? Надо иничиализировать TApplication?
Что-то я не понял, почему TStringList может быть завязан на оконную библиотеку?
Дурдом!!!!!!!!!!!!!
← →
iZEN (2002-11-27 10:02) [32]В общем, пора заняться чем-то стабильным.
Бросать надо Delphi, бросать!
← →
Zelius (2002-11-27 11:33) [33]Практика показывает, что большинство глюков, встречающихся в работе программы порождено пргораммистом! Или являются следствием его неграмотности, как это бывает у меня... Например, если дебагер ведет себя непонятно, то стоит отключить оптимизатор и попробовать еще раз. а твой кусок выглядит немного не рационально, правильнее так:
var
s: String;
list: TStringList;
begin
list := TStringList.Create();
try
...
list.Add(s);//s - строка символов
finally
FreeAndNil(list);
end;
end;
Хотя зачем в данном случае присваивать nil - совершенно непонятно! :)
← →
iZEN (2002-11-27 11:52) [34]/** Zelius © (27.11.02 11:33)
<...>Хотя зачем в данном случае присваивать nil - совершенно непонятно! :)
*/
Это просто привычка (хорошая, надо сказать).
Это вот откуда.
Если бы объект list был бы виден глобально (как поле класса, например), то проверка на nil здесь будет иметь решающее значение: после вызова Free() удаляется объект, но не всегда ссылка на него (ссылок может быть несколько, когда объект один). На такие грабли я уже наступал, пока не дошло про возможную множественность ссылок. Из java, кстати.
В данном случае, в контексте локальной процедуры, наши с вами действия эквивалентны и не имеют основания для спора.
← →
Zelius (2002-11-27 12:02) [35]Согласен!
Но тут принципиально другое - необходимо поставить list := TStringList.Create() перед try! Иначе, если, например, не хватит памяти и создание списка вызовет эксепшн, то переменная list будет содержать произвольное значение, причем не nil!!! И попытка его освободить вызовет нарушение доступа!
← →
iZEN (2002-11-27 12:03) [36]/**Zelius © (27.11.02 11:33)
Практика показывает, что большинство глюков, встречающихся в работе программы порождено пргораммистом! Или являются следствием его неграмотности, как это бывает у меня... Например, если дебагер ведет себя непонятно, то стоит отключить оптимизатор и попробовать еще раз.<...>
*/
Вот что я не понимаю, так это заявление о неграмотности.
Судите сами.
У нас есть простейший кусок кода. Этот код превосходно работает в оконном приложении, но наотрез отказывается работать в консольном приложении. После замены стандартного объекта (list: TStringList) на динамический массив (list: array of String;) и переписывани небольшого участка консольное приложение заработало.
Спрашивается, если консольное приложение не может жить с объектом класса TSringList, то видимо виноват программист? То есть я?
Откровенно говоря, я не ожидал такой засады от VCL. А опции оптимизации вообще ни при чём, иначе это - ошибка компилятора, что гораздо серьёзнее всего остального.
← →
Zelius (2002-11-27 12:24) [37]Когда я говорил о неграмотности, я говорил в большей степени о себе ( особенно после того, как обнаружил причины падучести моей программы) и о том, что нельзя сразу валить на дельфи, а стоит присмотреться к коду! Потому что приведенный код обсалютно не зависит от консольности! Пожалуйста, вырежи и пришли сюда полный кусок кода, может что-то прояснится... Еще помогает нажатие Ctrl+левая кнопки мыши на переменной list, может она берется не оттуда, откуда ты думаешь...
← →
iZEN (2002-11-27 13:04) [38]Было:
constructor TTSArchiv.Create(const NamesFileName, ValuesFileName: TFileName);
procedure FillNames();
//Запонение массива Именами ТС
var fis: TFileStream;
mis: TMemoryStream;
bufnames: array of Byte;
l, h, i, j, k: Integer;
s: ShortString;
a: AnsiChar;
c: Byte;
AllNames: TStringList;
begin
try
fis := TFileStream.Create(NamesFileName, fmOpenRead or fmShareDenyNone);
mis := TMemoryStream.Create();
mis.LoadFromStream(fis);
SetLength(bufnames, mis.Size);
AllNames := TStringList.Create();
mis.Position := 0;
mis.ReadBuffer(Pointer(bufnames)^, mis.Size);
s := ""; j := 0; k := 0;
l := Low(bufnames); h := High(bufnames);
for i := l to h
do begin
c := bufnames[i];
Inc(k);
if (c >= 32)
then begin
a := ANSI(c);
s := s + a;
end;
if (k = 20)
and (s <> "")
then begin
AllNames.Add(s);
Inc(j);
s := "";
k := 0;
end;
end;
finally
if (Assigned(AllNames)) then begin
SetLength(Names, j+1);
for i := Low(Names) to High(Names) do Names[i] := AllNames.Strings[i];
AllNames.Free();
AllNames := nil;
end;
while (Assigned(fis)) do begin fis.Free(); fis := nil; end;
while (Assigned(mis)) do begin mis.Free(); mis := nil; end;
Finalize(bufnames);
bufnames := nil;
end;
end;
begin
inherited Create();
FillNames();
end;
← →
iZEN (2002-11-27 13:05) [39]Стало:
constructor TTSArchiv.Create(const NamesFileName, ValuesFileName: TFileName);
procedure FillNames();
const
lenName = 20;
//Запонение массива Именами ТС
var fis: TFileStream;
mis: TMemoryStream;
bufnames: array of Byte;
l, h, i, j, k: Integer;
s: ShortString;
a: AnsiChar;
c: Byte;
AllNames: array of String;
begin
try
fis := TFileStream.Create(NamesFileName, fmOpenRead or fmShareDenyNone);
mis := TMemoryStream.Create();
mis.LoadFromStream(fis);
SetLength(bufnames, mis.Size);
SetLength(AllNames, mis.Size div lenName + lenName);
mis.Position := 0;
mis.ReadBuffer(Pointer(bufnames)^, mis.Size);
s := ""; j := 0; k := 0;
l := Low(bufnames); h := High(bufnames);
for i := l to h do begin
c := bufnames[i];
Inc(k);
if (c >= 32) then begin
a := ANSI(c);
s := s + a;
end;
if (k = lenName) and (s <> "") then begin
AllNames[j] := s;
Inc(j);
s := "";
k := 0;
end;
end;
finally
if (Assigned(AllNames)) then begin
SetLength(Names, j+1);
for i := Low(Names) to High(Names) do
Names[i] := AllNames[i];
end;
while (Assigned(fis)) do begin fis.Free(); fis := nil; end;
while (Assigned(mis)) do begin mis.Free(); mis := nil; end;
Finalize(AllNames);
AllNames := nil;
Finalize(bufnames);
bufnames := nil;
end;
end;
begin
inherited Create();
FillNames();
end;
← →
iZEN (2002-11-27 13:07) [40]Было: AllNames: TStringList;
Стало: AllNames: array of String;
Страницы: 1 2 вся ветка
Форум: "Базы";
Текущий архив: 2002.12.16;
Скачать: [xml.tar.bz2];
Память: 0.57 MB
Время: 0.008 c