![]() |
| ||
|
|
Новости |
Поиск |
FAQ |
Статьи |
Книги |
Новости VCL
| | Форумы ЧАТ |
Орешник |
|
| |||
| |||
« предыдущая ветвь | форум | следующая ветвь »
Компоненты ADO в сервисе Win32 [D6, IB6.x, MSSQL]
iZEN © (2002-11-15 15:23:00)Компоненты 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-27 13:05:00) [21]Стало:
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:04:00) [22]Было:
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;
Zelius (2002-11-27 12:24:00) [23]Когда я говорил о неграмотности, я говорил в большей степени о себе ( особенно после того, как обнаружил причины падучести моей программы) и о том, что нельзя сразу валить на дельфи, а стоит присмотреться к коду! Потому что приведенный код обсалютно не зависит от консольности! Пожалуйста, вырежи и пришли сюда полный кусок кода, может что-то прояснится... Еще помогает нажатие Ctrl+левая кнопки мыши на переменной list, может она берется не оттуда, откуда ты думаешь...
iZEN (2002-11-27 12:03:00) [24]/**Zelius © (27.11.02 11:33)
Практика показывает, что большинство глюков, встречающихся в работе программы порождено пргораммистом! Или являются следствием его неграмотности, как это бывает у меня... Например, если дебагер ведет себя непонятно, то стоит отключить оптимизатор и попробовать еще раз.<...>
*/
Вот что я не понимаю, так это заявление о неграмотности.
Судите сами.
У нас есть простейший кусок кода. Этот код превосходно работает в оконном приложении, но наотрез отказывается работать в консольном приложении. После замены стандартного объекта (list: TStringList) на динамический массив (list: array of String;) и переписывани небольшого участка консольное приложение заработало.
Спрашивается, если консольное приложение не может жить с объектом класса TSringList, то видимо виноват программист? То есть я?
Откровенно говоря, я не ожидал такой засады от VCL. А опции оптимизации вообще ни при чём, иначе это - ошибка компилятора, что гораздо серьёзнее всего остального.
Zelius (2002-11-27 12:02:00) [25]Согласен!
Но тут принципиально другое - необходимо поставить list := TStringList.Create() перед try! Иначе, если, например, не хватит памяти и создание списка вызовет эксепшн, то переменная list будет содержать произвольное значение, причем не nil!!! И попытка его освободить вызовет нарушение доступа!
iZEN (2002-11-27 11:52:00) [26]/** Zelius © (27.11.02 11:33)
<...>Хотя зачем в данном случае присваивать nil - совершенно непонятно! :)
*/
Это просто привычка (хорошая, надо сказать).
Это вот откуда.
Если бы объект list был бы виден глобально (как поле класса, например), то проверка на nil здесь будет иметь решающее значение: после вызова Free() удаляется объект, но не всегда ссылка на него (ссылок может быть несколько, когда объект один). На такие грабли я уже наступал, пока не дошло про возможную множественность ссылок. Из java, кстати.
В данном случае, в контексте локальной процедуры, наши с вами действия эквивалентны и не имеют основания для спора.
Zelius (2002-11-27 11:33:00) [27]Практика показывает, что большинство глюков, встречающихся в работе программы порождено пргораммистом! Или являются следствием его неграмотности, как это бывает у меня... Например, если дебагер ведет себя непонятно, то стоит отключить оптимизатор и попробовать еще раз. а твой кусок выглядит немного не рационально, правильнее так:
var
s: String;
list: TStringList;
begin
list := TStringList.Create();
try
...
list.Add(s);//s - строка символов
finally
FreeAndNil(list);
end;
end;
Хотя зачем в данном случае присваивать nil - совершенно непонятно! :)
iZEN (2002-11-27 10:02:00) [28]В общем, пора заняться чем-то стабильным.
Бросать надо Delphi, бросать!
iZEN (2002-11-27 10:00:00) [29]Немного не в тему.
Очень странное поведение объекта 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 может быть завязан на оконную библиотеку?
Дурдом!!!!!!!!!!!!!
Hiqwer (2002-11-26 16:42:00) [30]Все должно получиться, но придется использовать предков (или свойства) привычных компонентов.
Например тот же TADOConnection имеет свойство ConnectionObject оно как раз эквивалентно описанному выше созданному Conn.
Вообще, для визуализации данных полученых таким образом удобнее использовать не DB компоненты, а обычные (например TStringGrid).
Напомню, что в теме речь о консольном приложении.
Однако, такой способ подключения в приложениях с графическим интерфейсом хотя и усложняет код, но снимает все проблемы возникающие при использовании DLL, BPL, COM - обектов, обектов автоматизации и др. если из них осуществляеться доступ к базам.
Для простых приложений (одна форма и Grid) может и не стоит...
BlackTiger (2002-11-26 14:10:00) [31]Интересно, а совместимы ли форматы работы с данными (в смысле, можно ли это соединение использовать для работы с дельфийскими таблицами/запросами)? Или ВСЕ надо делать НОРМАЛЬНЫМ путем, по всем правилам АДО без дельфийских наворотов?
Например, у меня интерактивный сервис: пользователь видит иконку в трэйе и может вызвать форму, в которой есть грид с данными. Получится? (в данный момент нет никакой возможности проверить самому)
Hiqwer (2002-11-25 19:04:00) [32]Писать надо без компонент. Забыть про 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.
Где-то была статья на эту тему.
Нужны подробности, обращайся.
Zelius (2002-11-25 17:06:00) [33]У меня для того, что бы все работало в различных режимах (консоль, сервис, Вин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.
BlackTiger (2002-11-25 13:59:00) [34]Zelius! Если не трудно, приведи пример инициализации сервиса (все проблемы видимо где-то на этом этапе) для работы с АДО-компонентами. Никто тырить ничего не будет, а вот жизнь облегчишь многим. Мне интересно ЧТО нужно сделать еще для корректной инициализации. Чую, что что-то там не совсем стандартное и специфичное, но не могу понять - что.
У меня тоже не работало ничего в сервисе, а просто вываливалось с "фиг-его-знает-каким исключением", сколько я не бился.
Zelius (2002-11-25 11:05:00) [35]Сейчас пишу программу, которая может работать как сервис, она использует стандартные компоненты для работы с АДО, все без проблем! И это с учетом того, что прога может работать и под Вин98 и под НТ, и как сервис и как консоль... Могу посоветовать вставить лог для всех шагов, что бы каждый шаг программы записывался в файл, вызовы, параметры и прочее... Может у тебя ошибка вылезает, а ты ее не знаешь! Хорошо бы все в эксепшены обернуть и логи создавать. А может проге не хватает очереди выборки сообщений, что бы маршалинг работал по человечески, правда в этом я не уверен...
iZEN (2002-11-25 08:26:00) [36]Это снова я.
Сервис так и не работает!!!
Пишет то же самое: "Сервис был запущен и остановлен по причине "нечего делать". :))
Дело не в безопасности: с локальным сервером Firebird тоже не работает. Авторизация идёт не сквозная, как в случае с MSSQL2000, а через пару значений "пользователь/пароль" - всё в UDL-файле, сервис имеет доступ к этому файлу.
Проблему придётся кидать? :(
Fantasist (2002-11-21 11:48:00) [37]iZEN, хорошо, спасибо и на этом. Доберусь до NT/2000 обязательно попробую.
iZEN (2002-11-21 08:25:00) [38]Для Fantasist © (19.11.02 22:02).
Насчёт утилитки. Не пробовал. Времени нет. Проект уже сдавать надо.
Чужие глюки (VCL&etc.) ловить как-то не хочется. :(
Ещё раз повторяю: со своей стороны я использую только стандартные компоненты и стандартные механизмы функционирования. В приложениях всё работает, в сервисе - нет. Доступ к MS SQL Server 2000 через сквозную Windows-аутентификацию и UDL-файл. Знаю, что в этом может быть и дело - сервис всё-таки не от имени пользователя работает, но такова политика.
Выкрою время, обязательно разберусь в чём дело. За ссылку - спасибо.
Fantasist (2002-11-21 03:09:00) [39]Что-то темы вниз быстро убегают.
Fantasist (2002-11-20 08:33:00) [40]
> не, уже давно в потоках пользую, проблем не замечал.
Ну так я же пытаюсь выяснить хотя бы необходимый набор для этой проблемы. Может тут, конечно, потоки вообще непричем. Или ADO. Вот и хочется очертить минимальную границу при которой возникают неполадки.